让我们先看一下你的`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可以在子句的头部中统一术语,因此您可以避免冗余的统一。因此,这就是您所需要的:
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)
调用并寻找使
foo(X)
成功的另一个
X
值。如果找到了,则会使用新的
X
值再次调用
bar(X, Y)
,以此类推。
因此,
append(Add, Result)
不会将
Add
附加到
Result
并产生
Result
的新值。事实上,带有两个参数的
append
表示第二个列表参数是第一个列表的所有元素的连接,假设第一个参数是列表的列表,因此
append/2
的定义不匹配。
在考虑递归时,请意识到参数列表之间是一一对应的。结果列表的头是第一个参数列表的“平方对”的头。然后,递归地,第二个参数的尾部是第一个参数尾部的“平方对”列表。您只需要在Prolog中表达出来。我们也可以使用我上面描述的在条款头部内部进行统一的技术。
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).