【问题标题】:Pattern matching problem with PrologProlog 的模式匹配问题
【发布时间】:2011-04-12 05:04:28
【问题描述】:

编辑:请参阅下面的答案,了解为什么我是个傻瓜。不过,这里面还有一个谜,我很想回答。

我已经被困在这个问题上太久了。我正在尝试在一个漂亮的网格中打印出一个数独解决方案。

我认为我遇到了问题,因为我不了解模式匹配在 Prolog 中如何工作的一些关键部分。

事不宜迟,我的代码:

prettier_print([]).
prettier_print([Puzzle]) :- prettier_print(0, [Puzzle]).

prettier_print(0, Puzzle) :- 
    writeln('┌───────┬───────┬───────┐'), 
    prettier_print(1, Puzzle).
prettier_print(4, Puzzle) :- 
    writeln('│───────┼───────┼───────│'), 
    prettier_print(5, Puzzle).
prettier_print(8, Puzzle) :- 
    writeln('│───────┼───────┼───────│'), 
    prettier_print(9, Puzzle).
prettier_print(12, []) :- 
    writeln('└───────┴───────┴───────┘').

prettier_print(N, [Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9 | Puzzle]) :- 
    member(N, [1,2,3,5,6,7,9,10,11]),  % tried this when the line below did not work
    % N =\= 0, N =\= 4, N =\= 8, N =\= 13,

    format('│ ~d ~d ~d │ ~d ~d ~d │ ~d ~d ~d │~n', [Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9]), 
    succ(N, N1),
    prettier_print(N1, Puzzle).

来电:

prettier_print(0, [1,2,3,4,5,6,7,8,9, 
                   2,2,3,4,5,6,7,8,9, 
                   3,2,3,4,5,6,7,8,9, 
                   4,2,3,4,5,6,7,8,9, 
                   5,2,3,4,5,6,7,8,9, 
                   6,2,3,4,5,6,7,8,9,
                   7,2,3,4,5,6,7,8,9,
                   8,2,3,4,5,6,7,8,9,
                   9,2,3,4,5,6,7,8,9]).

这是输出:

┌───────┬───────┬───────┐
│ 1 2 3 │ 4 5 6 │ 7 8 9 │
│ 2 2 3 │ 4 5 6 │ 7 8 9 │
│ 3 2 3 │ 4 5 6 │ 7 8 9 │
│───────┼───────┼───────│
│ 4 2 3 │ 4 5 6 │ 7 8 9 │
│ 5 2 3 │ 4 5 6 │ 7 8 9 │
│ 6 2 3 │ 4 5 6 │ 7 8 9 │
│───────┼───────┼───────│
│ 7 2 3 │ 4 5 6 │ 7 8 9 │
│ 8 2 3 │ 4 5 6 │ 7 8 9 │
│ 9 2 3 │ 4 5 6 │ 7 8 9 │
└───────┴───────┴───────┘
true ;
false.

问题是它不只是返回真,我必须按;,然后它返回假。这反过来意味着我的prettier_print/1 规则无法正常工作。

我想我知道这意味着:

Prolog 正在回溯并尝试对我的规则进行另一种解释(如果我正确理解我的条款,看看是否还有其他可以统一的东西)。这找到了另一件事与之统一,但这立即失败了。那正确吗?

我希望只有一种可能的解释。我怎样才能修复我的功能?

谢谢你的帮助,这让我感觉自己像个傻瓜!

