前言:如何生成简单的数学表达式?

时间:2019-01-31 08:36:11

标签: prolog

我需要写(而不是计算)表示:的数字列表的所有状态

输入:

Numbers: 1,2,3
Operators: +,-,/,*

输出:

1+2+3
1-2-3
1/2/3
1*2*3
1+2-3
1+2/3
1+2*3
1-2+3
1-2/3
1-2*3
1/2+3
1/2-3
1/2+3
1*2+3
1*2-3
1+2-3

在下降代码中仅显示1+2+3

如何将它们发展到所有州?

list_sum([Item], Item).
list_sum([Item1,Item2 | Tail], Total) :-
   list_sum([Item1+Item2|Tail], Total).

5 个答案:

答案 0 :(得分:5)

Carlo Capelli发布的不错的解决方案的变体使我们能够说明一个有用的编程习惯用法,以更好地利用第一参数索引:

list_combine([N1| Ns], Os, Nt) :-
    list_combine(Ns, N1, Os, Nt).

list_combine([], N, _, [N]).
list_combine([N2| Ns], N1, Os, [N1, O| Nt]) :-
    member(O, Os),
    list_combine(Ns, N2, Os, Nt).

想法是通过将列表的头部与尾部分开来传递我们要遍历的列表,并将两者都作为参数传递,而将尾部作为第一个参数传递,如上所述。

在原始解决方案中,Prolog编译器通常不会区分只有一个元素的列表和只有一个或多个元素的列表。但是它将区分一个空列表(一个原子)和一个至少包含一个元素的列表(一个复合术语)。还请注意,除了list_combine/3谓词调用上的预期选择点之外,原始版本还会在对member/2谓词的调用上为每个递归调用创建一个虚假选择点。

答案 1 :(得分:4)

简单的递归:

list_combine([N|Nr],Os,[N,O|Nt]) :-
    member(O,Os),
    list_combine(Nr,Os,Nt).
list_combine([N],_,[N]).

现在

?- forall(list_combine([1,2,3],[+,*],C),writeln(C)).
[1,+,2,+,3]
[1,+,2,*,3]
[1,*,2,+,3]
[1,*,2,*,3]
true.

这是一个-也许-更易读的版本

list_combine(Ns,Os,Cs) :-
    [N|Nr] = Ns,
    member(O,Os),
    Cs = [N,O|Nt],
    list_combine(Nr,Os,Nt).

当然,可以使用替代方法,只是为了更好地理解统一如何分解和组合参数。

答案 2 :(得分:2)

DCGphrase/2一起用作generator

operator --> [+].
operator --> [-].
operator --> [*].
operator --> [/].

expr_trinary -->
  [1],
  operator,
  [2],
  operator,
  [3].

expr(E) :-
    phrase(expr_trinary,Expr_trinary),
    atomics_to_string(Expr_trinary,E).

示例运行:

?- expr(E).
E = "1+2+3" ;
E = "1+2-3" ;
E = "1+2*3" ;
E = "1+2/3" ;
E = "1-2+3" ;
E = "1-2-3" ;
E = "1-2*3" ;
E = "1-2/3" ;
E = "1*2+3" ;
E = "1*2-3" ;
E = "1*2*3" ;
E = "1*2/3" ;
E = "1/2+3" ;
E = "1/2-3" ;
E = "1/2*3" ;
E = "1/2/3".

由于您的问题适用于列表,因此将DCG视为列表处理的一种方法是使用listing/1将其转换为常规Prolog。

?- listing(operator).
operator([+|A], A).
operator([-|A], A).
operator([*|A], A).
operator([/|A], A).

true.

?- listing(expr_trinary).
expr_trinary([1|A], B) :-
    operator(A, C),
    C=[2|D],
    operator(D, E),
    E=[3|B].

true.

可以称为常规Prolog。

?- expr_trinary(E,[]).
E = [1, +, 2, +, 3] ;
E = [1, +, 2, -, 3] ;
E = [1, +, 2, *, 3] ;
E = [1, +, 2, /, 3] ;
E = [1, -, 2, +, 3] ;
E = [1, -, 2, -, 3] ;
E = [1, -, 2, *, 3] ;
E = [1, -, 2, /, 3] ;
E = [1, *, 2, +, 3] ;
E = [1, *, 2, -, 3] ;
E = [1, *, 2, *, 3] ;
E = [1, *, 2, /, 3] ;
E = [1, /, 2, +, 3] ;
E = [1, /, 2, -, 3] ;
E = [1, /, 2, *, 3] ;
E = [1, /, 2, /, 3].

在任意位置使用数字(1,2,3)的扩展解决方案:

number --> [1].
number --> [2].
number --> [3].

operator --> [+].
operator --> [-].
operator --> [*].
operator --> [/].

expr_trinary -->
  number,
  operator,
  number,
  operator,
  number.  

expr(E) :-
    phrase(expr_trinary,Expr_trinary),
    atomics_to_string(Expr_trinary,E).

示例运行:

?- expr(E).
E = "1+1+1" ;
E = "1+1+2" ;
E = "1+1+3" ;
E = "1+1-1" ;
E = "1+1-2" ;
E = "1+1-3" ;
...

有关如何使用DCG生成的说明,请参见以下Amzi部分:Generating with Difference Lists


在对您写的其他答案的评论中:

  

它不能工作超过3个元素吗?您可以为更多元素开发它吗?

随着元素数量的增加,combinatorial explosion也随之增加。

要使示例运行的组合爆炸更小,将仅使用两个数字(1,2)和两个运算符(+,*),您可以根据自己的喜好添加更多内容。

number(1) --> [1].
number(2) --> [2].

