Prolog 创建列表列表

Prolog create list of lists

我在 prolog 中遇到一些(或很多)列表问题。

所以我有一个数字列表,比如 [5,6,1,3] 作为输入。 输出应该是 [[5,25],[6,36],[1,1],[3,9]].

我已经有一个谓词“return”是 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.

到这里为止都是合乎逻辑的。现在我需要一个谓词递归遍历一个列表,将平方对添加到一个新列表,然后 returns 这个列表列表。我现在拥有的是:

return_list([], 0).
return_list([Head | Tail], Result) :-
get_squared_pair(Head, Add),
append(Add,Result),
return_list(Tail, Result).

由于多种原因,这不起作用,主要是因为我似乎无法弄清楚递归实际上是如何与列表一起工作的,更不用说列表列表了。此外,目前 运行 的顺序错误,这无济于事。

我知道这可能有点含糊,但我已经尝试用谷歌搜索解决这个问题,但似乎无法很好地将我发现的内容转化为我自己的问题。

如有任何帮助,我们将不胜感激!

所以你想把你的列表变成另一个,这样每个元素(一个数字)都变成一个 two-element 列表,数字和它的平方。

您需要做的就是将其告知 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 将针对输入列表的每个元素以及结果列表中的相应元素被调用。

先看看你的get_squared_pair/2。虽然它在工作,但可以对其进行一些整理,这也有助于理解 Prolog 的工作原理。 Prolog 的主要机制是 unification,这与其他语言中出现的 assignment 不同。在 unification 中,Prolog 检查两个术语并尝试 unify 它们,方法是在其中一个或两个术语中实例化变量以使它们匹配。 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/2append 在 SWI Prolog 中有两种形式:append/2append/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) 成功。如果它找到一个,那么它会用 X 的新值再次调用 bar(X, Y),依此类推。

所以 append(Add, Result) 不会将 Add 附加到 Result,从而为 Result 生成一个新值。事实上,带有两个参数的 append 表示第二个列表参数是第一个列表的所有元素的串联,假设第一个参数是列表的列表,所以 append/2 的定义不是'无论如何都不匹配。

考虑递归时,要意识到参数列表是一一对应的。结果列表的头部是第一个参数中列表头部的 "square pair"。然后,递归地,第二个参数的尾部是第一个参数尾部的方形对列表。您只需要在 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).