【问题标题】:Prolog: Sum one element of a list at a timeProlog:一次对列表的一个元素求和
【发布时间】:2017-12-09 22:28:07
【问题描述】:

我是 Prolog 的新手,并决定尝试解决一个问题,其中我有一个符号序列,每个符号的值都为 1 或 -1。我需要将它们全部加在一起,一次一个元素,并在哪个索引处提取总和第一次低于 0。由于我来自命令式背景,我想象一个计数变量和一个for-loop,但显然我不能在 Prolog 中做到这一点。

value('(', 1).
value(')', -1).

main(R) :- readFile("input", R), ???

readFile(Path, R) :- 
    open(Path, read, File), 
    read_string(File, _, Str), 
    stringToCharList(Str, Xs), 
    maplist(value, Xs, R).

stringToCharList(String, Characters) :-
    name(String, Xs),
    maplist(toChar, Xs, Characters ).

toChar(X, Y) :- name(Y, [X]).

如您所见,到目前为止,我真正管理的只是读取包含序列的文件,并将其转换为 1 和 -1。我不知道从这里去哪里。我想问题是三方面的:

  • 我需要遍历一个列表
  • 我需要对列表中的每个元素求和
  • 我需要返回某个索引

有什么建议吗?我可以以某种方式切断迭代将总和降至零以下的列表,然后返回长度吗?

【问题讨论】:

  • 您的输入文件是什么样的?如果它是一个或多个有效的 Prolog 术语,那么 Prolog I/O 可以直接读取它们,您不需要解析它们。
  • 只是一长串括号。
  • 现在您已经有了 1 和 -1 的列表,只需执行一个简单的递归谓词遍历列表,累积总和直到达到 0。计数器将成为索引。 index_at_zero_sum(L, Index) :- index_at_zero_sum(L, 0, Index). 开始使用。第二个参数是初始总和,当然是0。您的 index_at_zero_sum/3 将是递归的,并且应该在总和再次变为 0 时以基本情况终止。否则默认情况下它会失败(它没有达到零和)。
  • 您也可以在阅读文件时即时进行求和。您只需将累加器和索引变量添加到现有谓词中。
  • 你只需要从逻辑上考虑。 index_at_zero_sum([], 0, _) 是什么意思? _ 通常是一个“无关”变量。我怀疑你实际上非常关心。您可能需要一个 aux 变量作为索引,这意味着index_at_zero_sum 有 4 个参数。您从辅助索引的值 0 开始并递增它,并在不触及它的情况下继续传递实际索引变量。在您的基本情况下,您将辅助索引与索引统一,因为它就是您想要的值。

标签: list prolog sum iteration swi-prolog


【解决方案1】:

我将在 Prolog 中使用辅助变量的原理来充当计数器,直到条件达到我们想要的为止。然后辅助计数器在基本情况下与该点的变量统一。

我在这里盲目地假设您的代码按说明工作。我没有测试它(这取决于你)。

main(IndexAtZeroSum) :- readFile("input", R), index_at_zero_sum(R, IndexAtZeroSum).

readFile(Path, R) :- 
    open(Path, read, File), 
    read_string(File, _, Str), 
    stringToCharList(Str, Xs), 
    maplist(value, Xs, R).

stringToCharList(String, Characters) :-
    name(String, Xs),
    maplist(toChar, Xs, Characters ).

toChar(X, Y) :- name(Y, [X]).

% The following predicate assumes indexing starting at 0
index_at_zero_sum([V|Vs], IndexAtZeroSum) :-
    index_at_zero_sum(Vs, V, 0, IndexAtZeroSum).

% When sum is zero, Index is what we want
index_at_zero_sum(_, 0, Index, Index).
index_at_zero_sum([V|Vs], Sum, CurIndex, Index) :-
    S is Sum + V,
    NextIndex is CurIndex + 1,
    index_at_zero_sum(Vs, S, NextIndex, Index).

index_at_zero_sum/2 提供总和为零的给定列表的索引。它通过使用辅助谓词index_at_zero_sum/4 来实现这一点,从第一个值的总和开始(总和是值本身)和当前索引从 0 开始。所以第二个参数是索引 0 的总和。后续调用index_at_zero_sum/4 增加索引并累加总和,直到总和变为 0。此时,基本情况成功并将第四个参数与当前索引统一。如果在列表变空之前总和从未变为 0,则谓词失败。


您还可以避免读取整个文件并使用 get_char/2 创建数字列表:
index_at_zero_sum(Path, Index) :-
    open(Path, read, File),
    get_char(File, C),
    value(C, V),
    (   index_at_zero_sum(File, V, 0, Index)
    ->  close(File)
    ;   close(File),
        fail
    ).

index_at_zero_sum(_, 0, Index, Index).
index_at_zero_sum(File, Sum, CurIndex, Index) :-
    get_char(File, C),
    value(C, V),
    S is Sum + V,
    NewIndex is CurIndex + 1,
    index_at_zero_sum(File, S, NewIndex, Index). 

【讨论】:

  • 非常感谢!您在 cmets 中的提示让我走上了正确的道路,但我被困在不知道应该如何处理休息时。无论如何,这奏效了。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
相关资源
最近更新 更多