在 Prolog 中展平列表
我才使用 Prolog 几天。我明白一些事情,但这确实让我困惑。
我想编写一个函数来获取一个列表并将其展平。
?- flatten([a,[b,c],[[d],[],[e]]],Xs).
Xs = [a,b,c,d,e]. % expected result
该函数取出列表的内部结构。
这就是我到目前为止所拥有的:
flatten2([],[]).
flatten2([Atom|ListTail],[Atom|RetList]) :-
atom(Atom), flatten2(ListTail,RetList).
flatten2([List|ListTail],RetList) :-
flatten2(List,RetList).
现在,当我调用时,这是有效的:
?- flatten2([a,[b,c],[[d],[],[e]]], R).
R = [a,b,c,d,e]. % works as expected!
但是当我调用以查看我输入的列表是否已经展平时,返回 false
而不是 true:
?- flatten2([a,[b,c],[[d],[],[e]]], [a,b,c,d,e]).
false. % BAD result!
为什么一方面可以,另一方面不行?我觉得我错过了一些非常简单的东西。
I've only been working with Prolog for a couple days. I understand some things but this is really confusing me.
I'm suppose to write a function that takes a list and flattens it.
?- flatten([a,[b,c],[[d],[],[e]]],Xs).
Xs = [a,b,c,d,e]. % expected result
The function takes out the inner structures of the list.
This is what I have so far:
flatten2([],[]).
flatten2([Atom|ListTail],[Atom|RetList]) :-
atom(Atom), flatten2(ListTail,RetList).
flatten2([List|ListTail],RetList) :-
flatten2(List,RetList).
Now, this works when I call:
?- flatten2([a,[b,c],[[d],[],[e]]], R).
R = [a,b,c,d,e]. % works as expected!
But when I call to see if a list that I input is already flattened, is returns false
instead of true
:
?- flatten2([a,[b,c],[[d],[],[e]]], [a,b,c,d,e]).
false. % BAD result!
Why does it work on one hand, but not the other? I feel like I'm missing something very simple.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我没有使用
findall
找到解决方案,所以我将添加它。 (如果列表完全磨碎,包括其所有元素都完全磨碎,它将起作用)首先,我们定义如何测试列表:
以及
member
的传递闭包,我们称之为member*
:展平后的列表就是
member的全部解*
不是列表:例子:
I didn't find a solution using
findall
, so I'll add it. (it will work if the list is fully ground, including that all its elements are fully ground)First, we define how to test for a list:
and the transitive closure of
member
, we call itmember*
:The flattened list is all the solution of
member*
which are not lists:Example:
您给出的
flatten2/2
的定义已失效;它实际上的行为是这样的:因此,考虑到您已经将
R
绑定到[a,b,c,d,e]
,失败不是奇怪。您的定义是在第三个子句中丢弃列表的尾部 (
ListTail
) - 这需要整理并连接回列表以通过RetList
返回。这里有一个建议:这会递归地将所有列表列表减少为单个项目列表
[x]
或丢弃的空列表[]
。然后,它会累积并将它们全部追加到输出的一个列表中。请注意,在大多数 Prolog 实现中,空列表
[]
是一个原子和一个列表,因此对atom([])
的调用和is_list([])
均评估为 true;这不会帮助您丢弃空列表而不是字符原子。The definition of
flatten2/2
you've given is busted; it actually behaves like this:So, given the case where you've already bound
R
to[a,b,c,d,e]
, the failure isn't surprising.Your definition is throwing away the tail of lists (
ListTail
) in the 3rd clause - this needs to be tidied up and connected back into the list to return viaRetList
. Here is a suggestion:This one recursively reduces all lists of lists into either single item lists
[x]
, or empty lists[]
which it throws away. Then, it accumulates and appends them all into one list again out the output.Note that, in most Prolog implementations, the empty list
[]
is an atom and a list, so the call toatom([])
andis_list([])
both evaluate to true; this won't help you throw away empty lists as opposed to character atoms.您可以保持您的列表开放式,在其末尾有一个指向其开头的指针,并在其末尾有一个“结束孔/自由指针”(即 logvar),然后您可以在结束时实例化该列表。到达:
然后将其称为这样
就不需要在任何地方使用
reverse
。这种技术被称为“差异列表”,但将其视为“开放式列表”会更容易。更新:使用 dcg 语法。既然它是单向的(第一个参数必须完全接地),为什么不使用剪切呢:
测试:
如果定义是完全声明性的,那么最后一个也应该通过
X=[c] 成功; X=[[],c]; ...; X=[[c]]; ...
;唉,事实并非如此。(edit2:简化了两个版本,感谢 @mat 的评论!)
You can maintain your lists open-ended, with both a pointer to its start, and an "ending hole ⁄ free pointer" (i.e. logvar) at its end, which you can then instantiate when the end is reached:
You then call it as
That way there's no need to use
reverse
anywhere. This technique is known as "difference lists", but it's much easier just to think about it as "open-ended lists" instead.update: This is much easier coded using the dcg syntax. Since it is unidirectional (the first argument must be fully ground), why not use cuts after all:
Testing:
If the definition were fully declarative, the last one should've succeeded also with
X=[c] ; X=[[],c] ; ... ; X=[[c]] ; ...
; alas, it isn't.(edit2: simplified both versions, thanks to @mat's comments!)
Prolog 的列表符号是非常简单的 prolog 术语之上的语法糖。 Prolog 列表如下表示:
空列表由原子
[]
表示。为什么?因为这看起来像是空列表的数学符号。他们本可以使用像nil
这样的原子来表示空列表,但他们没有。非空列表由术语
.\2
表示,其中第一个(最左边)参数是列表的 head ,第二个(最右边)参数参数是列表的尾,递归地,它本身就是一个列表。一些示例:
空列表:
[]
表示为原子:<前><代码>[]
一个元素的列表,
[a]
内部是存储为<前><代码>.(a,[])
两个元素的列表
[a,b]
在内部存储存储为<前><代码>.(a,.(b,[]))
三个元素的列表
[a,b,c] 内部存储为
<前><代码>.(a,.(b,.(c,[])))
列表头部的检查同样是相同符号的语法糖:
[X|Xs]
与相同。(X,Xs)
[A,B|Xs]
与.(A,.(B,Xs))
[A,B]
(见上文)与 <代码>.(A,.(B,[])),我自己会写
flatten/2像这样:
Prolog's list notation is syntactic sugar on top of very simple prolog terms. Prolog lists are denoted thus:
The empty list is represented by the atom
[]
. Why? Because that looks like the mathematical notation for an empty list. They could have used an atom likenil
to denote the empty list but they didn't.A non-empty list is represented by the term
.\2
, where the first (leftmost) argument is the head of the list and the second (rightmost) argument is the tail of the list, which is, recursively, itself a list.Some examples:
An empty list:
[]
is represented as the atom it is:A list of one element,
[a]
is internally stored asA list of two elements
[a,b]
is internally stored asA list of three elements,
[a,b,c]
is internally stored asExamination of the head of the list is likewise syntactic sugar over the same notation:
[X|Xs]
is identical to.(X,Xs)
[A,B|Xs]
is identical to.(A,.(B,Xs))
[A,B]
is (see above) identical to.(A,.(B,[]))
Myself, I'd write
flatten/2
like this:在
if_//3
和list_truth/2
的基础上,我们可以实现myflatten/2
如下:示例查询(取自其他答案以及对答案的注释):
neither_nil_nor_cons_t
和not(nil_or_cons_t)
描述有相同的解,但解的顺序不同。考虑:Building on
if_//3
andlist_truth/2
, we can implementmyflatten/2
as follows:Sample queries (taken from other answers, and comments to answers):
neither_nil_nor_cons_t
andnot(nil_or_cons_t)
describe have same solutions, but the solution order differs. Consider:为了完整性,这是一个基于累加器的版本:
Here's an accumulator based version for completeness:
没有任何其他谓词,仅具有尾递归。
Without any other predicate, with tail-recursion only.