【问题标题】:Prolog: a list of characters <=> list of character listProlog:字符列表<=>字符列表列表
【发布时间】:2021-06-13 18:45:22
【问题描述】:

我正在实现一个words/2 谓词,其中可以将字符列表呈现为字符列表作为列表中的单词。我使用数学符号&lt;=&gt; 表示它们在任何模式下工作。有没有更好的表达方式请指教。

例子:

?- words([p,r,o,l,o,g,' ',i,s,' ',g,o,o,d],Y).
Y = [[p,r,o,l,o,g],[i,s],[g,o,o,d]]

?- words(X,[[p,r,o,l,o,g],[i,s],[g,o,o,d]]).
X = [p,r,o,l,o,g,' ',i,s,' ',g,o,o,d]

我所做的是尝试使用append/3,如下所示,尝试将空字符串放在两者之间,并将它们连接在一起。并用NewCharList 递归List,但由于“超出本地堆栈”而失败。

% base case, empty list
words([],[]).
% X is a list of characters
% Y is a list with characters list as a word
words(CharList,[WordList|List]):-
    append(WordList,[' '],NewWord),
    append(NewWord,CharList,NewCharList),
    words(NewCharList,List).

如何改进代码?谢谢。

编辑1 来自@rajashekar 的百万感谢。现在我明白了代码

% base case
split(_, [], [[]]).

% when the the element list of Ys is empty
% add a C to the Xs, in this case, C is a empty string ' '
split(C, [C|Xs], [[]|Ys]) :-
    split(C, Xs, Ys).

% put the X character from [X|Y] list to Xs
% and goes on next word list
split(C, [X|Xs], [[X|Y]|Ys]) :-
    split(C, Xs, [Y|Ys]).

但在我的 SWI 序言中,这似乎很奇怪:

?-split(' ', X, [[p,r,o,l,o,g],[i,s],[g,o,o,d]]).
X = [p, r, o, l, o, g, ' ', i, s|...]

这是跟踪记录:

[trace]  ?- split(X, [[p,r,o,l,o,g],[i,s],[g,o,o,d]]).
   Call: (10) split(_5702, [[p, r, o, l, o, g], [i, s], [g, o, o, d]]) ? creep
   Call: (11) split(_6210, [[r, o, l, o, g], [i, s], [g, o, o, d]]) ? creep
   Call: (12) split(_6266, [[o, l, o, g], [i, s], [g, o, o, d]]) ? creep
   Call: (13) split(_6322, [[l, o, g], [i, s], [g, o, o, d]]) ? creep
   Call: (14) split(_6378, [[o, g], [i, s], [g, o, o, d]]) ? creep
   Call: (15) split(_6434, [[g], [i, s], [g, o, o, d]]) ? creep
   Call: (16) split(_6490, [[], [i, s], [g, o, o, d]]) ? creep
   Call: (17) split(_6546, [[i, s], [g, o, o, d]]) ? creep
   Call: (18) split(_6596, [[s], [g, o, o, d]]) ? creep
   Call: (19) split(_6652, [[], [g, o, o, d]]) ? creep
   Call: (20) split(_6708, [[g, o, o, d]]) ? creep
   Call: (21) split(_6758, [[o, o, d]]) ? creep
   Call: (22) split(_6814, [[o, d]]) ? creep
   Call: (23) split(_6870, [[d]]) ? creep
   Call: (24) split(_6926, [[]]) ? creep
   Exit: (24) split([], [[]]) ? creep
   Exit: (23) split([d], [[d]]) ? creep
   Exit: (22) split([o, d], [[o, d]]) ? creep
   Exit: (21) split([o, o, d], [[o, o, d]]) ? creep
   Exit: (20) split([g, o, o, d], [[g, o, o, d]]) ? creep
   Exit: (19) split([' ', g, o, o, d], [[], [g, o, o, d]]) ? creep
   Exit: (18) split([s, ' ', g, o, o, d], [[s], [g, o, o, d]]) ? creep
   Exit: (17) split([i, s, ' ', g, o, o, d], [[i, s], [g, o, o, d]]) ? creep
   Exit: (16) split([' ', i, s, ' ', g, o, o, d], [[], [i, s], [g, o, o, d]]) ? creep
   Exit: (15) split([g, ' ', i, s, ' ', g, o, o|...], [[g], [i, s], [g, o, o, d]]) ? creep
   Exit: (14) split([o, g, ' ', i, s, ' ', g, o|...], [[o, g], [i, s], [g, o, o, d]]) ? creep
   Exit: (13) split([l, o, g, ' ', i, s, ' ', g|...], [[l, o, g], [i, s], [g, o, o, d]]) ? creep
   Exit: (12) split([o, l, o, g, ' ', i, s, ' '|...], [[o, l, o, g], [i, s], [g, o, o, d]]) ? creep
   Exit: (11) split([r, o, l, o, g, ' ', i, s|...], [[r, o, l, o, g], [i, s], [g, o, o, d]]) ? creep
   Exit: (10) split([p, r, o, l, o, g, ' ', i|...], [[p, r, o, l, o, g], [i, s], [g, o, o, d]]) ? creep
