【问题标题】:Add two more occurrences using prolog使用 prolog 再添加两次
【发布时间】:2013-12-21 20:56:06
【问题描述】:

我有一个列表[a, b, a, a, a, c, c] 我需要在每个元素中再添加两次。

最终结果应该是这样的:

[a, a, a, b, b, b, a, a, a, a, a, c, c, c, c]

如果我在列表中有一个项目与下一个项目相同,那么它会一直运行直到有一个新项目,当它找到新项目时,它会添加上一个项目的两次出现然后继续前进。

这是我目前的代码,但我不知道如何添加两个...

dbl([], []).
dbl([X], [X,X]).
dbl([H|T], [H,H|T], [H,H|R]) :- dbl(T, R).

【问题讨论】:

  • 好的,嗯,这个列表应该是一个普通的序言列表,这只是问题的语法,说明如果给定一个列表,(...)添加两个事件以使其成为喜欢 (...) 使用序言。这并不意味着按照确切的语法逐字列出列表。
  • 你的第一条规则看起来不错(双空列表是一个空列表)。您的第二条规则并没有真正翻倍。它只在列表末尾复制一次X。您的第三条规则有三个参数,其语义不清楚。这是作业吗?
  • 这是一个迷你期末项目,等一下,我想我想通了
  • 酷。把你发现的东西写下来。如果您遇到困难,请使用最新代码更新(编辑)您的问题,我们可以探索一些线索。 :)
  • 没问题。虽然检查它。如果你这样做了,例如dbl([a,a],L).,你会得到两个解决方案:正确的一个 (L = [a,a,a,a]) 和一个不正确的 (L = [a,a,a,a,a,a])。

标签: list prolog prolog-dif


【解决方案1】:

您的代码看起来有点奇怪,因为最后一条规则需要三个参数。你只调用二进制版本,所以没有递归会尝试派生它。

您已经有了一个好主意,可以查看列表中元素发生变化的部分。所以有4种情况:

1) 您的列表为空。 2)你只有一个元素。 3)您的列表以两个相等的元素开始。 4) 您的列表以两个不同的元素开始。

没有指定案例 1,因此您可能需要为此找到一个明智的选择。案例 2 与案例 4 有点相似,因为列表的末尾可以看作是元素的变化,您需要附加两个副本,然后就完成了。案例 3 非常简单,我们可以只保留元素并递归其余元素。情况 4 是您需要再次插入两个副本的地方。

这意味着您的代码将如下所示:

% Case 1
dbl([],[]).
% Case 2
dbl([X],[X,X,X]).
% Case 3
dbl([X,X|Xs], [X|Ys]) :-
   % [...] recursion skipping the leading X
% Case 4
dbl([X,Y|Xs], [X,X,X|Ys]) :-
   dif(X,Y),
   % [...] we inserted the copies, so recursion on [Y|Xs] and Ys

案例 3 应该很容易完成,我们只需从两个列表中删除第一个 X 并递归 dbl([X|Xs],Ys)。请注意,我们通过两次写入相同的变量来隐式地使前两个元素相等(即我们统一它们)。

如果你看案例4的头部,你可以直接模仿你描述的模式:假设列表以X开头,然后是Y并且它们不同(dif(X,Y)),X重复3次而不是只是复制,然后我们继续递归其余部分,从 Y 开始:dbl([Y|Xs],Ys)。

所以让我们试试谓词:

?- dbl([a,b,a,a,a,c,c],[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]).
true ;
false.

我们的测试用例被接受(真),我们没有找到多个解决方案(假)。 让我们看看我们是否找到了错误的解决方案:

?- dif(Xs,[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]), dbl([a,b,a,a,a,c,c],Xs).
false.

不,这也很好。如果我们的列表中有变量会发生什么?

?- dbl([a,X,a],Ys).
X = a,
Ys = [a, a, a, a, a] ;
Ys = [a, a, a, X, X, X, a, a, a],
dif(X, a),
dif(X, a) ;
false.

要么 X = a,则 Ys 是 5 as 的单次运行;或者 X 不等于 a,那么我们需要在所有三个运行中附加副本。看起来也不错。 (*)

现在让我们看看,如果我们只指定解决方案会发生什么:

?- dbl(X,[a,a,a,b,b]).
false.

是的,只有两个 bs 的列表不可能是我们规范的结果。所以让我们尝试添加一个:

?- dbl(X,[a,a,a,b,b,b]).
X = [a, b] ;
false.

万岁,成功了!所以让我们作为最后一个测试看看会发生什么,如果我们只用两个变量调用我们的谓词:

?- dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G15],
Ys = [_G15, _G15, _G15] ;
Xs = [_G15, _G15],
Ys = [_G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15, _G15] ;
[...]

似乎我们得到了正确的答案,但我们只看到单次运行的案例。这是prolog的搜索策略的结果(我不会在这里解释)。但是,如果我们在生成较长的列表之前先查看较短的列表,我们可以看到所有解决方案:

 ?- length(Xs,_), dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G16],
