Перевести понимание списка в Prolog
у меня есть понимание списка в Haskell, которое я хочу перевести в Prolog.
точка понимания списка вращает сетку 4 на 4:
rotate :: [Int] -> [Int]
rotate grid = [ grid !! (a + 4 * b) | a <- [0..3], b <- [0..3] ]
теперь в прологе я перевел это так:
rotateGrid([T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15],
[T0,T4,T8,T12,T1,T5,T9,T13,T2,T6,T10,T14,T3,T7,T11,T15]).
можем мы сделать лучше?
3 ответов
можно использовать findall/3
для понимания списка (ср. the документация SWI-Prolog). Е. Г.,
?- findall(X, between(1,10,X), Xs).
Xs = [1,2,3,4,5,6,7,8,9,10]
Xs
- это список, содержащий все значения, которые могут объединяться с X
, когда X
- это число от 1 до 10. Это примерно эквивалентно выражению Haskell let Xs = [x | x <- [1..10]]
(1). Вы можете прочитать findall/3
утверждение таким образом: "найдите все значения [первого аргумента] такие, что [условия во втором аргументе] удерживаются, и поместите эти значения в список, [третий Аргумент."]
я использовал findall/3
написать предикат rotate_grid(+Grid, ?RotatedGrid)
. Вот список приближенных эквивалентностей Haskell-Prolog, которые я использовал в предикате; каждая строка показывает отношение между значением, которое выражение Haskell будет оценивать, и переменной Prolog с тем же значением:
-
a <- [0..3]
=A
наbetween(0, 3, A)
-
b <- [0..3]
=B
наbetween(0, 3, B)
-
(a + 4 * d)
=X
inX is A + 4 * D
-
<Grid> !! <Index>
=Element
наnth0(Index, Grid, Element)
тогда просто нужно найти все значения Element
:
rotate_grid(Grid, RotatedGrid) :-
findall( Element,
( between(0,3,A),
between(0,3,B),
Index is A + 4 * B,
nth0(Index, Grid, Element) ),
RotatedGrid
).
чтобы убедиться, что это производит правильное преобразование, я опустил код пролога из вопроса и задал следующий запрос:
?- rotate_grid([t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15],
[t0,t4,t8,t12,t1,t5,t9,t13,t2,t6,t10,t14,t3,t7,t11,t15]).
| true.
Примечания:
(1): between/3
на самом деле не аналог [m..n]
, так как последний возвращает список значения m
to n
здесь between(M,N,X)
будет создавать экземпляр X с каждым значением между M и N (включительно) при обратном отслеживании. Чтобы получить список номеров в SWI-Prolog, мы можем использовать numlist(M,N,Ns)
. Таким образом, более строгий аналог для x <- [1.10]
будет вместе member(X, Ns), numlist(1, 10, Ns)
.
вы хотите перестановку списка. Конкретные элементы не рассматриваются. Поэтому вы можете обобщить свою подпись Haskell на
rotate :: [x] -> [x]
это уже очень ценная подсказка для Prolog: элементы списка не будут рассматриваться - элементы даже не будут сравниваться. Таким образом, решение Prolog должно иметь возможность напрямую обрабатывать переменные, например:
| ?- rotateGrid(L,R). L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P], R = [_A,_E,_I,_M,_B,_F,_J,_N,_C,_G,_K,_O,_D,_H,_L,_P].
и ваше первоначальное определение отлично справляется с этим.
ваша версия с помощью понимание списка предполагает, что оно должно быть реализовано путем отступления, необходимо принять определенные меры предосторожности. Используя findall/3
, как предложил @aBathologist переименует переменные:
| ?- length(L,16),rotate_grid(L,R). L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P], R = [_Q,_R,_S,_T,_U,_V,_W,_X,_Y,_Z,_A1,_B1,_C1,_D1,_E1,_F1].
встроенный предикат bagof/3
решает эту проблему. Обратите внимание, что мы должны объявить все локальные экзистенциальные переменные явно:
rotate_grid2(Grid, RotatedGrid) :- bagof( Element, A^B^Index^ % declaration of existential variables ( between(0,3,A), between(0,3,B), Index is A + 4 * B, nth0(Index, Grid, Element) ), RotatedGrid).
для списков, которые короче 16 элементов, версия Haskell производит чистую ошибку, но здесь мы получаем довольно случайный результаты:
| ?- L=[1,2,3,4],rotate_grid(L,R). L = [1,2,3,4], R = [1,2,3,4] ? yes | ?- L=[1,2,3,4,5],rotate_grid(L,R). L = [1,2,3,4,5], R = [1,5,2,3,4] ? yes
это связано с неясным разделением между частью, которая перечисляет и" генерирует " конкретный элемент. Самый чистый способ-добавить length(Grid, 16)
до цели bagof/3
.
список постижений в прологе
в настоящее время только B-Prolog предлагает форму понимания списка:
R@=[E: A in 0..3,B in 0..3,[E,I],(I is A+4*B,nth0(I,L,E))].
однако он не решает вторую проблему:
| ?- L = [1,2,3], R@=[E: A in 0..3,B in 0..3,[E,I],(I is A+4*B,nth0(I,L,E))]. L = [1,2,3] R = [1,2,3] yes
используйте предикат цикла foreach / 4
Если понимание должно сохранять переменные, что, например, важно в программировании ограничений, система Prolog может предложить предикат foreach/4. Этот предикат является приятелем DCG foreach / 2.
вот как переменные не сохраняются через findall/3, результат R содержит свежие переменные в соответствии с ISO основная семантика findall / 3:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.1)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- functor(L,foo,5), findall(X,
(between(1,5,N), M is 6-N, arg(M,L,X)), R).
L = foo(_5140, _5142, _5144, _5146, _5148),
R = [_5210, _5204, _5198, _5192, _5186].
а вот как переменные могут быть сохраняется через foreach / 4, результирующий список имеет те же переменные, что и соединение мы начали с:
Jekejeke Prolog 3, Runtime Library 1.3.0
(c) 1985-2018, XLOG Technologies GmbH, Switzerland
?- [user].
helper(N,L) --> [X], {M is 6-N, arg(M,L,X)}.
Yes
?- functor(L,foo,5), foreach(between(1,5,N),helper(N,L),R,[]).
L = foo(_A,_G,_M,_S,_Y),
R = [_Y,_S,_M,_G,_A]
использование foreach / 4 вместо bagof/3 может показаться немного излишним. foreach/4, вероятно, покажет свой полный потенциал только при реализации циклов Picat, поскольку он может создавать ограничения, чего не может сделать bagof / 3.
foreach/4-это реализация без полной материализации всего решения, которое затем возвращается. Он делит с bagof / 3 реконструировать переменные, но все же позволяет отступать в сочетании замыканий.