2017-05-14 110 views
0

所以我有這樣的數學語言,它是這樣的:定義的數學語言在序言

E -> number 
[+,E,E,E] //e.g. [+,1,2,3] is 1+2+3 %we can put 2 to infinite Es here. 
[-,E,E,E] //e.g. [-,1,2,3] is 1-2-3 %we can put 2 to infinite Es here. 
[*,E,E,E] //e.g. [*,1,2,3] is 1*2*3 %we can put 2 to infinite Es here. 
[^,E,E] //e.g. [^,2,3] is 2^3 
[sin,E] //e.g. [sin,0] is sin 0 
[cos,E] //e.g. [cos,0] is cos 0 

,我想寫找到一個數學表達式由該語言編寫的數值設定的規則在序言中。

我第一次寫了一個名爲「檢查」功能,它會檢查根據語言如果該列表是用正確的方式有:

check1([]). 
check1([L|Ls]):- number(L),check1(Ls). 
check([L|Ls]):-atom(L),check1(Ls). 

現在我需要寫函數「評估「這個表達式是一個用這種語言編寫的表達式,而一個變量是與這種語言相對應的數字值。 例如:

?-evaluate([*,1,[^,2,2],[*,2,[+,[sin,0],5]]]],N) -> N = 40 

所以我寫了這個:

sum([],0). 
sum([L|Ls],N):- not(is_list(L)),sum(Ls,No),N is No + L. 
min([],0). 
min([L|Ls],N):-not(is_list(L)), min(Ls,No),N is No - L. 
pro([],0). 
pro([X],[X]). 
pro([L|Ls],N):-not(is_list(L)), pro(Ls,No), N is No * L. 
pow([L|Ls],N):-not(is_list(L)), N is L^Ls. 
sin_(L,N):-not(is_list(L)), N is sin(L). 
cos_(L,N):-not(is_list(L)), N is cos(L). 

d([],0). 
d([L|Ls],N):- L == '+' ,sum(Ls,N); 
L == '-',min(Ls,N); 
L == '*',pro(Ls,N); 
L == '^',pow(Ls,N); 
L == 'sin',sin_(Ls,N); 
L == 'cos',cos_(Ls,N). 

evaluate([],0). 
evaluate([L|Ls],N):- 
is_list(L) , check(L) , d(L,N),L is N,evaluate(Ls,N); 
is_list(L), not(check(L)) , evaluate(Ls,N); 
not(is_list(L)),not(is_list(Ls)),check([L|Ls]),d([L|Ls],N), 
L is N,evaluate(Ls,N); 
is_list(Ls),evaluate(Ls,N). 

,它的工作只是一個列表,並返回正確的答案,但不包括主列表內的多個列表,我的代碼應該怎麼?

+0

如果'檢查(L)'名單上出現故障,應該不是整個謂語簡單地失敗,而比試圖評估一個無效列表?而'not(is_list(Ls))'在語義上可能不是合理的,因爲在這種情況下列表的尾部(在這種情況下爲'Ls')預計總是列表。 – lurker

+0

我不會用這種方式來組織程序,但是考慮到你的方法,你需要做的就是讓每個運算符謂詞遞歸調用到'd/2',並且在遇到任何時候調用'd/2'子目錄(你目前只允許'not(is_list(L))')。 – lurker

回答

3

你看起來像一個描述E(大概簡稱表達)產生式規則的工作規範可能是一個數字或6個指定的操作之一。這是空的列表[]不是一個表達式。所以事實

evaluate([],0). 

不應該在你的代碼。您的謂詞總和/ 2幾乎與您編寫它的方式一樣,除了空列表和包含單個元素的列表,根據您的規範,這些列表不是有效的輸入。但是謂詞min/2和pro/2是不正確的。請看下面的例子:

?- sum([1,2,3],X). 
X = 6      % <- correct 
    ?- sum([1],X). 
X = 1      % <- incorrect 
    ?- sum([],X). 
X = 0      % <- incorrect 
    ?- min([1,2,3],X). 
X = -6     % <- incorrect 
    ?- pro([1,2,3],X). 
X = 6 ? ;     % <- correct 
X = 0      % <- incorrect 

從數學上講,加法和乘法associative但減法不是。在編程語言中,這三種操作通常都是左關聯的(例如參見Operator associativity)以產生數學上正確的結果。也就是說,減法在上面的查詢序列將被計算:

1-2-3 = (1-2)-3 = -4 

定義這些操作的順序的方式類似於以下的計算:

[A,B,C]: ((0 op C) op B) op A 

,對於加建工程出精品:

[1,2,3]: ((0 + 3) + 2) + 1 = 6 

