【问题标题】:Understanding the splitting in Swi-prolog了解 Swi-prolog 中的拆分
【发布时间】:2014-01-23 09:30:41
【问题描述】:

我有这段代码用于将输入列表分成两半。好像没问题。

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). 

好的,所以我尝试对其进行解码。开头很清楚:

“将列表和 2 个逻辑变量减半”是这样的:

halve(List,A,B) 

(1) 接续这部分:

:- halve(List,List,A,B).

这意味着,我正在从第一个列表创建新的两个列表(列表、列表)还是什么?什么exacly 代表“:-”?我猜新的列表 = 一半将是 A 和 B,对吧?

(2) 第二,拜托,这两行我不太明白:

halve(B,[],[],B).
halve(B,[_],[],B).

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

(3) 嗯,希望经过你对(1)和(2)的解释,我能自己搞定最后一部分……

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B). 

非常非常感谢你帮助我。


好的,我们的第一个问题已经有了解决方案。长话短说,它的工作原理是这样的:

halve([1,2,3,4,5],[1,2],[3,4,5]). 
->true

如果您注意到它会将列表分成两半,但如果列表的元素数为奇数,则后半部分是较大的部分。

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

所以我在想这个:

我要达到这个目标:

Halves_div([1,2,3],A,B).
A=[1,2],
B=[3].

假设我的输入是列表:[1,2,3]。因此,我将从拆分列表的头部和尾部开始:[H|T],然后将H 与新的空列表合并——我的第一半(A)。 之后我有 A=[1]、B=[] 和 Input=[2,3]。

对于合并我有:

merge([],List,List).
merge([H|T],List,[H|New]) :- merge(T,List,New).

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

所以这是我的想法,我希望你能帮助我的唯一一件事就是把它写在 prolog 中。我有点困惑如何把它放在一起。

谢谢!


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

【问题讨论】:

  • 你不知道“:-”代表什么?你有没有在 prolog 中编写过任何程序?
  • 好吧,好吧...“could_be_wrong(X) :-human(X)。”该子句可以理解为“如果 X(我)是人类,X(我)可能会出错”。

标签: list prolog


【解决方案1】:

首先,Prolog 子句如下所示:

Head :- Body

您可以将其读作“Head if Body”,或“Body 暗示 Head”。

请注意,有时您只是有

Head

这是因为 Head 始终是 true。在这种情况下,我们宁愿将其称为事实,而不是将 Head 称为子句。

所以在这里,我们有:

halve(List,A,B) :- halve(List,List,A,B).

这意味着如果halve(List, List, A, B) 为真,则halve(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).

然后,如果您按照我之前所说的进行操作,Prolog 现在将尝试通过以下参数证明 halve/4 为真来证明 halve/3 为真:halve([1, 2], [1, 2], A, B).

为此,Prolog 有 3 个选择。第一个选择是以下子句:

halve(B,[],[],B).

显然,这行不通。因为当 Prolog 会尝试通过统一将调用者的第二个参数“放入”被调用者的第二个参数时,它会失败。因为 [1, 2]不能和[]统一。

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

halve(B,[_],[],B).

同样的事情,Prolog 无法统一 [1, 2][_],因为 _ 只是一个变量(如果您遇到问题,请参阅 my post 关于匿名变量 _)。

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

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).

这里,Prolog会想办法统一事物,让我们看看是哪一种方式:

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

好的好的,统一成功了,太好了!我们可以进入条款的主体。它只是使用这些值(如上计算)以递归方式调用halve/4

halve([2], [], A, B).

在这里,我们重新开始。虽然这次事情会很快,因为 Prolog 的第一选择很合适:

halve(B,[],[],B).

可以统一到

halve([2], [], A, B).

使用这些值:A = []B = [2]

这是一个很好的步骤,我们现在达到了递归的“基本情况”。我们现在只需要从下到上构建我们的结果。还记得我们在上面几步递归调用谓词halve/4 的时候吗?我们已经说过A 的第一个元素是1。现在我们知道尾部是[],所以我们可以声明A = [1]。我们没有对B 进行任何特别说明,因此B = [2] 未受影响。

既然我详细介绍了执行,您可能想知道,为什么会这样?好吧,如果你注意的话,你会注意到halve/4 的第二个参数的通过速度是第一个参数的两倍。 [H|T][_, _|T2]。这意味着当我们用第二个参数到达列表末尾时,第一个参数仍然在我们列表的中间。这样我们就可以把东西分成两部分了。

我希望我能帮助您了解这里工作中的一些微妙之处。

【讨论】:

  • 很好,谢谢!我要多读几遍,理解并尝试新的东西。 :-)
  • 精彩而干净的解释 - 做一些序言已经 16 年了
  • @Mog 嗨,我只是想知道如何获得这样的结果:A=[1,2], B=[3]. 当输入为:[1,2,3]。现在我得到了正确的结果,但结果相反(A=[1] 和 B=[2,3])。换句话说:减半([1,2,3,4,5],[1,2,3],[4,5])。必须是“真实的”。
  • 首先我认为这与 halve(B,[],[],B).halve(B,[_],[],B). 有关,但事实并非如此,或者我显然无法修改它。
  • @Mog 哦不,我尝试了将近 2 个小时来重新制作它。我必须这样做。你能帮忙吗?如果你愿意 - 电子邮件在我的个人资料中。
【解决方案2】:

halve(List,A,B)List 的前半部分复制到A 并与B 统一后半部分

当我们的列表长度为偶数时,这将是真的:halve(B,[],[],B).

当输出列表的长度为奇数时,这是正确的:halve(B,[_],[],B).

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).

在这里我们设置 2,让我们在每一步中将它们称为“指针”,我们将一个元素从列表的开头复制到 A,因为我们想要获得前半部分。
因为在每一步中,我们从列表中删除 2 个元素 [_,_|T2] 当列表只有一个左元素或为空时,谓词将停止,然后它将与列表的其余部分统一为 B。如果看不懂就用trace/0

【讨论】:

    【解决方案3】:

    这个版本可能很有用...

    split_in_half(Xs, Ys, Zs) :-  length(Xs, Len),
    Half is Len // 2,    % // denotes integer division, rounding down
    split_at(Xs, Half, Ys, Zs).
    
    split_at(Xs, N, Ys, Zs) :- length(Ys, N), append(Ys, Zs, Xs).
    

    【讨论】:

    • 不知何故,正是这个终于让我明白了,我意识到length+append => sub-list-of-length-n。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 1970-01-01
    • 2011-04-26
    • 2016-09-05
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    相关资源
    最近更新 更多