我对prolog中的列表列表有一些(或很多)麻烦。
所以我有一个数字列表,比如[5,6,1,3]作为输入。 输出应为[[5,25],[6,36],[1,1],[3,9]]。
我已经有一个谓词“返回2个项目列表”(请记住,我必须更改get_squared_pair函数以获取其他相关值):
get_squared_pair(Number, Result) :-
get_squared_value(Number, SquareValue),
Result = [Number, SquareValue].
get_squared_value(Number, Result) :-
Result is Number * Number.
直到这里它非常符合逻辑。现在我需要一个谓词递归迭代列表,将平方对添加到新列表,然后返回列表列表。我现在拥有的是:
return_list([], 0).
return_list([Head | Tail], Result) :-
get_squared_pair(Head, Add),
append(Add,Result),
return_list(Tail, Result).
由于多种原因,这并不起作用,而且主要是因为我似乎无法弄清楚递归实际上如何与列表一起使用,更不用说列表列表了。此外,它目前的运行顺序错误,没有任何帮助。
我知道这可能有点模糊,但我已经尝试用谷歌搜索我的方式,但似乎无法将我发现的内容很好地转化为我自己的问题。
非常感谢任何帮助!
答案 0 :(得分:4)
让我们先看一下你的get_squared_pair/2
。虽然它有效,但它可以整理一下,这也有助于理解Prolog的工作原理。 Prolog的主要机制是统一,这与其他语言中出现的赋值不同。在统一中,Prolog检查两个术语,并尝试通过在一个或两个术语中实例化变量来使统一它们以使它们匹配。 Prolog中有一些谓词,比如is/2
,它们用于在一个参数中计算表达式,然后用该结果统一第一个参数。
你的第一个谓词,你写的是:
get_squared_pair(Number, Result) :-
get_squared_value(Number, SquareValue),
Result = [Number, SquareValue].
get_squared_value(Number, Result) :-
Result is Number * Number.
可以通过两种方式简化。首先,您可以合并get_squared_value/2
,因为它只是一行,并不需要自己的谓词。我们将重命名谓词,因此它不是必要的。
square_pair(Number, Square) :-
S is Number * Number, % Square the number
Square = [Number, S]. % Unify Square with the pair
Prolog可以统一该子句的 head 中的术语,因此您可以避免冗余统一。所以这就是你所需要的:
square_pair(Number, [Number, Square]) :-
Square is Number * Number.
转到主谓词return_list/2
。首先,我们将此谓词重命名为square_pairs
。使用列表进行递归时,最常见的模式是继续减少列表,直到它为空,然后基本案例处理空列表。你的实现是这样做的,但基本情况有点困惑,因为第二个参数是一个整数而不是一个列表:
square_pairs([], 0).
这应该是:
square_pairs([], []).
您的主谓词条款没有正确使用append/2
。 SWI Prolog中有两种append
形式:append/2
和append/3
。您可以在SWI Prolog在线文档中查找这些内容。我可以告诉你,在Prolog中,除了通过回溯之外,你不能在谓词子句实例化之后更改变量的值。例如,查看可能在谓词子句中的以下序列:
X = a, % Unify X with the atom 'a'
X = b, % Unify X with the atom 'b'
在这种情况下,第二个表达式将始终失败,因为X
已经统一且无法再次统一。但是,如果我有这个:
foo(X), % Call foo, which unifies X with a value that makes 'foo' succeed
bar(X, Y), % Call bar, which might fail based upon the value of 'X'
在上述情况下,如果bar(X, Y)
失败,则Prolog会回溯到foo(X)
来电,并寻找X
的另一个值foo(X)
成功。如果找到,则会再次使用bar(X, Y)
的新值调用X
,依此类推。
因此append(Add, Result)
不会将Add
附加到Result
,从而为Result
产生新值。事实上,带有两个参数的append
表示第二个列表参数是第一个列表的所有元素的串联,假设第一个参数是列表列表,因此append/2
的定义不会无论如何都要匹配。
在考虑你的递归时,要意识到参数列表是彼此一一对应的。结果列表的头部是第一个参数中列表头部的“方形对”。然后,递归地,第二个参数的尾部是第一个参数的尾部的平方对的列表。你只需要在Prolog中表达它。我们也可以使用上面描述的技术在子句的 head 中进行统一。
square_pairs([Head | Tail], [SqPair | SqTail]) :-
square_pair(Head, SqPair),
square_pairs(Tail, SqTail).
square_pairs([], []).
现在我们可以做另一种简化,它完全消除了square_pair/2
辅助谓词:
square_pairs([Head | Tail], [[Head, SqHead] | SqTail]) :-
SqHead is Head * Head,
square_pairs(Tail, SqTail).
square_pairs([], []).
在Prolog中有一个名为maplist
的方便谓词,它可以用来定义一个在两个列表之间并行运行的关系,这就是我们这里的场景。我们可以带回square_pair/2
谓词并使用maplist
:
square_pairs(Numbers, SquarePairs) :-
maplist(square_pair, Numbers, SquarePairs).
答案 1 :(得分:2)
所以你想把你的列表变成另一个,这样每个元素(一个数字)就会变成一个两元素的列表,数字和它的正方形。
您需要做的就是告诉Prolog。首先,第二个:
turn_into_two(Num, [A,B]):-
什么是A
?
A is Num,
什么是B
?我们也告诉Prolog:
B is ... * ... .
现在,到我们的列表。 Prolog中的列表[A|B]
由其头元素A
及其尾B
组成 - 除非它当然是一个空列表[]
。列表的元素 并不重要;列表是一个列表。
我们需要考虑所有情况,否则我们不会谈论列表,而是其他事情:
turn_list([], Res):-
那么如果列表为空,我们的结果是什么?它也应该是空的,对吧?
Res = ... .
在另一种情况下,
turn_list([A|B], Res):-
我们的结果不会是空的,所以它 的头尾是正确的吗?
Res = [C|D],
接下来我们说出我们对头部的了解:输入列表的头部变成我们上面描述的两个元素列表,对吧?
turn_into_two(A,C),
然后我们说关于尾巴的文章。但是我们对尾巴了解多少?我们知道一个是另一个转换的结果,就像整个列表一样:
turn_list( ... , ...) .
就是这样。顺便提一下,我们所描述的,遵循映射的范例。我们可以使用任何其他谓词代替turn_into_two/2
,并且将为输入列表中的每个元素以及结果列表中的相应元素调用 it 。