但它確實不是減法:

[1,2,3]: ((0 - 3) - 2) - 1 = -6 

和乘法時,它是負責第二,不正確的解決方案:

[1,2,3]: ((0 * 3) * 2) * 1 = 0 

也有你的代碼的一些其他問題(例如見@ lurker的評論),但是,我不會在這方面進一步細節。相反,我建議一個嚴格遵守指定生產規則的謂詞。由於語法描述表達式並且你想知道相應的值,所以我們稱之爲expr_val/2。現在讓我們來描述自上而下的表達可能是什麼:它可以是一個數字:

expr_val(X,X) :- 
    number(X). 

它可以分別任意長度的加法或減法或乘法的順序。由於上述原因,所有三個序列應該以左聯合方式進行評估。所以這是很有誘惑力的使用一個規則爲所有這些:

expr_val([Op|Es],V) :- 
    sequenceoperator(Op),  % Op is one of the 3 operations 
    exprseq_op_val(Es,Op,V). % V is the result of a sequence of Ops 

功率函數如下三個元素的列表,第一個是^其餘均爲表達式。使得規則是非常簡單的:

expr_val([^,E1,E2],V) :- 
    expr_val(E1,V1), 
    expr_val(E2,V2), 
    V is V1^V2. 

爲正弦和餘弦的表達是具有兩個元件這兩個列表,第一個是sincos,第二個是一個表達式。請注意,sincos的參數是以弧度表示的角度。如果列表的第二個參數以弧度表示角度,則可以像在代碼中一樣使用sin/1和cos/2。但是,如果以度爲單位獲得角度,則需要先將其轉換爲弧度。我把後一種情況作爲例子,使用適合你的應用的那種。

expr_val([sin,E],V) :- 
    expr_val(E,V1), 
    V is sin(V1*pi/180).  % radians = degrees*pi/180 
expr_val([cos,E],V) :- 
    expr_val(E,V1), 
    V is cos(V1*pi/180).  % radians = degrees*pi/180 

對於expr_val/2,你需要定義三種可能的順序運算符的第二條規則:

sequenceoperator(+). 
sequenceoperator(-). 
sequenceoperator(*). 

並隨後謂詞exprseq_op_val/3。由於領先的操作員已經從expr_val/2的列表中刪除,該列表必須根據您的規範至少包含兩個元素。爲了在該列表的頭部的值被作爲一個累加器來另一個謂詞通過一個左結合的方法來評估序列exprseq_op_val_/4

exprseq_op_val([E1,E2|Es],Op,V) :- 
    expr_val(E1,V1), 
    exprseq_op_val_([E2|Es],Op,V,V1). 

被描述實際的評價。基本上有兩種情況:如果列表爲空,那麼無論操作員如何,累加器都會保存結果。否則,該列表至少包含一個元素。在這種情況下另一個謂詞,op_val_args/4,提供,其隨後遞歸作爲累加器傳入的各個操作(Acc1)的結果與所述列表的尾部(Es)至exprseq_op_val_/4旁邊:

exprseq_op_val_([],_Op,V,V). 
exprseq_op_val_([E1|Es],Op,V,Acc0) :- 
    expr_val(E1,V1), 
    op_val_args(Op,Acc1,Acc0,V1), 
    exprseq_op_val_(Es,Op,V,Acc1). 

最後,你必須定義op_val_args/4,再次是非常簡單的:

op_val_args(+,V,V1,V2) :- 
    V is V1+V2. 
op_val_args(-,V,V1,V2) :- 
    V is V1-V2. 
op_val_args(*,V,V1,V2) :- 
    V is V1*V2. 

現在讓我們來看看它如何工作。首先你的榜樣查詢:

?- expr_val([*,1,[^,2,2],[*,2,[+,[sin,0],5]]],V). 
V = 40.0 ? ; 
no 

按照您的要求最簡單的表達式是一個數字:

?- expr_val(-3.14,V). 
V = -3.14 ? ; 
no 

的空單是不是一種表達:

?- expr_val([],V). 
no 

運營商+-*至少需要2個參數:

?- expr_val([-],V). 
no 
    ?- expr_val([+,1],V). 
no 
    ?- expr_val([*,1,2],V). 
V = 2 ? ; 
no 
    ?- expr_val([-,1,2,3],V). 
V = -4 ? ; 
no 

冪函數正好有兩個參數:

?- expr_val([^,1,2,3],V). 
no 
    ?- expr_val([^,2,3],V). 
V = 8 ? ; 
no 
    ?- expr_val([^,2],V). 
no 
    ?- expr_val([^],V). 
no 

等等......