【问题标题】:Prolog subgroup of list of size n大小为 n 的列表的 Prolog 子组
【发布时间】:2019-01-19 23:40:59
【问题描述】:

我正在尝试创建一个规则来确定一个列表是否是另一个列表的大小为 n 的子列表。

isSubgroup/3
isSubgroup(+Subgroup, +Group, +N)

例如,isSubgroup([1, 2, 4], [1, 2, 3, 4, 5], 3) 将返回 True

但是,isSubgroup([4, 2, 1], [1, 2, 3, 4, 5], 3) 会返回 False(因为顺序不同)

我想检查子组的每个成员是否是大组的成员,但这会忽略顺序。

这个想法可行吗?

【问题讨论】:

  • 提示:尝试提出一个归纳定义。
  • 另一个提示:先在纸上做,然后把说明写下来,就好像你把它交给别人去做一样。然后将其翻译成 Prolog。很可能你可以写下这些步骤,但在将其翻译成 Prolog 时会遇到麻烦。这可能就是您想要发布的问题,作为此问题的更新。
  • @CapelliC:那很可能是 Turing,而 Turing 并不是唯一的。事实上,Dijkstra 自己几乎从未在机器上工作过,一切都写在纸上。
  • 提供额外提示:您可以同时扫描两个列表,以检查两个列表中的当前元素是否相同。如果是这样,请考虑两个列表的下一个元素;如果不是,请考虑子组列表的相同元素和组列表的下一个元素。

标签: prolog


【解决方案1】:

真的,试着写一个归纳关系。同时,library(yall) 加上 library(apply) 可以组成一个班轮:

isSubgroup(S,G,N) :- length(S,N),
    foldl({G}/[E,P,X]>>(nth1(X,G,E),X>=P),S,1,_F).

【讨论】:

  • @GuyCoder:好吧,只是等到有人最终发布它,然后再提交也不是一个好主意。但是 CapelliC 在这里设法用无法提交的答案来回答问题,或者至少在没有问题出现的情况下回答问题。
  • @WillemVanOnsem 我一直很感激他能够做到这一点。
【解决方案2】:

正如@WillemVanOnsem 所建议的,一个归纳解决方案:

subGroups([], []).

subGroups([X|Xs], [X|Ys]):-
    subGroups(Xs, Ys).

subGroups(Xs, [_|Ys]):-
    subGroups(Xs, Ys).

subGroupsN(Options, N, Solution) :-
    length(Solution, N),
    subGroups(Solution, Options).

【讨论】:

  • 虽然命名相当混乱。一个更好的名字可能是subGroupsOf。并且,subGroupsNOf 具有交换的参数顺序。 Prolog 中的一个传统 (?) 是命名谓词,使得 p(A,B) 读作“A (is) p B”。例如,father 不清楚,但 fatherOf 是。
【解决方案3】:

我们可以通过归纳定义来定义这个预测。 SubgroupGroup 的子组,如果:

  1. Subgroup 是一个空列表;
  2. Subgroup 的第一个元素与Group 的第一个元素相同,Subgroup 的其余部分是Group 其余部分的子组;
  3. SubgroupGroup 其余部分的子组。

我们需要相应地更新N,这样,如果Subgroup为空,则长度为0

isSubgroup([], _, 0).              %% (1)
isSubgroup([H|TS], [H|TG], N) :-   %% (2)
    N1 is N-1,
    isSubgroup(TS, TG, N1).
isSubgroup(S, [_|TG], N) :-        %% (3)
    isSubgroup(S, TG, N).

然而,上述结果导致 same 子组的重复值。这是因为我们可以通过多种方式满足谓词。例如,如果我们调用:

isSubgroup([], [1,2], 0).

那么它通过事实 (1) 得到满足,但最后一个子句 (3) 也用 isSubgroup([], [1], 0). 调用它,然后它将通过事实 (1) 得到满足,等等。

我们可以通过使最后一个子句更具限制性来避免这种情况:

isSubgroup([], _, 0).              %% (1)
isSubgroup([H|TS], [H|TG], N) :-   %% (2)
    N1 is N-1,
    isSubgroup(TS, TG, N1).
isSubgroup([HS|TS], [_|TG], N) :-  %% (3)
    isSubgroup([HS|TS], TG, N).

以上适用于给定的“方向”(所有参数都应接地,都是“输入”)。但通常人们也想在其他方向使用谓词。当我们使用参数作为“输出”时,我们可以实现一个基本工作的版本,并且仍然使用 tail-call optimization (TCO)

isSubgroup(S, G, N) :-
    isSubgroup(S, G, 0, N).

isSubgroup([], _, L, L).              %% (1)
isSubgroup([H|TS], [H|TG], L, N) :-   %% (2)
    L1 is L+1,
    isSubgroup(TS, TG, L1, N).
isSubgroup([HS|TS], [_|TG], L, N) :-  %% (3)
    isSubgroup([HS|TS], TG, L, N).

例如:

?- isSubgroup([1,4,2], G, N).
G = [1, 4, 2|_2974],
N = 3 ;
G = [1, 4, _2972, 2|_2986],
N = 3 ;
G = [1, 4, _2972, _2984, 2|_2998],
N = 3 ;
G = [1, 4, _2972, _2984, _2996, 2|_3010],
N = 3 .

因此,Prolog 能够提出以[1,4,2] 为子组的组,并且能够确定子组的长度N

我们也可以反方向查询:

?- isSubgroup(S, [1,4,2], N).
S = [],
N = 0 ;
S = [1],
N = 1 ;
S = [1, 4],
N = 2 ;
S = [1, 4, 2],
N = 3 ;
S = [1, 2],
N = 2 ;
S = [4],
N = 1 ;
S = [4, 2],
N = 2 ;
S = [2],
N = 1 ;
false.

Prolog 可以针对给定组 [1,4,2] 穷举枚举所有可能的子组,以及 N 该子组的长度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    • 2020-07-11
    • 2018-11-08
    • 2019-08-07
    相关资源
    最近更新 更多