Я не понимаю, как я могу сделать оба с одной функцией.
Да, поначалу это кажется странным, но потом, когда вы освоите его, вы упустите его на других языках.
Слово, которое вы хотите запомнить при ссылке на это: mode
Также см. Справочник по режимам Mercury для более подробной информации о языке программирования Mercury.
В Прологе аргумент может быть входным и выходным, или оба могут использоваться в качестве входных или выходных в зависимости от того, как он вызывается.
В Заголовки объявления типа, режима и детерминизма внизу перечислены 4 примера.
- длина (+ Список: список, -Длина: int) является дет.
- длина (? List: list, -Length: int) не определена.
- длина (? Список: список, + длина: int) является дет.
- Истинно, если List является списком длины Length.
и определение length/2
показывает
length(?List, ?Int)
смысл
что для аргумента List
список может быть связан или не связан, и
что для аргумента Int
значение может быть связано или не связано.
Таким образом, для двух аргументов с двумя опциями существует четыре способа использования length/2
Здесь они перечислены снова, но в фактическом использовании.
1
length(+List:list, -Length:int) is det.
в этом случае List связан, а Length не связан и всегда дает один ответ.
?- length([1,2,3],N).
N = 3.
2
length(?List:list, -Length:int) is nondet.
в этом случае List не связан, а Length не связан и может вернуть любое количество ответов.
?- length(List,N).
List = [],
N = 0 ;
List = [_5362],
N = 1 ;
List = [_5362, _5368],
N = 2 ;
List = [_5362, _5368, _5374],
N = 3 ;
List = [_5362, _5368, _5374, _5380],
N = 4 ;
List = [_5362, _5368, _5374, _5380, _5386],
N = 5
...
3.
length(?List:list, +Length:int) is det.
в этом случае List не связан, а Length связан и всегда дает один ответ.
?- length(List,4).
List = [_5332, _5338, _5344, _5350].
4
True if List is a list of length Length.
в этом случае List связан, а Length связан и всегда действует как предикат, возвращая либо true
, либо false
.
?- length([1,2,3],3).
true.
?- length([1,2,3],5).
false.
Так как это возможно?
Пролог использует синтаксическое объединение (↦) и НЕ назначение (=).
Если мы посмотрим на исходный код length/2
, используя listing/1
, мы получим
?- listing(length/2).
system:length(B, A) :-
var(A), !,
'$skip_list'(D, B, C),
( C==[]
-> A=D
; var(C)
-> C\==A,
'$length3'(C, A, D)
; throw(error(type_error(list, B), context(length/2, _)))
).
system:length(B, A) :-
integer(A),
A>=0, !,
'$skip_list'(D, B, C),
( C==[]
-> A=D
; var(C)
-> E is A-D,
'$length'(C, E)
; throw(error(type_error(list, B), context(length/2, _)))
).
system:length(_, A) :-
integer(A), !,
throw(error(domain_error(not_less_than_zero, A),
context(length/2, _))).
system:length(_, A) :-
throw(error(type_error(integer, A), context(length/2, _))).
, что слишком много деталей, но правильно выполняет все 4 режима.
Чтобы было легче понять, мы будем использовать эту версию, но она не поддерживает 1 режим правильно, но работает более чем в одном режиме, поэтому работает достаточно хорошо для демонстрации.
length_2([] , 0 ).
length_2([_|Xs] , L ) :-
length_2(Xs,N),
L is N+1 .
Теперь, чтобы увидеть объединение в действии, мы будем использовать функцию trace в SWI-Prolog и включить все порты для блочной модели , использовать visible / 1 и чтобы не останавливать его при запуске используйте поводок / 1 .
?- visible(+all),leash(-all).
?- trace.
1.
[trace] ?- length_2([1,2,3],N).
Call: (8) length_2([1, 2, 3], _2352)
Unify: (8) length_2([1, 2, 3], _2352)
Call: (9) length_2([2, 3], _2580)
Unify: (9) length_2([2, 3], _2580)
Call: (10) length_2([3], _2580)
Unify: (10) length_2([3], _2580)
Call: (11) length_2([], _2580)
Unify: (11) length_2([], 0)
Exit: (11) length_2([], 0)
Call: (11) _2584 is 0+1
Exit: (11) 1 is 0+1
Exit: (10) length_2([3], 1)
Call: (10) _2590 is 1+1
Exit: (10) 2 is 1+1
Exit: (9) length_2([2, 3], 2)
Call: (9) _2352 is 2+1
Exit: (9) 3 is 2+1
Exit: (8) length_2([1, 2, 3], 3)
N = 3.
2
[trace] ?- length_2(List,N).
Call: (8) length_2(_2296, _2298)
Unify: (8) length_2([], 0)
Exit: (8) length_2([], 0)
List = [],
N = 0 ;
Redo: (8) length_2(_2296, _2298)
Unify: (8) length_2([_2528|_2530], _2298)
Call: (9) length_2(_2530, _2550)
Unify: (9) length_2([], 0)
Exit: (9) length_2([], 0)
Call: (9) _2298 is 0+1
Exit: (9) 1 is 0+1
Exit: (8) length_2([_2528], 1)
List = [_2528],
N = 1 ;
Redo: (9) length_2(_2530, _2550)
Unify: (9) length_2([_2534|_2536], _2556)
Call: (10) length_2(_2536, _2556)
Unify: (10) length_2([], 0)
Exit: (10) length_2([], 0)
Call: (10) _2560 is 0+1
Exit: (10) 1 is 0+1
Exit: (9) length_2([_2534], 1)
Call: (9) _2298 is 1+1
Exit: (9) 2 is 1+1
Exit: (8) length_2([_2528, _2534], 2)
List = [_2528, _2534],
N = 2 ;
Redo: (10) length_2(_2536, _2556)
Unify: (10) length_2([_2540|_2542], _2562)
Call: (11) length_2(_2542, _2562)
Unify: (11) length_2([], 0)
Exit: (11) length_2([], 0)
Call: (11) _2566 is 0+1
Exit: (11) 1 is 0+1
Exit: (10) length_2([_2540], 1)
Call: (10) _2572 is 1+1
Exit: (10) 2 is 1+1
Exit: (9) length_2([_2534, _2540], 2)
Call: (9) _2298 is 2+1
Exit: (9) 3 is 2+1
Exit: (8) length_2([_2528, _2534, _2540], 3)
List = [_2528, _2534, _2540],
N = 3
3.
[trace] ?- length_2(List,3).
Call: (8) length_2(_5534, 3)
Unify: (8) length_2([_5724|_5726], 3)
Call: (9) length_2(_5726, _5746)
Unify: (9) length_2([], 0)
Exit: (9) length_2([], 0)
Call: (9) 3 is 0+1
Fail: (9) 3 is 0+1
Redo: (9) length_2(_5726, _5746)
Unify: (9) length_2([_5730|_5732], _5752)
Call: (10) length_2(_5732, _5752)
Unify: (10) length_2([], 0)
Exit: (10) length_2([], 0)
Call: (10) _5756 is 0+1
Exit: (10) 1 is 0+1
Exit: (9) length_2([_5730], 1)
Call: (9) 3 is 1+1
Fail: (9) 3 is 1+1
Redo: (10) length_2(_5732, _5752)
Unify: (10) length_2([_5736|_5738], _5758)
Call: (11) length_2(_5738, _5758)
Unify: (11) length_2([], 0)
Exit: (11) length_2([], 0)
Call: (11) _5762 is 0+1
Exit: (11) 1 is 0+1
Exit: (10) length_2([_5736], 1)
Call: (10) _5768 is 1+1
Exit: (10) 2 is 1+1
Exit: (9) length_2([_5730, _5736], 2)
Call: (9) 3 is 2+1
Exit: (9) 3 is 2+1
Exit: (8) length_2([_5724, _5730, _5736], 3)
List = [_5724, _5730, _5736]
Action (h for help) ? abort
% Execution Aborted
4.
[trace] ?- length_2([1,2,3],3).
Call: (8) length_2([1, 2, 3], 3)
Unify: (8) length_2([1, 2, 3], 3)
Call: (9) length_2([2, 3], _2058)
Unify: (9) length_2([2, 3], _2058)
Call: (10) length_2([3], _2058)
Unify: (10) length_2([3], _2058)
Call: (11) length_2([], _2058)
Unify: (11) length_2([], 0)
Exit: (11) length_2([], 0)
Call: (11) _2062 is 0+1
Exit: (11) 1 is 0+1
Exit: (10) length_2([3], 1)
Call: (10) _2068 is 1+1
Exit: (10) 2 is 1+1
Exit: (9) length_2([2, 3], 2)
Call: (9) 3 is 2+1
Exit: (9) 3 is 2+1
Exit: (8) length_2([1, 2, 3], 3)
true.
[trace] ?- length_2([1,2,3],5).
Call: (8) length_2([1, 2, 3], 5)
Unify: (8) length_2([1, 2, 3], 5)
Call: (9) length_2([2, 3], _2442)
Unify: (9) length_2([2, 3], _2442)
Call: (10) length_2([3], _2442)
Unify: (10) length_2([3], _2442)
Call: (11) length_2([], _2442)
Unify: (11) length_2([], 0)
Exit: (11) length_2([], 0)
Call: (11) _2446 is 0+1
Exit: (11) 1 is 0+1
Exit: (10) length_2([3], 1)
Call: (10) _2452 is 1+1
Exit: (10) 2 is 1+1
Exit: (9) length_2([2, 3], 2)
Call: (9) 5 is 2+1
Fail: (9) 5 is 2+1
Fail: (8) length_2([1, 2, 3], 5)
false.
и отключить трассировку
[trace] ?- notrace.
true.
[debug] ?- nodebug.
true.
Я не буду проходить каждую из строк в выводе трассировки, но если вы понимаете синтаксическое объединение и можете следовать трассировке, после проработки приведенных примеров вы увидите, как объединяются переменные в Prolog, что приводит к различным режимам, когда по сравнению с императивным программированием.
Помните, что переменные связаны только один раз в Прологе и никогда не переназначаются, и что числа в левой части трассы в скобках, например, (10) - уровень стека, поэтому, когда снова делается вызов предикату, становится доступным новый набор переменных, и, хотя может показаться, что ему присваивается значение, на самом деле это другая переменная в стеке, просто в другом стековом фрейме .
В качестве отступления при изучении Пролога, один совет, который я даю, состоит в том, что легче учиться, если вы откладываете то, что знаете об императивном и функциональном программировании, за исключением, возможно, рекурсии, и начинаете с нуля с объединения, а затем обратная цепочка .
Если вы можете прочитать OCaml, здесь - это упрощенная версия объединения и обратной цепочки. Обратите внимание, что это не Пролог, так как в нем нет списка или оператора вырезки, но если вы можете это понять, то пути объединения и обратного сцепления становятся очевидными.
Я должен добавить, что я не полностью удовлетворен своим ответом, так как знаю, что вы новичок, и этот ответ предназначен для усвоения большого количества информации и требует от вас большой работы для прохождения 4 примеров трассировки. Однако он отвечает на вопрос и дает практический пример с более чем достаточным количеством мяса на кости. Я работаю над попыткой придумать лучший пример, который включал бы логическая чистота и который демонстрирует, что не только объединение, но и отношения являются ключом к тому, как несколько режимов могут быть достигнуты в одном предикате. Радуйтесь, что я не использовал общую теорию относительности, как перефразировал релятивист Джон Джон Арчибальд Уилер, spacetime tells matter how to move; matter tells spacetime how to curve
.