以规范形式节省空间地读取字符

fal*_*lse 11 prolog iso-prolog

当使用字符(字符列表,因此长度为一的原子)来表示文本时,我们可以使用以下选项将它们写入术语中:

  • "First,"双引号列表表示法 (6.3.7) 是最有效的一种,至少需要n+2 个字符。double_quotes但只有当 Prolog 标志设置为chars时才能读回。

  • ['N',e,x,t,',']出现至少2n+1 个字符的列表符号。虽然它很好并且相对紧凑,但它意味着在写入其他数据时也会使用运算符,因为它是通过 启用的ignore_ops(false),并且这需要在读取时出现相同的运算符,这使得它非常脆弱。

  • '.'('L','.'(a,'.'(s,'.'(t,'.'(',',[])))))使用函数形式的规范表示法也适用于需要至少7n+2 个字符的列表。这是很多,但对于互操作性(包括与同一系统的互操作性)来说这是最好的,因为它既不依赖于标志double_quotes也不依赖于各种运算符声明。

以规范表示法写入字符可以在常量空间中完成。但对于阅读来说,情况就有点棘手了。毕竟,以 开头的序列'.'(a,也可以指代术语'.'(a,Further,b)。因此,天真的阅读必须等待(并使用空间),直到读入整个字符列表。另一方面,这似乎是一个安全的赌注,'.'(a,将是一个列表构造函数'.'(a,Further)。换句话说,

如何使用规范符号来读取术语,并使用恒定的辅助空间来读取其中的字符?

如果有帮助,请考虑条款sampleterm/1。因此,请考虑阅读以规范形式编写的所有此类术语。并且,如果您愿意,可以将其表述为 DCG。

sampleterm([]).
sampleterm(a).
sampleterm(b).
sampleterm('.'(E,Es)) :- % the real list constructor 
   sampleterm(E),
   sampleterm(Es).
sampleterm('.'(E,F,G)) :- % no list constructor
   sampleterm(E),
   sampleterm(F),
   sampleterm(G).
Run Code Online (Sandbox Code Playgroud)

如果这种节省空间的读取是可能的,那么支持诸如 Scryer 和 Trealla 之类的紧凑内部字符表示的系统甚至可以更进一步。

啊,以免我忘记我尝试过的:read/1确实如此,但目前并不理想。

rep*_*eat 4

以下简单的代码基于 Prolog 流。

它专注于从可重新定位流中读取“真实列表样本树”。

对于 (1) 不可重新定位的流和 (2) 处理,'.'/3我们回退到read/1.

主要谓词是read_sampleterm/1

read_sampleterm(术语) :-
    当前输入(S),
    (stream_property(S,重新定位(true)),
       流属性(S,位置(P)),
       ( aux_read_sampleterm_1(Term0),
          get_char('.') % 这太草率了!
       -> 正确
       ; 设置流位置(S,P),
          失败
       )
    -> 术语=术语0
    ; 读取(术语)% 回退
    )。

请注意,上面的代码很草率:在读取结束时,我们需要确保后面有 EOF 或不与 结合的字符'.'

实际的读取是通过以下辅助谓词完成的:

aux_read_sampleterm_1(术语):-
    get_char(Ch),
    aux_read_sampleterm_2(通道,术语,0)。% 使用索引

aux_read_sampleterm_2('\'',[X|Xs],N0) :-
    get_char('.'),
    get_char('\''),
    get_char('('),
    aux_read_sampleterm_1(X),
    get_char(','),
    N1 是 N0 + 1,
    get_char(Ch),
    aux_read_sampleterm_2(Ch,Xs,N1)。
aux_read_sampleterm_2('[',[],N) :-
    get_char(']'),
    eat_rparens(N)。
aux_read_sampleterm_2(a,a,N) :-
    eat_rparens(N)。
aux_read_sampleterm_2(b,b,N) :-
    eat_rparens(N)。

eat_rparens(N) :-
    (N > 0
    -> get_char(')'),
       N0 为 N-1,
       eat_rparens(N0)
    ; 真的
    )。

为了展示一些简单的用例,我们从文件中读取:

read_sampleterm_from_file(文件,术语) :-
    打开(文件,读取,S,[类型(文本)]),
    当前输入(S0),
    设置输入(S),
    read_sampleterm(Term0),
    设置输入(S0),
    关闭(S),
    期限 = 期限 0。

使用 GNU Prolog 1.5.0 的示例查询:

第一的,sample1.txt

'.'(a,'.'(b,[]))。

我们得到:

| ?- read_sampleterm_from_file('sample1.txt',T)。

T = [a,b]

是的

下一个,sample2.txt

'.'(a,'.'(b,a))。

我们得到:

| ?- read_sampleterm_from_file('sample2.txt',T)。

T = [a,b|a]

是的

sample3.txt接下来是:

'.'('.'(a,'.'(b,'.'(a,[]))),[])。

我们得到:

| ?- read_sampleterm_from_file('sample3.txt',T)。

T = [[a,b,a]]

(1 毫秒)是

请注意,运行上述测试是在没有“后备选项”的情况下运行的。

  • 当该术语恰好是这样的字符列表时,您只能读取具有恒定空间的规范字符列表。但即使是字符列表也无法有效读取。因此,这仅回答了一种非常具体的情况的问题。 (2认同)
  • 为了澄清起见,请考虑 `'.'('.'(a,'.'(b,'.'(c,[]))),[])`,它是 `["abc"]` 的规范 (2认同)