Ys = [_G16, _G16, _G16] ;
Xs = [_G16, _G16],
Ys = [_G16, _G16, _G16, _G16] ;
Xs = [_G86, _G89],
Ys = [_G86, _G86, _G86, _G89, _G89, _G89],
dif(_G86, _G89) ;
Xs = [_G16, _G16, _G16],
Ys = [_G16, _G16, _G16, _G16, _G16] ;
Xs = [_G188, _G188, _G194],
Ys = [_G188, _G188, _G188, _G188, _G194, _G194, _G194],
dif(_G188, _G194) ;
[...]

所以看起来我们有一个有效的谓词(**),假设你填补了文本中缺失的目标:)

(*) 这里需要说明一下:这种情况只适用于我们使用的是差异。第一个具有相等性的谓词,通常遇到的是 =、== 和它们各自的否定 \= 和 \==。 = 代表可统一性(将变量替换为参数 s.t. 它们变得相等),== 代表句法相等(术语完全相等)。例如:

?- f(X) = f(a).
X = a.

?- f(X) \= f(a).
false.

?- f(X) == f(a).
false.

?- f(X) \== f(a).
true.

这意味着,如果我们用 a 代替 X,我们可以使 f(X) 等于 f(a)。这意味着如果我们问它们是否不能相等(\=),我们会得到错误的答案。另一方面,这两个项不相等,所以 == 返回 false,而它的否定 \== 则返回 true。

这也意味着 X \== Y 总是正确的,所以我们不能在我们的代码中使用 \==。与此相反,dif 等待直到它可以决定它的参数是否相等。如果找到答案后仍未决定,则打印“dif(X,a)”语句。

