理解Swi-prolog中的分裂

Nol*_*ter 3 list prolog

我有这个代码将输入列表分成两半.好像没问题.

halve(List,A,B) :- halve(List,List,A,B), !.
halve(B,[],[],B).
halve(B,[_],[],B).
halve([H|T],[_,_|T2],[H|A],B) :-halve(T,T2,A,B). 
Run Code Online (Sandbox Code Playgroud)

好的,所以我试着解码它.一开始很清楚:

"Halve采取列表和2个逻辑变量"是这样的:

halve(List,A,B) 
Run Code Online (Sandbox Code Playgroud)

(1)然后连续这部分:

:- halve(List,List,A,B).
Run Code Online (Sandbox Code Playgroud)

这意味着,我正在从第一个创建新的两个列表(列表,列表)或者什么?什么exacly代表": - "?我猜新的列表=一半是A,而B,对吧?

(2)第二,拜托,我不太了解这两行:

halve(B,[],[],B).
halve(B,[_],[],B).
Run Code Online (Sandbox Code Playgroud)

也许你可以在一些例子中解释一下,好吗?

(3)嗯,我希望在您对(1)和(2)的解释之后,我将自己得到最后的部分......

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B). 
Run Code Online (Sandbox Code Playgroud)

非常非常感谢你帮助我.


好的,我们的第一个问题已经解决了.长话短说,它的工作原理如下:

halve([1,2,3,4,5],[1,2],[3,4,5]). 
->true
Run Code Online (Sandbox Code Playgroud)

如果你注意到它将列表分成两半,但如果列表中有奇数个元素,则后半部分是较大的元素.

现在我想要获得的是让第一个更大.

所以我在想这个:

我要达到这个目的:

Halves_div([1,2,3],A,B).
A=[1,2],
B=[3].
Run Code Online (Sandbox Code Playgroud)

假设我的输入是列表:[1,2,3].所以我将从分割列表的头部和尾部开始:[H|T]然后我将合并H新的空列表 - 我的上半部分(A).之后我有A = [1],B = []和输入= [2,3].

合并我有:

merge([],List,List).
merge([H|T],List,[H|New]) :- merge(T,List,New).
Run Code Online (Sandbox Code Playgroud)

还有一件事 - 我需要检查上半场是否已经> =下半场,对吧?

所以这是我的想法,我唯一能帮助你的是将它写在prolog中.我有点困惑如何把它放在一起.

谢谢!


看来我对解决方案的想法太复杂了,我找到了更好的东西!

m09*_*m09 9

首先,Prolog子句看起来像这样:

Head :- Body
Run Code Online (Sandbox Code Playgroud)

您可以将其视为" Headif Body"或" Body隐含Head".

请注意,有时你只是

Head
Run Code Online (Sandbox Code Playgroud)

那是因为Head总是如此true.Head在这种情况下,我们宁愿称之为事实,而不是调用一个子句.

所以在这里,我们有:

halve(List,A,B) :- halve(List,List,A,B).
Run Code Online (Sandbox Code Playgroud)

这意味着halve(List, A, B)如果halve(List, List, A, B)是真的那就是真的.具体地说,它只是一种委托工作的halve/3方式halve/4,即所谓的工人谓词.

为什么我们需要一个工人谓词?好吧,因为在这里我们想使用另一个变量来计算我们AB条款.但是我们不能这样做,halve/3因为halve/3输入列表已经采用了3个参数点,List结果的前半部分和结果A的后半部分,B.

关于这List, List件事,它只是一种说法,我们halve/4用相同的第一和第二个参数调用,就像你在任何编程语言中一样.

然后有趣的东西开始了.Prolog将尝试证明halve/4某些给定论点是正确的.让我们说来说明我们这样称呼的执行halve/3:

?- halve([1, 2], A, B).
Run Code Online (Sandbox Code Playgroud)

然后,如果您按照我之前谈到的内容,Prolog现在将尝试halve/3通过halve/4以下参数证明这是真的来证明这是真的:halve([1, 2], [1, 2], A, B)..

要做到这一点,Prolog有3个选择.第一个选择是以下条款:

halve(B,[],[],B).
Run Code Online (Sandbox Code Playgroud)

显然,那是行不通的.因为当Prolog将尝试通过统一来调整被调用者的第二个参数"in"被调用者的第二个参数时,它将失败.因为 [1, 2]无法统一[].

只剩下两个选择,下一个是:

halve(B,[_],[],B).
Run Code Online (Sandbox Code Playgroud)

同样的事情,Prolog无法统一[1, 2],[_]因为_它只是一个变量(如果你遇到麻烦,请参阅关于匿名变量的帖子_).

因此,Prolog必须找到解决您提出的问题的唯一机会是最后一个条款,即:

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).
Run Code Online (Sandbox Code Playgroud)

在这里,Prolog会找到一种统一的方法,让我们看看哪种方式:

  • 我们必须统一[1, 2]使用[H|T].这意味着H = 1.T = [2].
  • 我们必须统一[1, 2]使用[_,_|T2].这意味着T2 = [].
  • 现在我们开始用下一个统一来构建我们的结果,即A = [H|A'](我引用第二个A因为变量是本地范围的,它们不一样).在这里,我们告诉我们,当我们从子句的主体计算出我们的结果时,我们将添加H它.这里H1这样,我们已经知道的第一个元素A1.

好吧,统一成功了,太棒了!我们可以进入该条款的正文.它只halve/4是以递归方式调用这些值(上面计算):

halve([2], [], A, B).
Run Code Online (Sandbox Code Playgroud)

在这里,我们重新开始.虽然这次事情会很快,因为Prolog的第一选择将是一个很好的选择:

halve(B,[],[],B).
Run Code Online (Sandbox Code Playgroud)

可以统一到

halve([2], [], A, B).
Run Code Online (Sandbox Code Playgroud)

与这些价值观:A = []B = [2].

这是一个很好的步骤,我们现在达到了递归的"基本情况".我们现在必须从下到上构建我们的结果.还记得当我们递归调用我们的谓词halve/4几步之后吗?我们已经说的第一个元素A1.现在我们知道尾巴是[]如此,我们可以说明A = [1].我们没有说明任何特别的事情B因此B = [2]未被触及的结果.

现在我详细说明了执行情况,您可能想知道,为什么这样做有效?好吧,如果你注意,你会注意到第二个参数的halve/4速度是第一个参数的两倍.[H|T]VS [_, _|T2].这意味着当我们使用第二个参数到达列表的末尾时,第一个参数仍位于列表的中间位置.这样我们可以将事物分成两部分.

我希望我帮助你在这里找到一些微妙的工作.