Пролог: Как генерировать простые математические выражения? - PullRequest
0 голосов
/ 31 января 2019

Мне нужно записать (не рассчитать) все состояния списка чисел, что означает:

Ввод:

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

inнисходящий код просто показать 1+2+3

Как мне развить их до всех состояний?

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

Ответы [ 5 ]

0 голосов
/ 04 августа 2019

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).
0 голосов
/ 31 января 2019

Вариант прекрасного решения, опубликованного Карло Капелли, позволяет нам проиллюстрировать полезную идиому программирования для лучшего использования индексации первого аргумента:

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.

0 голосов
/ 31 января 2019

Использование DCG с фразой / 2 в качестве генератора :

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 в качестве обработки списка - использовать перечисление / 1 для преобразования его в обычный пролог.

?- 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.

, который можетназываться обычным прологом.

?- 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 : Создание со списками различий


В комментариях для другого ответаВы писали:

это не работа более чем на 3 элемента?Можете ли вы разработать его для большего количества элементов?

По мере увеличения числа элементов увеличивается комбинаторный взрыв .

Чтобы подавить комбинаторный взрыв, напримерЗапустите это будет использовать только два числа (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).

Обратите внимание, что это использует длина / 2 за итеративное углубление .В основном length/2 генерирует список увеличивающейся длины, а затем фразу / 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).

, то есть прямая левая рекурсия , преобразуется в этот

<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) ;
...
0 голосов
/ 31 января 2019

простая рекурсия:

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).

Конечно, используйте вальтернатива, просто чтобы лучше понять, как объединение действует при разложении и составлении аргументов.

0 голосов
/ 31 января 2019

Один из способов -

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'|...].
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...