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

5
我正在学习Prolog,并看到了这段代码。
foo([],[]).
foo([[A,_ ]|L], [A|P]) :-foo(L ,P).

结果表明,此代码获取列表的N个元素,例如,如果我们提供以下查询:
?foo([[car],[house],[man]],X)
X= [c,h,m]

一开始读到这里,我发现有些问题。对于我来说,此代码获取了列表的最后一个元素和列表的第一个元素的剩余部分,因此我认为首次扩展将是(trace)。

foo([[house],[man]], ar)
foo([[man]], ouse)
foo([], an)
false.

我尝试使用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.

我哪里做错了吗?
1个回答

5

获取第一个元素

在你的语句中,模式[A, _]是错误的,或者至少不够通用[A, _]可以与包含恰好两个元素的列表匹配,但对于具有超过两个或仅一个元素的列表将失败,正如你所发现的那样。

你需要使用[A|_]模式:确实是一个列表,其中头部是A,而我们不关心其余部分(尾部)。例如:

foo([],[]).
foo([<b>[A|_]</b>|L], [A|P]) :- foo(L, P).

话虽如此,您可以通过实现接受列表头的谓词简化这个过程:

head([H|_], H).

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

foo(A, B) :-
    <b>maplist</b>(head, A, B).

maplist会像这样调用headhead(Ai, Bi),其中AiBi分别是AB的元素。

使用第一个字符获取子串

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

head_first([A|_], C) :-
    string_chars(A, [C|_]).

然后使用maplist/3 [swi-doc]重新定义foo/2

foo(A, B) :-
    maplist(head_first, A, B).

我们随后得到:
?- foo([[car],[house],[man]], X).
X = [c, h, m].

也许我的问题会显得很蠢...在foo/2中的[A|P]不会从foot(L,P)的第一个列表(在列表中)中获取尾巴P。因为我很好理解[[A|_]|L],但是[A|P]我有一些疑问。顺便说一句,总是清晰明了。对于我在Prolog方面提出的更多问题,很抱歉,我正在学习这个主题。 - theantomc
使用 [A|P],我们此刻正在构建一个列表(假设我们是朝这个方向读取的),因此A是结果的第一个元素,而P则是尾部(剩余列表)。你在问题中实际上自己写了[A|P]这一部分。 - Willem Van Onsem
你的意思是构建一个可变部分吗?(X = [c, h, m])。因为我第一次看代码时,我以为这部分是指查询中列表的列表(查询的第一个元素),但我肯定是错了(因为它是第二个元素)。 - theantomc
@theantomc:是的,这里我们正在构造X=[c,h,m]部分。 - Willem Van Onsem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接