更容易获得“独特”绑定?

时间:2018-05-18 18:28:30

标签: prolog

对不起我的术语是关闭的。长期(40年?......)势在必行的程序员,涉及功能,今天早上花了一些时间试图对声明性更加认真。通过学习网站,决定在练习2.4中尝试“填字游戏”:http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse7

我已经得到了它,但感觉可笑得很笨拙。这是我的新手解决方案:

word(astante,  a,s,t,a,n,t,e).
word(astoria,  a,s,t,o,r,i,a).
word(baratto,  b,a,r,a,t,t,o).
word(cobalto,  c,o,b,a,l,t,o).
word(pistola,  p,i,s,t,o,l,a).
word(statale,  s,t,a,t,a,l,e).

crossword(V1,V2,V3,H1,H2,H3):-
    word(V1,V1a,V1b,V1c,V1d,V1e,V1f,V1g),
    word(V2,V2a,V2b,V2c,V2d,V2e,V2f,V2g),
    word(V3,V3a,V3b,V3c,V3d,V3e,V3f,V3g),
    word(H1,H1a,H1b,H1c,H1d,H1e,H1f,H1g),
    word(H2,H2a,H2b,H2c,H2d,H2e,H2f,H2g),
    word(H3,H3a,H3b,H3c,H3d,H3e,H3f,H3g),
    V1b = H1b,
    V1d = H2b,
    V1f = H3b,
    V2b = H1d,
    V2d = H2d,
    V2f = H3d,
    V3b = H1f,
    V3d = H2f,
    V3f = H3f,
    not(V1 = V2),
    not(V1 = V3),
    not(V1 = H1),
    not(V1 = H2),
    not(V1 = H3),
    not(V2 = V3),
    not(V2 = H1),
    not(V2 = H2),
    not(V2 = H3),
    not(V3 = H1),
    not(V3 = H2),
    not(V3 = H3),
    not(H1 = H2),
    not(H1 = H3),
    not(H2 = H3).

有效。填字游戏会给我两种可能的布局(毕竟拼图是对称的)。但是,是的......

有些笨拙只是因为我刚刚开始,所以我对如何将绑定标记为“不关心”(单词的第1,第3,第5和第7个字母)没有任何感觉例如,完全无关紧要。但是现在真正令我烦恼的是必须在最后放入“不要复制任何绑定”的三角矩阵。这个问题不断出现(早期玩具问题涉及爱(A,B)和嫉妒(X,Y),如果你允许X = Y,那么所有引发爱情关系的人都声称自己会嫉妒(我看到有人)其他人几年前还在争吵:Get unique results with Prolog)),但本教程没有解决这个问题。它甚至还没有告诉我“不” - 我不得不在其他地方挖掘它以获得它,这导致完全有效的问题“好吧,你想要什么样的'不相等'?”,我是目前尚未准备好回答。但我离题了......

对我而言,这是实际代码中实际完成的方式,这是不可思议的。结合起来,这太荒谬了。它似乎违反(或至少弯曲)最不惊讶的原则。没有唯一性限制,有大量的解决方案只设置V1 = H1,V2 = H2,V3 = H3。我想我只能禁止那些,但真正的解决方案需要完全限制。

是的,我完全理解逻辑上和数学上没有理由不将相同的值绑定到多个参数中,并且还有很多情况下这样的多重绑定不仅有用,而且需要处理手头的问题。我根本没有与默认行为争论,只是在我需要时寻找表达唯一性约束的更好方法。

我很想知道更好的方法来解决这个问题,因为老实说我不认为我可以深入研究这些教程,如果他们都需要这么多毛茸茸来做一些看似显而易见的事情。

提前致谢!

2 个答案:

答案 0 :(得分:3)

在我看来,这项练习让你失败

首先,因为它使用不适合的知识表示以统一的方式处理类似的元素。

其次,因为你在本书的这一点上没有必要的先决条件,即使知识表示会使它更容易。

我可以向您保证:在Prolog中进行编程时,您所做的当然是而不是

所以,不要让一本书贬低你的语言。

此时,如果您有更多使用该语言的经验并使用更合适的语言功能,我想向您展示如何解决此任务。当您阅读其他材料时,您可能会喜欢回到此处。

我想做的关键改变是让你更明确地推断可用的单词,作为你的程序中的数据结构,而不是“仅”作为事实:

words(Ws) :-
        Ws = [[a,s,t,a,n,t,e],
              [a,s,t,o,r,i,a],
              [b,a,r,a,t,t,o],
              [c,o,b,a,l,t,o],
              [p,i,s,t,o,l,a],
              [s,t,a,t,a,l,e]].

