Вложенные петли с аккумуляторами в B-Prolog
B-Prolog имеет логические циклы. Например, вот как мы можем вычислить сумму [1,2,3]:
test1 :-
foreach(A in 1..3, [], ac(Sa, 0), (
Sa^1 is Sa^0 + A
)),
writeln(sa(Sa)).
?- test1.
test1.
sa(6)
yes
Но когда я пробую два вложенных цикла с аккумуляторами, я получаю ошибки:
test2 :-
foreach(_A in 1..3, [Sb], ac(Sa, 0), (
foreach(B in 1..3, [], ac(Sb, 0), (
Sb^1 is Sb^0 + B
)),
writeln(sb(Sb)),
Sa^1 is Sa^0 + Sb
)),
writeln(sa(Sa)).
?- test2.
test2.
*** error(invalid_argument,(+)/2)
Другой вариант, не включающий Sb в список локальных переменных внешнего цикла:
test3 :-
foreach(_A in 1..3, [], ac(Sa, 0), (
foreach(B in 1..3, [], ac(Sb, 0), (
Sb^1 is Sb^0 + B
)),
writeln(sb(Sb)),
Sa^1 is Sa^0 + Sb
)),
writeln(sa(Sa)).
?- test3.
test3.
sb(6)
*** error(invalid_argument,(+)/2)
Возможно ли даже иметь вложенные циклы с аккумуляторами в B-Prolog?
Моя версия B-Prolog - 8.0#1.
2 ответа
Ваша программа работает правильно при компиляции.
|? - кл (сергей)
Compiling::sergey.pl
** Warning: Variable 'Sb' is treated as global in foreach (17-25).
** Warning: Variable 'Sb' is treated as global in list_comprehension (36-38).
compiled in 0 milliseconds
loading...
yes
| ?- test1
sa(6)
yes
| ?- test2
sb(6)
sb(6)
sb(6)
sa(18)
yes
| ?- test3
sb(6)
sb(6)
sb(6)
sa(18)
Там должна быть какая-то проблема с переводчиком. Этот аккумулятор очень уродливый, и я никогда им не пользуюсь. В Picat, преемнике B-Prolog, вы можете использовать:= для "обновления" переменных.
test1 =>
Sa = 0,
foreach(A in 1..3)
Sa := Sa+A
end,
writeln($sa(Sa)).
test2 =>
Sa = 0,
foreach(_A in 1..3)
Sb := 0,
foreach(B in 1..3)
Sb := Sb+B
end,
writeln($sb(Sb)),
Sa := Sa+Sb
end,
writeln($sa(Sa)).
Еще лучший способ - использовать понимание списка.
test1 =>
Sa = sum([A : A in 1..3]),
writeln($sa(Sa)).
test2 =>
Sa = sum([Sb : _A in 1..3, Sb=sum([B : B in 1..3])]),
writeln($sa(Sa)).
Компилятор компилирует суммы в программы, использующие ':='. Поскольку списки на самом деле не создаются, накладные расходы отсутствуют.
http://www.probp.com/manual/node55.html кажется, показывает вложенный цикл, сплющенный:
?-foreach(A in [a,b], I in 1..2, ac(L,[]), L^1=[(A,I)|L^0]).
L = [(b,2),(b,1),(a,2),(a,1)]
Нам просто нужно восстановить условие конца внутреннего цикла. Посмотрите, работает ли это:
test2 :-
foreach(A in 1..3,
B in 1..3, [], [ac(Sa, 0), ac(La, []) ac(Sb, 0)], (
( ( La^0 = [] ; La^0 = [A] ) % or just (La^0 = [A])
-> % same A, next B
Sa^1 is Sa^0, Sb^1 is Sb^0 + B
; % new A, B-loop has ended
Sa^1 is Sa^0 + Sb^0, writeln(sb(Sb^0)), Sb^1 is 0 + B
),
La^1 = [A]
)),
Sa_final = Sa + Sb, % process the final iteration
writeln(sa(Sa_final)).
Если могут быть повторения в A
s, просто проиндексируйте их, как показывают документы:
?-foreach((A,I) in ([a,b],1..2), ac(L,[]), L^1=[(A,I)|L^0]).
L = [(b,2),(a,1)]
и разбить на изменения в I
,
(Не испытано).