(**) 最后一句话:还有一个带有 if-then-else 结构的解决方案 (test ->goals_if_true;goals_if_false,它合并了案例 3​​ 和 4。由于我更喜欢​​这个解决方案,您可能需要自己看看另一个版本。

【讨论】:

    【解决方案2】:

    TL;DR: 从声明的角度来看, code sketched by @lambda.xy.x 是完美的。 它的确定性可以提高而不牺牲


    代码变体 #0:@lambda.xy.x 的代码

    这是我们要改进的代码:

    dbl0([],[])。 dbl0([X],[X,X,X])。 dbl0([X,X|Xs], [X|Ys]) :- dbl0([X|Xs], Ys)。 dbl0([X,Y|Xs], [X,X,X|Ys]) :- 差异(X,Y), dbl0([Y|Xs], Ys)。

    考虑以下查询和 SWI-Prolog 给我们的答案:

    ?- dbl0([a],Xs)。 Xs = [a,a,a] ; 错误

    使用 ; false SWI 表示在证明目标时留下了一个选择点。

    • 对于第一个答案,Prolog 没有搜索整个证明树。
    • 相反,它回答“这是一个答案,可能还有更多”。
    • 然后,当被要求提供更多解决方案时,Prolog 遍历了证明树的剩余分支,但没有找到更多答案。

    换句话说:Prolog 需要三思而后行,以证明我们一直都知道的事情!

    那么,我们如何向 Prolog 提供确定性提示? 通过利用:

    1. control constructs !/0 和/或(->)/2(可能不纯)

    2. first argument indexing principal functor从不不纯)

    earlier answer by @CapelliC 中提供的代码(基于!/0(->)/2 和元逻辑谓词 (\=)/2)运行良好如果所有参数都是充分实例化。否则,可能会导致答案不稳定——正如@lambda.xy.x 的评论所示。

    代码变体 #1:索引

    索引可以提高确定性,而永远不会呈现非单调的代码。虽然不同的 Prolog 处理器具有不同的高级索引功能,但“第一参数主函子”索引变体是广泛可用的。

    Principal? 就是为什么执行目标dbl0([a],Xs)会留下一个选择点:是的,目标只匹配一个子句—dbl0([X],[X,X,X]).—但看起来没有比主函子更深入 Prolog 假设最后三个子句中的任何一个可以最终被使用。当然,我们更了解...

    告诉 Prolog 我们使用了主函子第一参数索引:

    dbl1([],[])。 dbl1([E|Es], Xs) :- dbl1_(Es, Xs, E)。 dbl1_([], [E,E,E], E)。 dbl1_([E|Es], [E|Xs], E) :- dbl1_(Es, Xs, E)。 dbl1_([E|Es], [E0,E0,E0|Xs], E0) :- 差异(E0,E), dbl1_(Es, Xs, E)。

    更好?有点,但确定性可能会更好......

    代码变体 #2:在 reified term equality 上建立索引

    为了让 Prolog 看到 dbl1_/3 的两个递归子句是互斥的(在某些情况下),我们具体化了 术语相等,然后索引 那个值

    这就是 reified term equality (=)/3 发挥作用的地方:

    dbl2([],[])。 dbl2([E|Es], Xs) :- dbl2_(Es, Xs, E)。 dbl2_([], [E,E,E], E)。 dbl2_([E|Es], Xs, E0) :- =(E0, E, T), t_dbl2_(T, Xs, E0, E, Es)。 t_dbl2_(true, [E|Xs], _, E, Es) :- dbl2_(Es, Xs, E)。 t_dbl2_(false, [E0,E0,E0|Xs], E0, E, Es) :- dbl2_(Es, Xs, E)。

    使用 SWI-Prolog 的示例查询:

    ?- dbl0([a],Xs)。 Xs = [a, a, a] ; 错误的。 ?- dbl1([a],Xs)。 Xs = [a, a, a]。 ?- dbl2([a],Xs)。 Xs = [a, a, a]。 ?- dbl0([a,b,b],Xs)。 Xs = [a, a, a, b, b, b, b] ; 错误的。 ?- dbl1([a,b,b],Xs)。 Xs = [a, a, a, b, b, b, b] ; 错误的。 ?- dbl2([a,b,b],Xs)。 Xs = [a, a, a, b, b, b, b]。

    为了使上面的代码更紧凑,请使用控制构造 if_/3

    【讨论】:

      【解决方案3】:

      当我看到@repeat 已经建议它时,我正要扔掉这个带有if_/3(=)/3 的版本。所以这基本上是@repeat 概述的更紧凑的版本:

      list_dbl([],[]).
      list_dbl([X],[X,X,X]).
      list_dbl([A,B|Xs],DBL) :-
         if_(A=B,DBL=[A,B|Ys],DBL=[A,A,A,B|Ys]),
         list_dbl([B|Xs],[B|Ys]).
      

      它产生与@repeat 的 dbl2/2 相同的结果:

         ?- list_dbl([a],DBL).
      DBL = [a,a,a]
         ?- list_dbl([a,b,b],DBL).
      DBL = [a,a,a,b,b,b,b]
      

      OP 的示例查询按预期工作:

         ?- list_dbl([a,b,a,a,a,c,c],DBL).
      DBL = [a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]
      

      另外,这里是@lambda.xy.x 提供的一些示例查询。它们产生与@repeat 的 dbl/2 和 @lambda.xy.x 的 dbl/2 相同的结果:

         ?- dif(Xs,[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]), list_dbl([a,b,a,a,a,c,c],Xs).
      no
      
         ?- list_dbl(X,[a,a,a,b,b]).
      no
      
         ?- list_dbl(L,[a,a,a,b,b,b]).
      L = [a,b] ? ;
      no
      
         ?- list_dbl(L,DBL).
      DBL = L = [] ? ;
      DBL = [_A,_A,_A],
      L = [_A] ? ;
      DBL = [_A,_A,_A,_A],
      L = [_A,_A] ? ;
      DBL = [_A,_A,_A,_A,_A],
      L = [_A,_A,_A] ? ;
      ...
      
         ?- list_dbl([a,X,a],DBL).
      DBL = [a,a,a,a,a],
      X = a ? ;
      DBL = [a,a,a,X,X,X,a,a,a],
      dif(X,a),
      dif(a,X)
      
         ?- length(L,_), list_dbl(L,DBL).
      DBL = L = [] ? ;
      DBL = [_A,_A,_A],
      L = [_A] ? ;
      DBL = [_A,_A,_A,_A],
      L = [_A,_A] ? ;
      DBL = [_A,_A,_A,_B,_B,_B],
      L = [_A,_B],
      dif(_A,_B) ? ;
      DBL = [_A,_A,_A,_A,_A],
      L = [_A,_A,_A] ? 
      

      【讨论】:

      • 使用 SWI 顶层选择点很容易看到......使用 SICStus 并不容易。
      • 你在正确的轨道上!但目标dbl([a],_) 应该更好地确定性地成功。
      • @repeat:我的印象是:请参阅上面的查询?- list_dbl([a],DBL). 它会产生答案DBL = [a,a,a],没有任何选择点。查询?- list_dbl([a],_). 也是如此,不同之处在于答案只是yes,没有替换第二个参数。但同样没有选择点。还是我误读了您的评论?
      • @repeat: YAP 6.2.2 (x86_64-linux): Sun Nov 24 20:27:47 UTC 2013 相当老了,但据我所知它是最新的稳定版。我喜欢稳定的:-)
      • 哇! yap 索引 确实 确实可以处理这些情况!我站得更正了。有关更多信息,请查看stackoverflow.com/questions/29823117/… ...我已经尝试使用 yap、SICStus 和 SWI 使用?- call_cleanup(list_dbl([a],_), Det=true), Det==true.。使用 SICStus 和 SWI:失败。随着 yap:成功
      【解决方案4】:
      dbl([X,Y|T], [X,X,X|R]) :- X \= Y, !, dbl([Y|T], R).
      dbl([H|T], R) :-
              T = []
          ->  R = [H,H,H]
          ;   R = [H|Q], dbl(T, Q).
      

      第一个子句处理基本要求,在序列更改时添加两个元素。 第二个将列表终止处理为序列更改,否则进行普通副本。

      【讨论】:

      • 此版本不处理列表中的变量:?- dbl([A,B,C],Y)。 Y = [A、B、C、C、C]。最后一个应该只适用于 A=B 和 A=C,而不适用于一般的 A、B 和 C。
      • 狡猾(+1),但当术语没有足够实例化时就不健全。对于基于索引的纯代码,请查看我的答案...
      猜你喜欢
      • 1970-01-01
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 2018-06-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多