operator(+) --> [+].
operator(*) --> [*].

expr(N) --> number(N).
expr((E1,Op,E2)) --> operator(Op),expr(E1),expr(E2).

expr(E) :-
    length(Expr,_),
    phrase(expr(E),Expr).

请注意,这将length/2用于iterative deepening。基本上length/2会生成长度不断增加的列表,然后phrase/2会给出该长度的答案。

?- length(Ls,N).
Ls = [],
N = 0 ;
Ls = [_870],
N = 1 ;
Ls = [_870, _876],
N = 2 ;
Ls = [_870, _876, _882],
N = 3 ;
Ls = [_870, _876, _882, _888],
N = 4 ;
Ls = [_870, _876, _882, _888, _894],
N = 5 ;
Ls = [_870, _876, _882, _888, _894, _900],
N = 6 
...

例如,正常生成的BNF和DCG可以使生成器按预期工作

<expr> ::= <expr> <op> <expr>

expr((E1,Op,E2)) --> expr(E1),operator(Op),expr(E2).

direct left recursive转换为此

<expr> ::= <op> <expr> <expr>

expr((E1,Op,E2)) --> operator(Op),expr(E1),expr(E2).

示例运行:

?- expr(E).
E = 1 ;
E = 2 ;
E =  (1, (+), 1) ;
E =  (1, (+), 2) ;
E =  (2, (+), 1) ;
E =  (2, (+), 2) ;
E =  (1, (*), 1) ;
E =  (1, (*), 2) ;
E =  (2, (*), 1) ;
E =  (2, (*), 2) ;
E =  (1, (+), 1, (+), 1) ;
...
E =  (1, (+), 2, (+), 2, (*), 1) ;
E =  (1, (+), 2, (+), 2, (*), 2) ;
E =  (1, (+), (1, (+), 1), (+), 1) ;
E =  (1, (+), (1, (+), 1), (+), 2) ;
E =  (1, (+), (1, (+), 2), (+), 1) ;
...

答案 3 :(得分:1)

一种方法是

get_calcul([X], _, Temp, Calcul):-
    append(Temp, [X], Calcul).

get_calcul([N|T], [Op|Top], Temp, Out) :-
    append(Temp, [N, Op], Temp1),
   get_calcul(T, Top, Temp1, Out).

all_operations(In, Out) :-
   setof(Op, X^Ops^(permutation([+,-,*,/], X), get_calcul(In, X, [], Ops), atomic_list_concat(Ops, Op)), Out).

结果

?- all_operations([1,2,3], Out).
Out = ['1*2+3', '1*2-3', '1*2/3', '1+2*3', '1+2-3', '1+2/3', '1-2*3', '1-2+3', '1-2/3'|...].

好吧,我解决了我的问题,而不是您的问题!

这可以做到:

member_(In, X) :-
    member(X, In).

get_calcul([N], _, Temp, Out) :-
    append(Temp, [N], Out).

get_calcul([N|T], [Op|Top], Temp, Out) :-
    append(Temp, [N, Op], Temp1),
   get_calcul(T, Top, Temp1, Out).

all_operations(In, Out) :-
    % if you have N numbers
    length(In, Len),
    % you need N-1 operators
    LenOps is Len - 1,
    length(LOps, LenOps),
   setof(Op, LOps^Ops^(maplist(member_([+,-,*,/]), LOps),get_calcul(In, LOps, [], Ops), atomic_list_concat(Ops, Op)), Out).

例如:

 ?- all_operations([1,2,3], Out), maplist(writeln, Out).
1*2*3
1*2+3
1*2-3
1*2/3
1+2*3
1+2+3
1+2-3
1+2/3
1-2*3
1-2+3
1-2-3
1-2/3
1/2*3
1/2+3
1/2-3
1/2/3
Out = ['1*2*3', '1*2+3', '1*2-3', '1*2/3', '1+2*3', '1+2+3', '1+2-3', '1+2/3', '1-2*3'|...].

答案 4 :(得分:1)

enter image description here这是我的解决方案建议,我发现它很简单直接,将下面的内容复制并粘贴到notepad ++编辑器中以获得最佳可读性。

* ________________________________________________                           *
*|find_expression(NumsList,TargetValue,Expression)|                          *
**------------------------------------------------*                          *
* Expression is an arithmetic expression of the numbers in Numslist with     *
* possible operators '+','-','*','/' and '(' and ')' between the numbers     *
*  in such a way that the expression evaluates to the TargetValue argument   *  
*****************************************************************************/%

/* a single element number list can evaluate only to itself */ 
find_expression([SingleNumber],SingleNumber,SingleNumber).

/* expression of a multypile number list */ 
find_expression(NumberList,Target,Expression):-

/* non-deterministically  divide the number list 
 into 2 separate lists which include at least one number each*/ 
append([X|Xs],[Y|Ys], NumberList),

/* recursively find an expression for east list, 
   where the expression evaluates to itself */ 
find_expression([X|Xs],Exp1,Exp1),
find_expression([Y|Ys],Exp2,Exp2),

/* non-deterministically  choose an operand from [+,-,*,division] 
   and compose Expression to be (Exp1 Operand Exp2) */ 
(   member(Expression,[Exp1+Exp2,Exp1-Exp2,Exp1*Exp2]) 
    ; /* prevent zero divison */
    (Val2 is Exp2, Val2 =\= 0, Expression = (Exp1/Exp2))), %/*

/* assure that final expression evaluates(matches) the target value 
   and convert value from integer to float if necessary */
( Target = Expression ; Target is Expression 
  ; FloatTarget is Target*1.0, FloatTarget is Expression).