当然,您可以使用 all-solutions 谓词(例如findall/3)自动轻松获取此类显式(有时称为空间)表示。

我现在介绍的关键谓词让我们将此表示中的单词与其每秒字母相关联:

word_evens([_,A,_,B,_,C,_], [A,B,C]).

请注意,如果您可以明确说明构成单词的字母列表,那么表达这种关系是多么容易。

现在,整个解决方案,使用谓词permutation/2transpose/2,您可以在Prolog系统中找到它们作为库谓词,也可以自己轻松实现:

solution(Ls) :-
        Ls = [V1,V2,V3,H1,H2,H3],
        words(Ws),
        Ws = [First|_],
        maplist(same_length(First), Ls),
        maplist(word_evens, [H1,H2,H3], Ess),
        transpose(Ess, TEss),
        maplist(word_evens, [V1,V2,V3], TEss),
        permutation(Ws, Ls).

示例查询和两个解决方案:

?- solution(Ls).
Ls = [[a, s, t, a, n, t, e], [c, o, b, a, l, t, o], [p, i, s, t, o, l|...], [a, s, t, o, r|...], [b, a, r, a|...], [s, t, a|...]] ;
Ls = [[a, s, t, o, r, i, a], [b, a, r, a, t, t, o], [s, t, a, t, a, l|...], [a, s, t, a, n|...], [c, o, b, a|...], [p, i, s|...]] ;
false.

Prolog中至少应该有maplist/2,如果您的系统没有提供,same_length/2很容易定义。

如果您真的想表达不一致的术语,请使用

答案 1 :(得分:2)

  

但是现在真正令我烦恼的是必须加入三角形矩阵,不要复制任何绑定"最后。

我们可以使用此处的all_dif/1谓词获得更好的解决方案:https://stackoverflow.com/a/47294595/4391743

all_dif([]).
all_dif([E|Es]) :-
   maplist(dif(E), Es),
   all_dif(Es).

此谓词接受彼此不同的元素列表。不要担心还没有理解它,你现在可以把它当作一个黑盒子。

您可以使用它来替换谓词定义末尾的not目标的大块:

crossword(V1,V2,V3,H1,H2,H3):-
    ... % unchanged code here
    all_dif([V1, V2, V3, H1, H2, H3]).

通过这种方式,您只能回到使用所有单词的两个解决方案。

  

我没有任何关于如何标记绑定的感觉,因为他们并不关心" (例如,单词的第1,第3,第5和第7个字母完全不相关)。

这确实是一个非常重要的问题! Prolog应该警告你一次只使用一次的变量(称为#34;单身"),因为它们是错误的常见来源:

Singleton variables: [V1a,V1c,V1e,V1g,V2a,V2c,V2e,V2g,V3a,V3c,V3e,V3g,H1a,H1c,H1e,H1g,H2a,H2c,H2e,H2g,H3a,H3c,H3e,H3g]

您明确将变量标记为"不关心"给它一个以下划线字符_开头的名称,或者只是命名为__的不同出现标志着不同的关注"不关心"变量。所以我们得到:

crossword(V1,V2,V3,H1,H2,H3):-
    word(V1,_,V1b,_,V1d,_,V1f,_),
    word(V2,_,V2b,_,V2d,_,V2f,_),
    word(V3,_,V3b,_,V3d,_,V3f,_),
    word(H1,_,H1b,_,H1d,_,H1f,_),
    word(H2,_,H2b,_,H2d,_,H2f,_),
    word(H3,_,H3b,_,H3d,_,H3f,_),
    V1b = H1b,
    V1d = H2b,
    V1f = H3b,
    V2b = H1d,
    V2d = H2d,
    V2f = H3d,
    V3b = H1f,
    V3d = H2f,
    V3f = H3f,
    all_dif([V1, V2, V3, H1, H2, H3]).

警告已消失我们程序更容易阅读,因为下划线会留下"空洞"在无趣的地方,我们更清楚地看到哪些变量很重要。

这使我们完成了删除所有这些方程式的任务。一般Prolog提示:除非有时为了清楚起见,否则永远不需要编写Var1 = Var2形式的方程式,其中双方都是变量。只需对整个子句中的两个变量使用相同的名称,就可以获得相同的结果!

因此,让V1bH1b替换为名为AV1dH2b的同一变量{{1}等等:

B

这相当于您的初始解决方案,我希望,这对初学者友好。

希望这可以让您了解Prolog程序可能比您的第一次尝试少一些笨重。请坚持下去,如果你遇到困难,我们会在这里提供帮助,希望你会有一些不那么令人沮丧的经历,看看Prolog的魔力。