列表 Prolog 的列表的第一个元素

the*_*omc 5 prolog

我正在学习 Prolog,我看到了这段代码

foo([],[]).
foo([[A,_ ]|L], [A|P]) :-foo(L ,P).
Run Code Online (Sandbox Code Playgroud)

结果表明,此代码获取列表列表中的 N 个元素,如果我们给出以下查询,则举个例子:

?foo([[car],[house],[man]],X)
X= [c,h,m]
Run Code Online (Sandbox Code Playgroud)

初读时我发现有些不对劲。对我来说,这段代码采用列表列表的尾部和列表第一个元素的其余部分,所以对我来说,第一次扩展将是(跟踪)

foo([[house],[man]], ar)
foo([[man]], ouse)
foo([], an)
false.
Run Code Online (Sandbox Code Playgroud)

我尝试使用 swi-prolog 进行编译并给出以下跟踪:

[trace]  ?- trace,foo([[car],[house],[man]],X).
Call: (9) foo([[car], [house], [man]], _1016) ? creep
Fail: (9) foo([[car], [house], [man]], _1016) ? creep
false.
Run Code Online (Sandbox Code Playgroud)

我有什么错吗?

Wil*_*sem 5

获取第一个元素

您的子句中的模式[A, _]是错误的,或者至少不够通用与仅包含两个[A, _]元素的列表相结合,但是对于具有两个以上元素或一个元素的列表,这将失败,就像您发现的那样。

您需要使用该[A|_]模式:确实是一个列表,其中头部是A,我们对其余部分(尾部)不感兴趣。喜欢:

foo([],[]).
foo([[A|_]|L], [A|P]) :- foo(L, P).
Run Code Online (Sandbox Code Playgroud)

话虽如此,您可以通过实现一个采用列表头部的谓词来简化这一点:

head([H|_], H).
Run Code Online (Sandbox Code Playgroud)

然后使用maplist/3[swi-doc]

foo(A, B) :-
    maplist(head, A, B).
Run Code Online (Sandbox Code Playgroud)

maplist因此将分别调用like head、和的元素。head(Ai, Bi)AiBiAB

获取第一个字符的子字符串

但根据示例输出,这不是您想要的:您还想获得原子的第一个“字符”,我们可以通过使用string_chars/2[swi-doc]来做到这一点:

head_first([A|_], C) :-
    string_chars(A, [C|_]).
Run Code Online (Sandbox Code Playgroud)

然后foo/2maplist/3[swi-doc]再次定义:

foo(A, B) :-
    maplist(head_first, A, B).
Run Code Online (Sandbox Code Playgroud)

然后我们得到:

?- foo([[car],[house],[man]], X).
X = [c, h, m].
Run Code Online (Sandbox Code Playgroud)