组合生成器结果并将结果写入流 [英] Combing generator results and writing result to stream

查看:68
本文介绍了组合生成器结果并将结果写入流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我可以生成表达式树.

expression_tree([_|N_s],N_s, [number(0)]).expression_tree([_|N_s0],N_s1, [op(neg),[E1]]) :-表达式树(N_s0,N_s1,E1).expression_tree([_|N_s0],N_s2, [op(add), [E1, E2]]) :-表达式树(N_s0,N_s1,E1),表达式树(N_s1,N_s2,E2).生成表达式(N_c,E):-长度(N,N_c),表达式树(N,[],E).?- 生成表达式(N,E).N = 1,E = [数字(0)];N = 2,E = [op(neg), [[number(0)]]] ;N = 3,E = [op(neg), [[op(neg), [[number(0)]]]]] ;N = 3,E = [op(add), [[number(0)], [number(0)]]] ;N = 4,E = [op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]] ;N = 4,E = [op(neg), [[op(add), [[number(0)], [number(0)]]]]] ;N = 4,E = [op(add), [[number(0)], [op(neg), [[number(0)]]]]] ;N = 4,E = [op(add), [[op(neg), [[number(0)]]], [number(0)]]];N = 5,E = [op(neg), [[op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]]]]

其中 N 是树的节点数.

我也可以独立生成序列号.

sequence_number(Sequence_number) :-序列号(1,序列号).序列号(I,I).序列号(I,K):-J 是 I + 1,序列号(J​​,K).?- 序列号(N).N = 1 ;N = 2 ;N = 3 ;N = 4 ;N = 5 ;N = 6

我也可以生成和输出表达式,但不能使用正确的序列号

print_expression(Stream, Prefix, Suffix, Sequence_number, E) :-写(流,前缀),格式(流,'~|~`0t~d~7+',序列号),写(流,,"),写(流,E),写(流,后缀),nl(流).打印表达式_a(流,前缀,后缀,序列号,N):-生成表达式(N,E),打印表达式(流、前缀、后缀、序列号、E).打印表达式_a :-流 = user_output,前缀 = '(',后缀 = ')',序列号 = 1,N = 4,print_expressions_a(流,前缀,后缀,序列号,N).


?- print_expressions_a.(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;(0000001, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])真的 ;(0000001, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])真的 ;(0000001, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])真的 ;错误的.

注意序列号都是0000001.

这是生成选择点,所以我使用 forall

修改了它

print_expressions_b(Stream, Prefix, Suffix, Sequence_number, N) :-对所有人(生成表达式(N,E),打印表达式(流,前缀,后缀,序列号,E)).打印表达式_b :-流 = user_output,前缀 = '(',后缀 = ')',序列号 = 1,N = 4,打印表达式_b(流,前缀,后缀,序列号,N).?- 打印表达式_b.(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])(0000001, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])(0000001, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])(0000001, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])真的.

这仍然是错误的.

<小时>

我寻求的输出是

(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])(0000002, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])(0000003, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])(0000004, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])

其中每个序列号都是唯一的,并且从 01 开始是连续的,并且可以写入文件.对于此示例,流设置为 user_output 以简化问题.

如果我将序列号生成器添加到组合中

