【问题标题】:Prolog - Keep track of multiple sum countersProlog - 跟踪多个总和计数器
【发布时间】:2019-05-16 13:24:48
【问题描述】:

所以我有一个如下所示的列表:

[ 
  ["p1", "p2", "100", "Storgatan"], 
  ["p1", "p3", "200", "Lillgatan"], 
  ["p2", "p4", "100", "Nygatan"], 
  ["p3", "p4", "50", "Kungsgatan"], 
  ["p4", "p5", "150", "Kungsgatan"]
]

每个嵌套列表中的元素代表(按顺序):
第一个元素 = 起点
第二个元素 = 终点
第三个元素 = 距离
第 4 个元素 = 街道名称。

我现在必须写一个谓词来计算哪条街道最短,哪条街道最长,以及它们各自的(总和)距离。

例如,最终输出应如下所示:

Longest street: Kungsgatan, 200
Shortest street: Storgatan, 100

我真的不明白为什么起点和终点是这里的相关信息。我目前的想法是收集所有独特的街道名称,将它们与从零开始的每条街道的计数器一起放在一个单独的列表中,然后使用该列表来累积每条单独街道的所有距离。

类似:

create_sum_list([
   ["p1", "p2", "100", "Storgatan"], 
   ["p1", "p3", "200", "Lillgatan"], 
   ["p2", "p4", "100", "Nygatan"], 
   ["p3", "p4", "50", "Kungsgatan"], 
   ["p4", "p5", "150", "Kungsgatan"]
], SL).

SL= [[Storgatan, 0], [Lillgatan, 0],
     [Nygatan, 0], [Kungsgatan ,0]]

accumulate(SL, List).

List=[[Storgatan, 100], [Lillgatan, 200], 
      [Nygatan, 100], [Kungsgatan ,200]]

这可能是一个愚蠢的想法,可能有更好的方法来解决这个问题。我想了很多不同的想法,要么我走到了死胡同,要么对于这样一个“简单”的任务来说太复杂了。

我可以通过“普通”命令式编程轻松实现这一点,但我是逻辑编程和 Prolog 的新手。我不知道如何实现这一点。

帮助?

谢谢!

【问题讨论】:

  • @GuyCoder 有很大的不同吗?我真的不知道。我猜我可以把它们转换成原子?
  • @GuyCoder 这不是错字。 Kungsgatan 的长度是 200。你看到 Kungsgatan 在我的列表中出现了两次,分别为 50 和 150 (150+50=200)?我正在总结街道的总长度。
  • 这是家庭作业,但我编造了数据/列表的结构。在此之前的任务(我已经完成)是读取文件并将内容拆分为“适当的数据结构”。该文件将每个嵌套列表作为单独的行。例如首先是p1\tp2\t100\tStorgatan,然后是下一行的p1\tp3\t200\tLillgatan。我在这里创建了一个相关的帖子:stackoverflow.com/questions/56159054/…
  • @GuyCoder 这是文件内容:pastebin.com/V70Ubs4M 这是我用来读取文件并输出上述列表结构的代码:pastebin.com/3n1Htjgs
  • @GuyCoder 不,没有后续任务。这是我作业中的最后一项任务。

标签: list prolog sum counter accumulate


【解决方案1】:

如果您已经有一个列表,并且想要按街道名称进行分组并对长度求和,则必须决定如何进行分组。一种方法是使用库(对):

streets_lengths(S, L) :-
    maplist(street_name_and_length, S, NL),
    keysort(NL, NL_sorted),
    group_pairs_by_key(NL_sorted, G),
    maplist(total_lengths, G, GT),
    transpose_pairs(GT, By_length), % sorts!
    group_pairs_by_key(By_length, L).

street_name_and_length([_, _, N, L], L_atom-N_number) :-
    number_string(N_number, N),
    atom_string(L_atom, L).

total_lengths(S-Ls, S-T) :-
    sum_list(Ls, T).

你可以这样使用它:

?- streets_lengths([
   ["p1", "p2", "100", "Storgatan"],
   ["p1", "p3", "200", "Lillgatan"],
   ["p2", "p4", "100", "Nygatan"],
   ["p3", "p4", "50", "Kungsgatan"],
   ["p4", "p5", "150", "Kungsgatan"]
], SL).
SL = [100-['Storgatan', 'Nygatan'], 200-['Lillgatan', 'Kungsgatan']].

由于可以有许多相同长度的街道,因此返回的结果按长度分组。您可以通过获取列表的第一个和最后一个元素来获取“最短”和“最长”,如下所示:

L = [First|_], last(L, Last)