X = [p, r, o, l, o, g, ' ', i, s|...] .

EDIT2 我发现使用! (cut) 可以减少回溯。

% base case
split(_, [], [[]]).

% when the the element list of Ys is empty
% add a C to the Xs, in this case, C is a empty string ' '
split(C, [C|Xs], [[]|Ys]) :-
    split(C, Xs, Ys),!.

% put the X character from [X|Y] list to Xs
% and goes on next word list
split(C, [X|Xs], [[X|Y]|Ys]) :-
    split(C, Xs, [Y|Ys]),!.

【问题讨论】:

  • 使用dcg-notation!
  • @false 谢谢你的建议。我从 amzi.com/manuals/amzi/pro/ref_dcg.htm 中找到了一个 DCG 示例,例如 sentence(s(S,V,O)) --&gt; subject(S), verb(V), object(O).。在这个例子中,我如何表达数学上主语、动词和宾语也可以组成一个句子的模式,其中关系是双向的?
  • w键让SWI显示完整答案
  • @gusbro 谢谢!有用。但是由于有实现的测试用例,我只能写谓词。

标签: list prolog char


【解决方案1】:

代码完全符合您的要求。 这里:

?-split(' ', X, [[p,r,o,l,o,g],[i,s],[g,o,o,d]]).
X = [p, r, o, l, o, g, ' ', i, s|...]

X 正是预期的输出。

如果您想查看所有 X,请执行以下操作:

?-split(' ', X, [[p,r,o,l,o,g],[i,s],[g,o,o,d]]), write_canonical(X).
[p,r,o,l,o,g,' ',i,s,' ',g,o,o,d]
X = [p, r, o, l, o, g, ' ', i, s|...]

在 SWI prolog 的输出中缩短了长列表。

【讨论】:

  • 感谢另一个解决方案,但由于有一个测试用例,我只能写谓词。
  • X = [p, r, o, l, o, g, ' ', i, s|...] 的缩写只是视觉上的。 SWI内部知道X与[p,r,o,l,o,g,' ',i,s,' ',g,o,o,d]统一
【解决方案2】:
split(_, [], [[]]).
split(C, [C|Xs], [[]|Ys]) :-
    split(C, Xs, Ys).
split(C, [X|Xs], [[X|Y]|Ys]) :-
    split(C, Xs, [Y|Ys]).
| ?- split(' ', [p,r,o,l,o,g,' ',i,s,' ',g,o,o,d], X).

X = [[p,r,o,l,o,g],[i,s],[g,o,o,d]] ? 

yes
| ?- split(' ', X, [[p,r,o,l,o,g],[i,s],[g,o,o,d]]).

X = [p,r,o,l,o,g,' ',i,s,' ',g,o,o,d] ? 

yes
  • 您可以使用模式声明来说明谓词在两种模式下都有效。例如,您可以说words/2 谓词需要在words(+Chars, -Words)words(-Chars, +Words) 模式下工作(或在words(+, -)words(-, +) 模式下)。 See 了解模式详情。这些主要用于 prolog 中的文档目的(查看 mercury 真正起作用的地方)。
  • 使用Definite Clause Grammers DCG 将使此类问题变得容易得多。
  • 当您感到递归失控时,请尝试使用trace。如果您使用它,您大部分时间都可以发现问题。

【讨论】:

  • 感谢您的回答和其他建议,但正如您所见,要求仅使用两个参数。
  • 而且,关于模式,在两个参数都可以生成的问题上,所以根据手册,应该是words(?,?)?谢谢。
  • 我使用C 作为更通用解决方案的额外参数。您可以将其删除,只需将子句正文中的C 替换为' '。或者直接说words(X, Y) :- split(' ', X, Y).。一个谓词可以有多个模式,你可以只记录你需要的那些,并且可以保证正确性。例如,您可以说lengthlength(+, +)length(+, -)length(?, +)length(?, -) 模式下工作,但它通常被记录为length(?, ?)
  • 感谢您的全面解释。
  • @Woden: SWI-Prolog 正在省略您的解决方案。当列表变得太大时,swipl 默认情况下不会打印所有列表。您可以使用write(X) 来明确打印它或在选择点或跟踪中按w 以更改默认设置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
  • 2021-03-17
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多