【问题标题】:(prolog) Can i abstract all the permutations of this pattern instead of having to write each one individually?(序言)我可以抽象出这种模式的所有排列,而不必单独编写每个排列吗?
【发布时间】:2019-07-19 15:38:28
【问题描述】:

我刚开始使用 prolog,虽然我认为我已经很好地掌握了如何使用它,但我在这里碰壁了。我有这样的规则:

foo(A, B, C) :- bar(A, B), baz(C).
foo(A, B, C) :- bar(A, C), baz(B).
foo(A, B, C) :- bar(B, A), baz(C).
% etc, etc, for all permutations of `A, B, C`

但是,真正的规则有超过 3 个参数,并且将它们全部写出/生成它们会使我最终得到一个非常非常大的文件。有没有办法抽象出这种模式?

【问题讨论】:

    标签: prolog


    【解决方案1】:

    这就是我会做的:

    foo(A, B, C) :- 
        select(X, [A, B, C], L1),
        select(Y, L1, [Z]),
        bar(X, Y),
        baz(Z).
    

    我们的想法是,我们将使用 select/3(我最喜欢的谓词之一)重新调整这些参数的每个组合,然后对它们进行排列,我们可以将它们交给您关心的两个谓词。

    这样做可能更清楚,但 L2 总是会包含一个元素,所以我只是在上面的上一个子句中匹配了它:

    foo(A, B, C) :- 
        select(X, [A, B, C], L1),
        select(Y, L1, L2),
        select(Z, L2, _),
        bar(X, Y),
        baz(Z).
    

    编辑:让我们处理任意 N。首先,我们需要一个 select_n/4,它为我提供列表中的 N 个项目。我打算用 DCG 来写这个,因为它对我来说更容易:

    select_n([X|Xs], N) --> select(X), { succ(N0, N) }, select_n(Xs, N0).
    select_n([], 0) --> [].
    

    DCG 不仅是解析文本的好方法,也是将具有输入列表和输出列表的谓词串在一起的便捷方法。

    现在我们可以使用这个帮助器来构建更通用的谓词版本。假设您为简单起见列出一个列表:

    foo(L) :-
        select_n(BarArgs, 2, L, L1),
        select_n(BazArgs, 1, L1, []),
        apply(bar, BarArgs), 
        apply(baz, BazArgs).
    

    您可以将上面的21 替换为您想要的任何内容,大概基于您想要调用的barbaz 的数量。

    如果您不使用 SWI,或者只想使用 call/1,则必须先使用 univ 运算符 =.. 来构建术语,然后再使用 call/1

    foo(L) :-
        select_n(BarArgs, 2, L, L1),
        select_n(BazArgs, 1, L1, []),
        Bar =.. [bar|BarArgs],
        Baz =.. [baz|BazArgs],
        call(Bar),
        call(Baz).
    

    【讨论】:

    • 虽然我没试过,但=.. 会不会更有用。那么代码不依赖于参数的确切数量。
    • @GuyCoder 我已经编辑了答案,请告诉我你的想法。
    • 我明白为什么它不适用于长度为 3 的数组。我认为它们与大小无关,但我发现它们是用 1 和 @ 硬编码的987654339@.
    • 我认为它会像this
    • @GuyCoder 我认为真正的问题是当 N 超过相当小的数字时他将经历的组合爆炸,但我认为知道如何处理 Prolog 的评估是有效使用它的关键。
    【解决方案2】:

    另一个答案已经很好了。只是一些进一步的想法。

    在 Prolog 中,为您的微语言编写迷你解释器非常容易。这是解决原始问题的另一种(廉价)方法:

    foo(A, B, C) :-
        permutation([A, B, C], [X, Y, Z]),
        bar(X, Y), baz(Z).
    

    ...使用call,您可以参数化barbaz

    foo(Bar, Baz, A, B, C) :-
        permutation([A, B, C], [X, Y, Z]),
        call(Bar, X, Y), call(Baz, Z).
    

    这意味着您现在可以(按名称)将任何元数为 2 的谓词传递为 Bar,并将任何元数为 1 的谓词传递为 Baz。如果您有一个带有 3 个参数的谓词 bar_x,您可以提前绑定第一个参数。例如,您可以将foo/5 称为:

    ?- foo(bar(hello), baz, 1, 2, 3).
    

    这将为第一个解决方案评估 bar(hello, 1, 2)

    所以现在唯一重要的问题是,你的界面是什么?你想如何写下你的查询?您将三个参数作为单独的参数还是作为列表提供?你想参数化调用的谓词吗?以此类推。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-23
      • 2013-06-23
      • 2013-03-08
      • 1970-01-01
      • 2022-09-27
      相关资源
      最近更新 更多