【讨论】:

  • 感谢您的回答。您能否向我解释一下谓​​词“参数”中的- 是什么意思(例如,L_atom-N_numberS-T 等)?另外,是否有一个“推荐”的数据结构可以用于我的列表,以使解决方案更简单还是没关系?
  • Nvm,我认为 - 运算符在 Prolog 中创建了一对。现在感觉好傻哈哈。
  • 我注意到您的代码中有一个很大的缺陷。如果 2 个相同的街道名称在列表中不连续,则您的代码不起作用。例如,如果我将列表中的最后一个嵌套列表移到列表的前面。可能是因为group_pairs_by_key 只对具有等效 CONSECUTIVE 键的值进行分组:(
  • 是的,必须先按键排序!它也在文档中说 - 修复了它。还有其他的事情我没有处理(毕竟是你的问题),点数。我不知道它们应该是什么意思,但我想它们对于将部分街道连接在一起也很重要?
  • 我的解决方案现在在分组之前进行排序。但是你完全需要更仔细地考虑你在做什么。有无数种不同的方法来解决它。这只是一种方法(我不相信它最适合您的目的)。
【解决方案2】:

由于这是作业,我不会给你完整的答案,而是代码的关键部分。

正如我在 cmets 中指出的,结构的格式很重要,例如列表、术语、原子、字符串等。

test(Street_lengths,Shortest) :-
    List =
        [
         street(p1, p2, 100, 'Storgatan'),
         street(p1, p3, 200, 'Lillgatan'),
         street(p2, p4, 100, 'Nygatan'),
         street(p3, p4, 50,  'Kungsgatan'),
         street(p4, p5, 150, 'Kungsgatan')
        ],
    street_lengths(List,Street_lengths),
    lengths1(Street_lengths,Lengths),
    min_list(Lengths,Min),
    convlist(value_shortest2(Min),Street_lengths,Shortest).

street_lengths([H|T],Street_lengths) :-
    merge_streets(H,T,Street_lengths).

% 2 or more items in list
merge_streets(street(_,_,Length0,Name),[street(_,_,Length1,Name),street(_,_,Length2,Name2)|Streets0],[street(Length,Name)|Streets]) :-
    Length is Length0 + Length1,
    merge_streets(street(_,_,Length2,Name2),Streets0,Streets).
merge_streets(street(_,_,Length0,Name0),[street(_,_,Length1,Name1)|Streets0],[street(Length0,Name0)|Streets]) :-
    Name0 \= Name1,
    merge_streets(street(_,_,Length1,Name1),Streets0,Streets).

% 1 item in list
merge_streets(street(_,_,Length0,Name),[street(_,_,Length1,Name)],[street(Length,Name)]) :-
    Length is Length0 + Length1.
merge_streets(street(_,_,Length0,Name0),[street(_,_,Length1,Name1)],[street(Length0,Name0)|Streets]) :-
    Name0 \= Name1,
    merge_streets(street(_,_,Length1,Name1),[],Streets).

% no item in list
merge_streets(street(_,_,Length,Name),[],[street(Length,Name)]).

lengths1(List,Lengths) :-
    maplist(value_length1,List,Lengths).

value_length1(street(Length,_),Length).

value_shortest2(Min,street(Min,Name),street(Min,Name)).

示例运行:

?- test(Street_lengths,Shortest).
Street_lengths = [street(100, 'Storgatan'), street(200, 'Lillgatan'), street(100, 'Nygatan'), street(200, 'Kungsgatan')],
Shortest = [street(100, 'Storgatan'), street(100, 'Nygatan')] ;
false.

我留给你做的时间最长,但应该是小菜一碟。

要显示您在问题中提到的信息,我将使用format/2

因此,现在您要么必须将读取数据的方式更改为此代码的格式,要么更改此代码以使用您构建数据的方式。恕我直言,我会更改数据以使用此结构。

如果想知道你的代码效率如何,你可以使用time/1

?- time(test(Street_lengths,Shortest)).
% 44 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
Street_lengths = [street(100, 'Storgatan'), street(200, 'Lillgatan'), street(100, 'Nygatan'), street(200, 'Kungsgatan')],
Shortest = [street(100, 'Storgatan'), street(100, 'Nygatan')] ;
% 17 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
false.

【讨论】:

  • 还要考虑到“Kungsgatan”并不是最短的街道。 “Storgatan”/“Nygatan”是。
猜你喜欢
  • 1970-01-01
  • 2017-03-08
  • 2014-08-09
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
  • 2016-08-12
  • 2020-04-17
相关资源
最近更新 更多