Loops 打破以/3为基础的;“循环”;在SWI Prolog中,同时保持其后的选择点

Loops 打破以/3为基础的;“循环”;在SWI Prolog中,同时保持其后的选择点,loops,prolog,prolog-cut,Loops,Prolog,Prolog Cut,我需要迭代从1到Length的一系列数字(例如使用between/3),其中Length是给定列表的长度。迭代的数字,比如说N,然后应用于谓词,比如说do\u something,直到它停止失败。也就是说,dou\u something搜索正确的N,然后必须删除/3之间的所有选择点。但是,必须执行do_something的所有选择点,并应用适当的N 第一次尝试是这样的: % main(+InputList, -N, -OutputList) main(InputList, N, OutputLi

我需要迭代从1到
Length
的一系列数字(例如使用
between/3
),其中
Length
是给定列表的长度。迭代的数字,比如说
N
,然后应用于谓词,比如说
do\u something
,直到它停止失败。也就是说,
dou\u something
搜索正确的
N
,然后必须删除/3之间的所有选择点。但是,必须执行
do_something
的所有选择点,并应用适当的
N

第一次尝试是这样的:

% main(+InputList, -N, -OutputList)
main(InputList, N, OutputList) :-
  length(InputList, Length),
  between(1, Length, N),

  % cut all between/3 choice points as soon as Num is instantiated
  freeze(Num, !),

  do_something(InputList, N, OutputList),
  Num is N.
这不起作用,因为
freeze/2
不会在/3选择点之间剪切
。带有
when/2
的版本也不起作用。我推测,这是因为
freeze/2
when/2
都是在一个单独的线程中执行的,并且不会影响/3
之间的
主线程

过了一段时间,我得到了以下代码,这些代码完成了所需的工作,但效率低下:

main(InputList, N, OutputList) :-
  length (InputList, Length),
  between(1, Length, N),
  do_something(InputList, N, _),

  % cut all prior choice points as soon as proper N is found
  !,

  % start do_something over!
  do_something(InputList, N, OutputList).
效率低下的原因是,
do\u something
的选择点与适当的
N
执行了两次。首先是在切割前,然后是切割后

虽然这个解决方案在我的情况下有效,但它不适用于所有
dou\u something
选择点只能执行一次的一般情况

请注意,
N
必须是
1..Length
范围之外的最小值,并且事先不知道。因此,使用
dou\u something
搜索它


有更好的解决办法吗?有没有一种方法可以实现类似于/3
之间的谓词,当以某种方式发出信号时可以停止?是否有一个专门的内置谓词来执行所需的操作?非常感谢任何富有成效的想法。

希望我能理解您的问题描述:

main(InputList, N, OutputList) :-
    length(InputList, Length),
    between(1, Length, N),
    findall(
        OutputList0,
        do_something(InputList,N,OutputList0),
        OutputLists
    ),
    % cut all prior choice points as soon as proper N is found
    OutputLists = [_|_],
    !,
    member(OutputList, OutputLists).
findall/3
调用将返回
Solutions=[]
,直到
dou\u something/3
谓词成功。当这种情况发生时,
findall/3
调用确保,对于
N
的值,
do\u something(InputList,N,OutputList)
的所有选择点都被访问。然后,下面的剪切修复了
N
的值,您可以从那里开始


p.S.更新了您在评论中描述的更改,以使其适用于您的案例。如果您不想收集所有的解决方案,有些不可移植的黑客只能找到给定数量的解决方案。

还有另一种使用的可能性,它是->/2的一种变体,不会扼杀条件的选择点。现在我们不在了,我们想杀死一个老的选秀点。我不知道是否有Prolog系统可以这样做。大多数人都有杀死所有选择点的规定,但我不知道有哪一个会杀死某个特定的选择点。因此,我们必须插入一段代码来有条件地停止进一步的处理。这导致:

main(InputList, N, OutputList) :-
    length(InputList, Length),
    State = state(cont),
    between(1, Length, N),
    (   State = state(cont)
    ->  true
    ;   !,
        fail
    ),
    (   do_something(InputList, N, OutputList)
    *-> nb_setarg(1, State, stop)
    ;   fail
    ).
这是完全不可移植的,尽管许多系统具有*->(有时命名为if/3),并且许多系统具有某种形式的不可回溯的分配,而如果您不顾一切,则可以使用assert/retract

在线查看


保罗的答案当然更便于携带。但是,这应该快得多,并且在返回第一个解决方案之前不会评估所有的
do\u something
,也不会评估两次do\u something。

事实证明,
在/3
之间会分散注意力。我们不需要它,因此一个简单、高效、可移植的解决方案是可能的:

main(InputList, N, OutputList) :-
    length(InputList, Length),
    Length >= 1,
    main(1, Length, InputList, OutputList).

main(N, Length, InputList, OutputList) :-
    (   do_something(InputList, N, OutputList) *->
        true
    ;   N < Length,
        M is N + 1,
        main(M, Length, InputList, OutputList)
    ).
main(输入列表,N,输出列表):-
长度(输入列表,长度),
长度>=1,
main(1,长度,输入列表,输出列表)。
main(N,长度,输入列表,输出列表):-
(做某事(输入列表,N,输出列表)*->
真的
;N<长度,
M是N+1,
主(M、长度、输入列表、输出列表)
).
与Jan的解决方案一样,它不会在返回第一个解决方案之前评估
do_something/3
的所有解决方案,也不会评估谓词两次。但它也不需要讨厌的、不可移植的
nb_setarg/2
谓词技巧

请注意,正如Jan所说,软切割控制结构,
*->/2
,或其
if/3
元谓词变体,可以在几个Prolog系统中找到(以一种或另一种形式,包括CxProlog、Ciao Prolog、ECLiPSe、GNU Prolog、JIProlog、SICStus Prolog、SWI Prolog和YAP)


另外,我保留了我的第一个答案,因为它更便于携带,并且举例说明了一种可能对其他问题有用的模式。

@Willenss,是的,在
G(N)
之前和之后,所有G(N)选择点都用尽了。换句话说,需要所有具有适当
N
的选择点,但不需要任何其他选择点。还有,我看不出其他人能帮上什么忙。你是说在G1之前切一个伤口?--我认为贴出的答案给出了一个解决方案,特别是如果
dou something(…)
是最后一个目标(否则,只需将它们全部放入
findall
调用中,比如
findall(1,(dou something(…),…,Solutions),…
),这将在到达
G(N)
之前,在/3选择点之间削减所有
,是吗?没关系,贴出的答案似乎很好。(好奇的人,这里有我的一些评论,其中有一些关于
*->
的想法,但后来保罗贴出了,所以我把它们删除了。然后简贴出了)是的,
findall/3
似乎完全按照需要工作,只需对你的示例进行一些小的修改
OutputList
必须迭代所有解决方案:`findall(Result,do_something(InputList,N,Result),solutions),`
solutions=[[uuuu124;.],
`!,`<代码>成员(输出列表,解决方案)。
不确定如何正确使用SO标记,使其显示为多行代码。这里有四行代码。一个问题