У меня есть функция, которая берет первый элемент из списка и проверяет, является ли этот элемент одним из нескольких элементов, соответственно выполняет некоторые функции и рекурсивно выполняет следующие элементы в списке.
Моя проблема в том, что в строке 6 переменная, созданная как первый элемент списка, больше не определена, и мне трудно понять, почему.
Я только начал работать с KBS, так что простите меня, если я ошибаюсь.
1 f(L) :-
2 [F|Ls] = L,
3 (
4 (F = value1 -> ...);
5 (F = value2 -> ...)
6 ) -> f(Ls); format('~w is not a valid action', [F]).
Пример использования ->/2:
do_something_with_first_elem([Head|_]) :-
do_something_with_elem(Head).
do_something_with_elem(Elem) :-
( Elem = a -> writeln('Head is a')
; Elem = b -> writeln('Head is b')
; Elem = 3 -> writeln('Head is 3 (a number)')
; format('~w is not a valid action', [Elem])
).
Результат в swi-прологе:
?- do_something_with_first_elem([b, c, d]).
Head is b
true.
?- do_something_with_first_elem([z, c, d]).
z is not a valid action
true.
Пожалуйста, отредактируйте свой вопрос, чтобы объяснить, что действие требуется для каждого элемента списка, а не только для первого.
После компиляции кода задайте следующий запрос:
?- listing(f).
f(L) :-
( L=[F|Ls], % <== unification IS PART OF the condition!
( F=value1
-> ...
; F=value2
-> ...
)
-> f(Ls)
; format('~w is not a valid action', [F]) % <== when condition fails, unification is UNDONE
).
Как видите, унификация является частью условия. Таким образом, когда условие терпит неудачу, объединение является отменен, превращая F
в переменную Один.
Для решения проблемы отформатируйте код следующим образом:
f(L) :-
[F|Ls] = L, % <== Now, unification IS NOT PART OF the condition!
( ( (F = value1 -> ...)
; (F = value2 -> ...) )
-> f(Ls)
; format('~w is not a valid action', [F]) ).
Большой! Спасибо за ответ! Это очень помогает. Я не уверен, как сформулировать следующий вопрос, но почему первое объединение было включено компилятором в условие? Как компилятор оценивает выражения AND и соответствующим образом вкладывает структуры? Если есть ссылки по этому вопросу, буду признателен.
Оператор (,)/2
обозначает логическую связку И, а оператор (;)/2
обозначает логическую связку ИЛИ. Поскольку (,)/2
имеет приоритет над (->)/2
, а (->)/2
имеет приоритет над (;)/2
, термин (a, b -> c ; d)
интерпретируется как (((a,b) -> c) ; d)
. Вы можете использовать предикат write_canonical/1
, чтобы увидеть это. Например, запрос ?- write_canonical((a, b -> c ; d)).
показывает ;(->(','(a,b),c),d)
.
Спасибо за ответ. Хотя я ценю это, он не вызывает рекурсивно функтор в остальной части списка. Мне также было интересно узнать, в чем конкретно была логическая ошибка в моем коде.