【问题讨论】:

    标签: prolog pattern-matching backtracking


    【解决方案1】:

    要查看此内容的一种方式,请尝试使用 SWI-Prolog 的图形跟踪器:

    ?- gtrace, your_goal.
    

    跟踪器将向您显示创建选择点的位置,从而引入非确定性。在回溯时(例如,当您在顶层按 SPACE 或“;”时),将考虑剩余的替代子句。

    【讨论】:

      【解决方案2】:

      它正在“返回”true,然后是 false,因为您正在用 ; 强制它回溯。由于只有一种解决方案,因此回溯失败。

      这种行为只发生在交互式 Prolog 解释器中;如果您要编译程序或在非交互式解释器中运行它,它只会打印结果而不“返回”任何内容。这有点类似于 Python 解释器的行为

      >>> 'foo'
      'foo'
      

      回显输入的最后一个表达式的值,但仅限于交互模式。除非您输入 print 语句,否则脚本不会打印任何内容。

      如果您不想看到 false 消息,那么要么不要回溯,即按 Enter 而不是 ;,或者使用元谓词 once/1

      ?- member(X,[1,2,3]).
      X = 1 ;
      X = 2 .    % ENTER pressed here
      
      ?- once(member(X,[1,2,3])).
      X = 1.
      
      ?- 
      

      但不要只在程序的任何地方这样做,因为它会改变程序的语义。

      【讨论】:

      • 您似乎至少误读了我的部分问题。为了澄清这一点,我知道 ; 做了什么,并且我知道 REPL 通常的行为方式。我使用的许多函数(或规则?)(以及我编写的一些函数)即使在交互式解释器中也只是简单地打印出单个正确答案。我写的这个没有这样做,我不知道为什么。那是我困惑的核心。一开始,我也错误地认为这是 prettier_print/1 规则不起作用的原因,但设法清除了这一点。谢谢你的回答,我很感激任何帮助。
      【解决方案3】:

      它在回溯时返回 true 然后返回 false 的原因是您在子句中留下了选择点。您的应用程序流程只会执行 prettier_print/2 的一个子句,但 prolog 解释器事先并不知道,所以它留下一个选择点,然后在回溯时,将尝试查看 prettier_print/2 的任何剩余子句是否会成功。

      您可以“剪切”回溯,通过使用剪切 (!) 来承诺一个选择:

      prettier_print([]).
      prettier_print([Puzzle]) :- prettier_print(0, [Puzzle]).
      
      prettier_print(0, Puzzle) :-
          writeln('┌───────┬───────┬───────┐'),
          !,
          prettier_print(1, Puzzle).
      prettier_print(4, Puzzle) :-
          writeln('│───────┼───────┼───────│'),
          !,
          prettier_print(5, Puzzle).
      prettier_print(8, Puzzle) :-
          writeln('│───────┼───────┼───────│'),
          !,
          prettier_print(9, Puzzle).
      prettier_print(12, []) :-
          writeln('└───────┴───────┴───────┘'),
          !.
      prettier_print(N, [Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9 | Puzzle]) :-
          member(N, [1,2,3,5,6,7,9,10,11]),  % tried this when the line below did not work
      
          format('│ ~d ~d ~d │ ~d ~d ~d │ ~d ~d ~d │~n', [Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9]),
          succ(N, N1),
          prettier_print(N1, Puzzle),
          !.
      

      您也可以重写您的解决方案,以便通过使用其他谓词而不是 prettier_print 的不同子句来不留下选择点。这样你就不需要使用剪切了:

      prettier_print(Puzzle) :-
        Puzzle \= [] ->
        (
          print_head,
          print_rows(Puzzle, NRows1),
          print_line,
          print_rows(Puzzle, NRows1),
          print_line,
          print_rows(Puzzle, NRows1),
          print_end
        ).
      
      print_rows(Rows, NRows):-
        print_row(Rows, Rows1),
        print_row(Rows1, Rows2),
        print_row(Rows2, NRows).
      
      print_head:-
          writeln('┌───────┬───────┬───────┐').
      
      print_line:-
          writeln('│───────┼───────┼───────│').
      
      print_end:-
          writeln('└───────┴───────┴───────┘').
      
      print_row([Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9 | NRows], NRows) :-
          format('│ ~d ~d ~d │ ~d ~d ~d │ ~d ~d ~d │~n', [Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9]).
      

      【讨论】:

      • 很好的解释,这正是我所需要的,谢谢!
      【解决方案4】:

      哎呀,没关系,我是个白痴!

      我首先指出了一些不存在问题的东西。将prettier_print/1 更改为:

      prettier_print([]).
      prettier_print(Puzzle) :- prettier_print(0, Puzzle).
      

      让它正常工作。

      我仍然想了解为什么它会返回 true,然后返回 false。如果有人可以回答,我会将他们的回答标记为已接受。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-30
        • 2011-02-03
        • 1970-01-01
        • 2011-06-17
        • 2020-07-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多