Генерация 3 случайных чисел в Прологе: forall, foreach, findall, bagof, maplist - PullRequest
1 голос
/ 04 апреля 2020

Я хочу сгенерировать 3 случайных числа, используя так называемые предикаты «итерация и сбор».

Я собираю комментарии по любой из следующих попыток.

Запись встроенного предиката сбивает с толку и подвержен ошибкам (или, возможно, даже невозможен с is там?), поэтому мы определяем:

side_effect_and_unify(X) :- 
   format("Called with ~w\n",X),
   X is random(100),
   format("Returning with ~w\n",X).

Используя forall/2

Мы читаем:

Предикат forall/2 (forall(:Cond,:Action)) реализован как \+ ( Cond, \+ Action), т. Е. Нет экземпляра Cond , для которого Action ложно ..

Это должно "отгородить" любые переменные экземпляры вообще. Не удивительно:

?- length(L,3),forall(member(X,L),side_effect_and_unify(X)).
Called with _32964
Returning with 94
Called with _32964
Returning with 36
Called with _32964
Returning with 93
L = [_33490, _33496, _33502].

100% уверены, что невозможно получить эти случайные числа в L.

, используя foreach/2

Мы читаем:

В отличие от forall/2, который запускает отказоустойчивый l oop, который доказывает Цель для каждого решения Генератор , foreach/2 создает соединение. Каждый член соединения является копией Goal , где переменные, которые он разделяет с Generator , заполняются значениями из соответствующего решения.

?- length(L,3),foreach(member(X,L),side_effect_and_unify(X)).
Called with _29916
Returning with 33
Called with _29916
Returning with 3
Called with _29916
Returning with 97
L = [_30434, _30440, _30446].

Нет радости, но возможно ли это?

Описание заставляет меня думать, что это то, что работает:

?- L=[L1,L2,L3],side_effect_and_unify(L1),side_effect_and_unify(L2),side_effect_and_unify(L3).
Called with _31202
Returning with 88
Called with _31208
Returning with 81
Called with _31214
Returning with 5
L = [88, 81, 5],
L1 = 88,
L2 = 81,
L3 = 5.

, но, очевидно, нет.

Использование findall/3

?- length(L,3),findall(X,(member(X,L),side_effect_and_unify(X)),Out).
Called with _28410
Returning with 75
Called with _28410
Returning with 18
Called with _28410
Returning with 66
L = [_29046, _29052, _29058],
Out = [75, 18, 66].

Да!

Использование bagof/3

bagof/3 дружелюбнее, но мы должны сказать, что «не связывай вар L в цель». Я не совсем уверен относительно значения: очевидно, это означает, что L является переменной над целью, поэтому она может принимать значение, определенное для L вне цели.

Хотя здесь мы читаем:

Эта функция светится в сочетании с bagof/3 или setof/3, где вы обычно должны указывать переменные, в которых связывание, которое вас не интересует, используя конструкцию Var^Goal (пометив Var как экзистенциально количественно).

Это будет означать обратное: L - это свободная переменная внутри цели (но тогда строка ниже не будет работать). Как правильно думать об этом?

?- length(L,3),bagof(X,L^(member(X,L),side_effect_and_unify(X)),Out).
Called with _24336
Returning with 10
Called with _24336
Returning with 24
Called with _24336
Returning with 22
L = [_24912, _24918, _24924],
Out = [10, 24, 22].

Другая специальность bagof/3 основана на замечании здесь :

[это] не ' т копирует условия, которые он накапливает в списке. Это может быть фундаментальным, например, когда ваш список будет собирать атрибутивные переменные, например, те, которые вы используете при моделировании проблемы с библиотекой (clpfd).

Использование maplist/2

Ладно, это просто.

?- length(L,3),maplist(side_effect_and_unify,L).
Called with _26224
Returning with 23
Called with _26230
Returning with 63
Called with _26236
Returning with 70
L = [23, 63, 70].

Не только приятнее, но ИМХО, фактически читабельно читаемо с yall. Вы на самом деле видите метаколл:

?- length(L,3),maplist([X]>>side_effect_and_unify(X),L).
Called with _27420
Returning with 34
Called with _27426
Returning with 41
Called with _27432
Returning with 25
L = [34, 41, 25].
...