如何向DCG语法添加打印命令

时间:2015-06-17 22:54:03

标签: prolog dcg

我想在我的DCG语法中添加print命令,这是我的内容:

program( (R0 --> R) ) -->
    [begin],instructs(( R0 --> R )),[end].
instructs( ( R0 --> R ) ) -->
    instr(( R0 --> R )).
instructs( ( R0 --> R ) ) -->
    instr(( R0 --> R1 )),
    instructs(( R1 --> R )).
instr( ( R0 --> R ) ) -->
    [ dte], { R is 2*R0}.
instr( ( R0 --> R ) ) -->
    [ dto], { R is 2*R0 + 1}.
instr( ( R0 --> R ) ) -->
    [ halve], { R is R0 // 2}.

要添加print,我需要将R0 --> R更改为R0 --> OutTape,其中OutTape是该计划的输出。

我以为我可以做到以下几点:

program( (R0 --> OutTape) ) -->
  [begin],instructs(( R --> Tape )),[end].
instructs( ( R --> Tape ) ) -->
  instr(( R --> Tape )).
instructs( ( R --> Tape ) ) -->
  instr(( R --> Tape)),
  instructs(( R --> Tape )).
instr( ( R --> Tape ) ) -->
  [ dte], { R is 2*R}. % is this a legal term?
instr( ( R --> Tape ) ) -->
  [ dto], { R is 2*R + 1}.
instr( ( R --> Tape ) ) -->
  [ halve], { R is R // 2}.
instr( ( R --> Tape ) ) -->
    [ print], {append()}. % how to append R to Tape?

但我不知道如何将R附加到磁带上,请你指引我走向正确的方向?

1 个答案:

答案 0 :(得分:3)

在Prolog中,您无法重新分配变量。因此,R is R // 2之类的表达式会失败,因为在Prolog中,它在语义上说* R本身是整数除2,只有在R为0时才会为真。

同样,假设Tape是列表,则无法继续附加到同一列表(或磁带)。您必须提供磁带的先前状态,然后在打印到磁带后提供新状态。这需要在谓词中添加一个额外的参数来表示先前的磁带状态。在开始时,磁带为空,因此先前状态为[]

此外,虽然在您的问题中没有完全解释,但您可能希望将中间结果“打印”到磁带上。这意味着您还需要携带中间结果,以便在遇到print指令时可以将其“打印”到磁带上。那将是另一个论点。

program((R0 --> Tape)) -->
    % Initially, the tape is empty: []
    % OutTape is needed here, not "Tape" as originally shown
    % The second argument of `instructs` will have the final value,
    %   but we'll use `_` here since we only care about the values
    %   "printed" to the `OutTape`
    [begin], instructs((R0 --> OutTape), _, []), [end], { reverse(OutTape, Tape) }.
instructs((R0 --> Tape), R, PrevTape) -->
    instr((R0 --> Tape), R, PrevTape).
instructs((R0 --> Tape), R, PrevTape) -->
    instr((R0 --> NextTape), R1, PrevTape),   % NextTape is an intermediate tape state
    instructs((R1 --> Tape), R, NextTape).
instr((R0 --> PrevTape), R, PrevTape ) -->
    [dte], { R is 2*R0 }.
instr((R0 --> PrevTape), R, PrevTape) -->
    [dto], { R is 2*R0 + 1 }.
instr((R0 --> PrevTape), R, PrevTape) -->
    [halve], { R is R0 // 2 }.
instr((R --> [R|PrevTape]), R, PrevTape) -->
    [print].

另外,在上面的代码中,我使用reverse作为一种快速的方法,将磁带按顺序从左到右,尽管我不确定这是否符合您的要求。

| ?- phrase(program((3 --> Tape)), [begin, dte, dto, print, dte, print, end], []).

Tape = [13,26] ? ;

(1 ms) no
| ?-

<小时/> 结语

在此上下文中使用-->作为参数的函子,虽然在语法上是允许的,但有点不寻常,可能会令人困惑。使用逗号会更加规范:

program(R0, Tape) -->
    % Initially, the tape is empty: []
    % OutTape is needed here, not "Tape" as originally shown
    % The 3rd argument of `instructs` will have the final value,
    %   but we'll use `_` here since we only care about the values
    %   "printed" to the `OutTape`
    [begin], instructs(R0, OutTape, _, []), [end], { reverse(OutTape, Tape) }.
instructs(R0, Tape, R, PrevTape) -->
    instr(R0, Tape, R, PrevTape).
instructs((R0, Tape, R, PrevTape) -->
    instr(R0, NextTape, R1, PrevTape),   % NextTape is an intermediate tape state
    instructs(R1, Tape, R, NextTape).
instr(R0, PrevTape, R, PrevTape ) -->
    [dte], { R is 2*R0 }.
instr(R0, PrevTape, R, PrevTape) -->
    [dto], { R is 2*R0 + 1 }.
instr(R0, PrevTape, R, PrevTape) -->
    [halve], { R is R0 // 2 }.
instr(R, [R|PrevTape], R, PrevTape) -->
    [print].