print_expressions_c(Stream, Prefix, Suffix, N) :-生成表达式(N,E),序列号(序列号),打印表达式(流、前缀、后缀、序列号、E).打印表达式_c :-流 = user_output,前缀 = '(',后缀 = ')',N = 4,print_expressions_c(流,前缀,后缀,N).?- 打印表达式_c.(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;(0000002, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;(0000003, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;(0000004, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;(0000005, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])真的 ;

序列号现在是正确的,但永远不会生成新的表达式,因为序列号生成器使用选择点来生成下一个序列号,因此谓词 sequence_number 不会回溯到generate_expression 谓词以获取新表达式.

那么,我可以连续使用两个依赖回溯的生成器吗?如果是这样,如何?

补充

这与我之前关于树生成器的问题有关.
我知道这应该用 来完成,并且应该更改数据结构,但是当我试图理解这一点时,以这种方式看待它更容易理解.

相关 SO 问题

解决方案

撤回回溯

总结一下这个问题,你想:

  • 使用迭代深化的生成器生成表达式
  • 编号每个具有连续整数的解决方案.

因此,您面临的核心问题是保留信息而不是回溯.

这在纯 Prolog 中当然不可能:这样做会破坏我们期望从关系中获得的最基本的属性,特别是我们期望回溯撤销 在当前计算分支中发生的一切.

因此,一个纯粹的解决方案是消除回溯

我不是在开玩笑:我们现在将改变对解决方案的整个搜索,以便找到每个解决方案没有回溯,即使程序看起来as ifem> 它使用了回溯.事实上,程序甚至会保持不变,但我们对它的解释与普通 Prolog 不同.这种策略允许我们随身携带一个计数器,并为我们找到的每个解决方案配备连续的整数.

本质上,我现在在 Prolog 内实现回溯,也就是说,我正在使用 Prolog 的内置回溯机制实现回溯,这样我就可以自由地随心所欲地扩展它.

具体化回溯

<块引用>

to reify = 使其成为一件事"(来自拉丁语:res, rei f. = 物质、事物、事件)

首先,我将以不同的方式表示整个程序,以便更容易推理.我将使用避免常规 Prolog 目标的默认表示,而是使用目标列表.我会将每个子句表示为以下形式的事实:

<预>head_body(Head, [Goal1,Goal2,...,Goaln]).

这种变化纯粹是语法上的(尽管它对我们程序中的进一步处理有很大帮助),并且可以轻松实现自动化:

<预>head_body(expression_tree([_|N_s],N_s, [number(0)]), []).head_body(expression_tree([_|N_s0],N_s1, [op(neg),[E1]]),[表达式树(N_s0,N_s1,E1)]).head_body(expression_tree([_|N_s0],N_s2, [op(add), [E1, E2]]),[表达式树(N_s0,N_s1,E1),表达式_树(N_s1,N_s2,E2)]).

我们可以使用元解释器解释这个程序,如下所示:

<预>mi([G-[]|_], G).mi([Gs0|Rest], G) :-findall(G0-Body, (Gs0 = G0-[First|Others],head_body(第一,Body0),append(Body0, Others, Body)), Nexts, Rest),mi(下一个,G).

注意,这个解释器在搜索解决方案时不再发生回溯,除了收集所有匹配的子句,实际上报告任何解决方案,这只是接口的一部分,而不是核心的一部分.;逻辑.

另请注意,append/3 调用可以通过在子句表示中使用列表差异来消除.我将此作为一个非常简单的练习.

为了使用这个解释器,我们将主谓词更改为:

<预>生成表达式(N_c,E):-长度(N,N_c),mi([E-[expression_tree(N,[],E)]], E).

示例查询:

<预>?- generate_expression(N, E).N = 1,E = [数字(0)];N = 2,E = [op(neg), [[number(0)]]] ;N = 3,E = [op(neg), [[op(neg), [[number(0)]]]]] ;N = 3,E = [op(add), [[number(0)], [number(0)]]] ;N = 4,E = [op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]] .

这与您已有的等价,因此目前没有太大帮助.顺便说一句,也许现在是摆脱这种我们有足够的括号了吗"符号的好时机,以便未来的解决方案更容易阅读.例如,考虑用 op_arguments/2 形式的术语来表示表达式,或者更好的简单的 Prolog 术语 (+)/2 等形式.

列举解决方案

现在回到重点:使用元解释器的主要优点是它让我们改变Prolog 执行此类程序的方式.

在我们的例子中,现在是引入计数器来解决问题的时候了.我们的第一次尝试可能如下所示:

<预>mi(Alts0, S0, S, G) :-( Alts0 = [G0-[]|休息] ->( S #= S0,G = G0;S1 #= S0 + 1,mi(休息,S1,S,G));Alts0 = [Gs0|休息],findall(G0-Body, ( Gs0 = G0-[First|Others],head_body(第一,Body0),append(Body0, Others, Body)), Alts, Rest),mi(Alts, S0, S, G)).

调用谓词如下所示:

<预>generate_expression(N_c, S, E) :-长度(N,N_c),mi([E-[expression_tree(N,[],E)]], 0, S, E).

几乎解决了问题,但我们仍然有以下问题:

<预>?- 生成表达式(_, S, _).S = 0 ;S = 0 ;S = 0 ;S = 1;S = 0 ;S = 1 ;S = 2 ;S = 3;S = 0 ;S = 1 ;S = 2 ;S = 3 ;S = 4 ;S = 5 ;S = 6 ;S = 7 ;S = 8;S = 0 ;S = 1 .

因此,枚举了解决方案,但仍然回溯:回溯发生在 length/2 中,并且对于正在尝试的每个新长度,计数器是 重置.

从一开始就公平

因此,我们现在更改解释器以从一开始就实施公平计算策略.公平,我们的意思是所有存在的解决方案最终都会找到.

迭代深化就是这样一种策略.我将此留作练习,并在此示例中实现广度优先搜索.获得广度优先搜索很容易:我们只需追加新的替代方案.事实上,既然我们现在要实现公平性作为解释器的一个基本属性,我们也可以简化程序来阅读:

<预>head_body(expression_tree([number(0)]), []).head_body(expression_tree([op(neg), [E1]]),[表达式树(E1)]).head_body(expression_tree([op(add), [E1, E2]]),[表达式树(E1),表达式树(E2)]).

一个公平的元解释器,实现广度优先搜索枚举解决方案:

<预>mi(Alts0, S0, S, G) :-( Alts0 = [G0-[]|休息] ->( S #= S0,G = G0;S1 #= S0 + 1,mi(休息,S1,S,G));Alts0 = [Gs0|休息],findall(G0-Body, ( Gs0 = G0-[First|Others],head_body(第一,Body0),append(Body0, Others, Body)), Alts1),追加(休息,Alts1,Alts),mi(Alts, S0, S, G)).

我们的主谓词:

<预>generate_expression(S, E) :-mi([E-[expression_tree(E)]], 0, S, E).

我们开始:

<预>?- generate_expression(S, E).S = 0,E = [数字(0)];S = 1,E = [op(neg), [[number(0)]]] ;S = 2,E = [op(neg), [[op(neg), [[number(0)]]]]] ;S = 3,E = [op(add), [[number(0)], [number(0)]]] ;S = 4,E = [op(neg), [[op(neg), [[op(neg), [[...]]]]]]]];S = 5,E = [op(neg), [[op(add), [[number(0)], [number(0)]]]]] ;S = 6,E = [op(add), [[number(0)], [op(neg), [[number(0)]]]]] ;S = 7,E = [op(add), [[op(neg), [[number(0)]]], [number(0)]]] .

保持纯洁!

使用这种纯粹的方法来解决问题给了我们一些希望将其推广到其他组合器,因为可以相对孤立地解决不同的问题,并且原始程序可以保持它们的方式 

另请注意,我让 toplevel 进行打印.如有必要,我可以随时使用不纯谓词编写这些解决方案,但首先,每个解决方案都可用作谓词参数,我实际上可以在 in Prolog 中对其进行推理.

Currently I can generate expression trees.

expression_tree([_|N_s],N_s, [number(0)]).
expression_tree([_|N_s0],N_s1, [op(neg),[E1]]) :-
    expression_tree(N_s0,N_s1, E1).
expression_tree([_|N_s0],N_s2, [op(add), [E1, E2]]) :-
    expression_tree(N_s0,N_s1, E1),
    expression_tree(N_s1,N_s2, E2).

generate_expression(N_c, E) :-
    length(N, N_c),
    expression_tree(N,[], E).

?- generate_expression(N,E).
N = 1,
E = [number(0)] ;
N = 2,
E = [op(neg), [[number(0)]]] ;
N = 3,
E = [op(neg), [[op(neg), [[number(0)]]]]] ;
N = 3,
E = [op(add), [[number(0)], [number(0)]]] ;
N = 4,
E = [op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]] ;
N = 4,
E = [op(neg), [[op(add), [[number(0)], [number(0)]]]]] ;
N = 4,
E = [op(add), [[number(0)], [op(neg), [[number(0)]]]]] ;
N = 4,
E = [op(add), [[op(neg), [[number(0)]]], [number(0)]]] ;
N = 5,
E = [op(neg), [[op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]]]]

where N is the number of nodes for the tree.

I can also independently generate sequence numbers.

sequence_number(Sequence_number) :-
    sequence_numbers(1, Sequence_number).

sequence_numbers(I, I).
sequence_numbers(I, K) :-
    J is I + 1,
    sequence_numbers(J, K).

?- sequence_number(N).
N = 1 ;
N = 2 ;
N = 3 ;
N = 4 ;
N = 5 ;
N = 6 

I can also generate and output the expressions but not with the correct sequence numbers

print_expression(Stream, Prefix, Suffix, Sequence_number, E) :-
    write(Stream,Prefix),
    format(Stream, '~|~`0t~d~7+', Sequence_number),
    write(Stream,", "),
    write(Stream,E),
    write(Stream,Suffix),
    nl(Stream).

print_expressions_a(Stream, Prefix, Suffix, Sequence_number, N) :-
    generate_expression(N, E),
    print_expression(Stream, Prefix, Suffix, Sequence_number, E).

print_expressions_a :-
    Stream = user_output,
    Prefix = '(',
    Suffix = ')',
    Sequence_number = 1,
    N = 4,
    print_expressions_a(Stream, Prefix, Suffix, Sequence_number, N).


?- print_expressions_a.
(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;
(0000001, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])
true ;
(0000001, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])
true ;
(0000001, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])
true ;
false.

Notice the sequence numbers are all 0000001.

Which is generating choice-points, so I modified it using forall

print_expressions_b(Stream, Prefix, Suffix, Sequence_number, N) :-
    forall(
        generate_expression(N, E),
        print_expression(Stream, Prefix, Suffix, Sequence_number, E)
    ).

print_expressions_b :-
    Stream = user_output,
    Prefix = '(',
    Suffix = ')',
    Sequence_number = 1,
    N = 4,
    print_expressions_b(Stream, Prefix, Suffix, Sequence_number, N).

?- print_expressions_b.
(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
(0000001, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])
(0000001, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])
(0000001, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])
true.

which is still wrong.


The output I seek is

(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
(0000002, [op(neg),[[op(add),[[number(0)],[number(0)]]]]])
(0000003, [op(add),[[number(0)],[op(neg),[[number(0)]]]]])
(0000004, [op(add),[[op(neg),[[number(0)]]],[number(0)]]])

Where each sequence number is unique and sequential starting from 0 or 1 and can be written to a file. For this example the stream is set to user_output to simplify the question.

If I add the sequence number generator into the mix

print_expressions_c(Stream, Prefix, Suffix, N) :-
    generate_expression(N, E),
    sequence_number(Sequence_number),
    print_expression(Stream, Prefix, Suffix, Sequence_number, E).

print_expressions_c :-
    Stream = user_output,
    Prefix = '(',
    Suffix = ')',
    N = 4,
    print_expressions_c(Stream, Prefix, Suffix, N).

?- print_expressions_c.
(0000001, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;
(0000002, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;
(0000003, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;
(0000004, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;
(0000005, [op(neg),[[op(neg),[[op(neg),[[number(0)]]]]]]])
true ;

the sequence numbers are now correct, but new expressions are never generated because the sequence number generator is using a choice point to generate the next sequence number and so the predicate sequence_number, does not backtrack to the generate_expression predicate to get a new expression.

So, can I use two generators that rely on backtracking in succession? If so, how?

Supplement

This is related to my earlier questions on tree generators.
I am aware that this should be done with , and that the data structure should be changed, but while I am trying to understand this, seeing it this way is easier to comprehend.

Related SO Questions

解决方案

Retracting backtracking

To summarize the question, you would like to:

  • generate expressions using a generator that uses iterative deepening
  • number each solution with consecutive integers.

Thus, the core problem you are facing is preserving information over backtracking.

This is of course impossible in pure Prolog: Doing so would destroy the most elementary properties we expect from relations, in particular our expectation that backtracking undoes everything that happened in the current branch of the computation.

A pure solution, therefore, is to eliminate backtracking!

I'm not joking: We will now change the entire search for solutions in such a way that each solution is found without backtracking, even though the program looks as if it used backtracking. In fact, the program will even stay the same, but we interpret it differently than plain Prolog would do it. This strategy allows us to carry a counter with us, and equip each solution we find with consecutive integers.

In essence, I am now implementing backtracking within Prolog, i.e., I am implementing backtracking without using Prolog's built-in mechanism for backtracking, so that I am free to extend it as I want.

Reifying backtracking

to reify = "to make it a thing" (from Latin: res, rei f. = matter, thing, affair)

First, I will represent the whole program differently, so that it easier to reason about it. The representation I shall use avoids the defaulty representation for regular Prolog goals, and instead uses lists of goals. I will represent each clause as a fact of the form:

head_body(Head, [Goal1,Goal2,...,Goaln]).

This change is purely syntactical (even though it helps enormously for further processing within our programs), and can be easily automated:

head_body(expression_tree([_|N_s],N_s, [number(0)]), []).
head_body(expression_tree([_|N_s0],N_s1, [op(neg),[E1]]),
          [expression_tree(N_s0,N_s1, E1)]).
head_body(expression_tree([_|N_s0],N_s2, [op(add), [E1, E2]]),
          [expression_tree(N_s0,N_s1, E1),
           expression_tree(N_s1,N_s2, E2)]).

We can interpret this program with a meta-interpreter like the following:

mi([G-[]|_], G).
mi([Gs0|Rest], G) :-
        findall(G0-Body, (Gs0 = G0-[First|Others],
                          head_body(First, Body0),
                          append(Body0, Others, Body)), Nexts, Rest),
        mi(Nexts, G).

Note that backtracking no longer occurs in this interpreter in the search for solutions, except for collecting all matching clauses, and actually reporting any solutions, which is only part of the interface but not of the core logic.

Note also that the append/3 call can be eliminated by using list differences in the clause representation. I leave this as a very easy exercise.

To use this interpreter, we change our main predicate to read:

generate_expression(N_c, E) :-
        length(N, N_c),
        mi([E-[expression_tree(N,[],E)]], E).

Sample query:

?- generate_expression(N, E).
N = 1,
E = [number(0)] ;
N = 2,
E = [op(neg), [[number(0)]]] ;
N = 3,
E = [op(neg), [[op(neg), [[number(0)]]]]] ;
N = 3,
E = [op(add), [[number(0)], [number(0)]]] ;
N = 4,
E = [op(neg), [[op(neg), [[op(neg), [[number(0)]]]]]]] .

This is equivalent to what you already have, and so it does not help a lot currently. By the way, maybe it is now a good time to get rid of this "have we got enough brackets yet" notation, so that future solutions are a bit easier to read. Consider for example terms of the form op_arguments/2 to represent expressions, or better yet simply Prolog terms of the form (+)/2 etc.

Enumerating solutions

Now back to the main point: The key advantage of using a meta-interpreter is that it lets us change how plain Prolog would execute such programs.

In our case, now is the time to introduce a counter for solutions. Our first attempt could look like this:

mi(Alts0, S0, S, G) :-
        (   Alts0 = [G0-[]|Rest] ->
            (   S #= S0,
                G = G0
            ;   S1 #= S0 + 1,
                mi(Rest, S1, S, G)
            )
        ;   Alts0 = [Gs0|Rest],
            findall(G0-Body, ( Gs0 = G0-[First|Others],
                               head_body(First, Body0),
                               append(Body0, Others, Body)), Alts, Rest),
            mi(Alts, S0, S, G)
        ).

With the invoking predicate looking like this:

generate_expression(N_c, S, E) :-
        length(N, N_c),
        mi([E-[expression_tree(N,[],E)]], 0, S, E).

This almost solves the issue, but we still have the following problem:

?- generate_expression(_, S, _).
S = 0 ;
S = 0 ;
S = 0 ;
S = 1 ;
S = 0 ;
S = 1 ;
S = 2 ;
S = 3 ;
S = 0 ;
S = 1 ;
S = 2 ;
S = 3 ;
S = 4 ;
S = 5 ;
S = 6 ;
S = 7 ;
S = 8 ;
S = 0 ;
S = 1 .

So, solutions are enumerated, but there's still backtracking: The backtracking happens in length/2, and for each new length that is being tried, the counter is reset.

Fair from the start

We therefore now change the interpreter to implement a fair computation strategy right from the start. By fair, we mean that every solution that exists is eventually found.

Iterative deepening is one such strategy. I leave this as an exercise, and implement breadth-first search in this example. Obtaining breadth-first search is easy: We simply append new alternatives. In fact, since we are now about to implement fairness as a fundamental property of the interpreter, we can also simplify the program to read:

head_body(expression_tree([number(0)]), []).
head_body(expression_tree([op(neg), [E1]]),
          [expression_tree(E1)]).
head_body(expression_tree([op(add), [E1, E2]]),
          [expression_tree(E1),expression_tree(E2)]).

A fair meta-interpreter, implementing breadth-first search and enumerating solutions:

mi(Alts0, S0, S, G) :-
        (   Alts0 = [G0-[]|Rest] ->
            (   S #= S0,
                G = G0
            ;   S1 #= S0 + 1,
                mi(Rest, S1, S, G)
            )
        ;   Alts0 = [Gs0|Rest],
            findall(G0-Body, ( Gs0 = G0-[First|Others],
                               head_body(First, Body0),
                               append(Body0, Others, Body)), Alts1),
            append(Rest, Alts1, Alts),
            mi(Alts, S0, S, G)
        ).

Our main predicate:

generate_expression(S, E) :-
        mi([E-[expression_tree(E)]], 0, S, E).

And here we go:

?- generate_expression(S, E).
S = 0,
E = [number(0)] ;
S = 1,
E = [op(neg), [[number(0)]]] ;
S = 2,
E = [op(neg), [[op(neg), [[number(0)]]]]] ;
S = 3,
E = [op(add), [[number(0)], [number(0)]]] ;
S = 4,
E = [op(neg), [[op(neg), [[op(neg), [[...]]]]]]] ;
S = 5,
E = [op(neg), [[op(add), [[number(0)], [number(0)]]]]] ;
S = 6,
E = [op(add), [[number(0)], [op(neg), [[number(0)]]]]] ;
S = 7,
E = [op(add), [[op(neg), [[number(0)]]], [number(0)]]] .

Stay pure folks!

Using this pure approach to solve the issue gives us some hope to generalize this to other combinators, since the different concerns can be addressed in comparative isolation, and the original programs can stay the way they are.

Note also that I let the toplevel do the printing. If necessary, I can always write these solutions anywhere I want using impure predicates, but first and foremost each solution is available as a predicate argument that I can actually reason about within Prolog.

这篇关于组合生成器结果并将结果写入流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