28、编写可移植的标准Prolog程序及DCG支持代码解析

编写可移植的标准Prolog程序及DCG支持代码解析

可移植标准Prolog程序编写要点

在编写可移植的标准Prolog程序时,兼容性文件的定义至关重要。一旦定义好兼容性文件,程序的其余部分就可以像在标准Prolog中一样使用相关谓词。

如果实现提供了某个谓词,但含义与标准不同,应尝试在兼容性文件中给出符合标准的定义。同时,要对程序和兼容性文件定义中的相关谓词进行重命名,以避免与实现提供的谓词冲突。虽然这样的程序可能不符合标准,但记录使其符合标准所需的转换并不困难。

在实践中,确定某个实现需要添加哪些标准Prolog谓词的过程,对于给定的实现或使用特定实现的特定站点,可能只需进行一次。

Prolog实现与标准的差异问题

Prolog实现有时会与标准存在差异,以下是一些需要注意的问题:
1. 字符处理 :标准Prolog中,字符是名称长度为1的原子。一些内置谓词处理字符,而另一些处理整数字符代码(如ASCII码),不同实现可能有所不同,且字符代码依赖于实现。
2. 字符串处理 :标准允许程序员为用双引号括起来的字符组成的Prolog术语选择一组固定的含义,但实现可能无法提供所有这些含义,因此在编写时应避免使用这种表示法。
3. 未知谓词处理 :标准允许程序员在调用未定义的谓词时选择一组固定的可能操作,但实现可能无法提供所有这些操作,因此不应依赖特定的行为。
4. 数据库更新 :当满足某个谓词的目标时,如果撤销该谓词的子句,可能会出现奇怪的情况。虽然标准规定了这种情况下应该发生的事情,但实现并不总是遵循,因此应避免编写会导致这种情况发生的程序。
5. 谓词名称 :内置谓词在不同实现中可能有不同的名称,例如 \+ 可能被称为 not 。最好使用标准名称,必要时创建兼容性文件,根据非标准名称定义这些谓词。
6. 运算符优先级 :不同实现之间运算符优先级可能不同,最好依赖标准优先级,必要时(如果可能)在加载程序之前显式调用 op/3 来设置使用的运算符。
7. 项比较 :某些实现可能不提供项比较功能,项比较的结果可能依赖于字符代码,因此在一定程度上依赖于实现,尽管标准对项比较的操作有一些限制。
8. 输入/输出 :过去的Prolog系统在处理文件输入/输出方面差异很大,并不总是基于流,文件名的内部结构也依赖于实现。
9. 指令 :不同实现中可用于影响程序加载方式的指令不同。
10. 咨询程序 :标准未指定为此提供哪些谓词,因此不同实现会有所不同。
11. 算术运算 :Prolog标准指定了一组可用于算术表达式的函子,但实现可能在处理整数和浮点数、处理错误等方面有所不同。
12. 出现检查 :标准规定Prolog程序不应能够创建循环项,但一些实现甚至为循环项提供特殊支持,为了可移植性,不应使用它们。

部分标准谓词的定义

为了帮助创建自己的兼容性文件,下面给出了一些标准Prolog内置谓词的简单定义。由于这些定义限制在Clocksin和Mellish核心Prolog子集内,因此不能完全符合标准的所有方面。特别是,在核心子集中无法创建错误条件,因此有时这些定义在标准规定应出现错误条件时会失败。这些定义应支持谓词的大多数正确使用,但如果使用不当,可能会有不同的行为。

以下是标准谓词及其基于非标准谓词的定义总结:
| 标准谓词 | 基于非标准谓词的定义 |
| — | — |
| atom_chars/2 | name/2 |
| number_chars/2 | name/2 |
| get_char/1 | get0/1 , name/2 |
| put_char/1 | put/1 , name/2 |
| dynamic/1 | (nothing) |
| close/1 | seeing/1 , see/1 , seen/0 , telling/1 , tell/1 , told/0 |
| current_input/1 | seeing/1 |
| current_output/1 | telling/1 |
| open/1 | seeing/1 , see/1 , telling/1 , tell/1 |
| set_input/1 | see/1 |
| set_output/1 | tell/1 |
| write_canonical/1 | display/1 |
| \+/1 | not/1 |
| number/1 | integer/1 |
| @=</2 , @>/2 , @>=/2 , @</2 | name/2 |

字符处理相关代码
% atom_chars/2
%
% Translate between atoms and lists of characters
%
% Known problems:
%
% tends to fail rather than produce an error
%
% converts a sequence of characters which happen to be from '0' to '9'
%
% into a number, not an atom
atom_chars(Atom,Chars) :-
    var(Atom), nonvar(Chars), !,
    '$$collect_codes'(Chars,Codes),
    name(Atom, Codes).
atom_chars(Atom,Chars) :-
    name(Atom, Codes),
    '$$cotlect_codes'(Chars,Codes).
'$$collect_codes'([Ch|Chs],[Co|Cos]) :-
    (nonvar(Chs); nonvar(Cos)), !,
    name(Ch,[Co]),
    '$$collect_codes'(Chs,Cos).
'$$collect_codes'( [],[]).

% number_chars/2
%
% Translate between numbers and lists of characters
%
% Known problems:
%
% tends to fail rather than produce an error
%
% in fact also produces characters from non-numbers
number_chars(Num,Chars) :-
    var(Num), nonvar(Chars), !,
    '$$collect_codes'(Chars, Codes),
    name(Num,Codes).
number_chars(Num,Chars) :-
    nonvar(Num),
    name(Num,Codes),
    '$$collect_codes'(Chars,Codes).

% get_char/1
%
% Single character input
%
% Known problems:
%
% The definition of '$$end_of_file_code' needs to be updated so
%
% that it represents the character code used for "end of file".
get_char(Char) :- get0(Code), '$$code_to_char'(Code,Char).
'$$code_to_char'(Code,Char) :-
    '$$end_of_file_code'(Code), !, Char=end_of_file.
'$$code_to_char'(Code,Char) :-
    name(Char,[Code]),
    '$$name_to_atom'(Char,Char1).
% some versions of 'name' create numbers in their first arguments
% make sure that the result really is an atom
'$$name_to_atom'(0,'0'):-!.
'$$name_to_atom'(1,'1'):-!.
'$$name_to_atom'(2,'2'):- !.
'$$name_to_atom'(3,'3'):-!.
'$$name_to_atom'(4,'4'):- !.
'$$name_to_atom'(5,'5'):-!.
'$$name_to_atom'(6,'6'):-!.
'$$name_to_atom'(7,'7'):-!.
'$$name_to_atom'(8,'8'):- !.
'$$name_to_atom'(9,'9'):- !.
'$$name_to_atom'(X,X).
'$$end_of_file_code'(-1).

% put_char/1
%
% Single character output
%
% Known problems:
%
% will fail if given a non-character atom (rather than produce an error)
put_char(X):- name(X,[C]), put(C).
指令相关代码
% dynamic/1
%
% Declare a set of
%
% Known problems:
%
% Does nothing. This at least means that dynamic directives will not
%
% create errors, but it does not address whatever might be required
%
% to allow a predicate to be dynamic in a given implementation.
?- op(1200,fx,':-').
?- op(1100,fx,dynamic).
dynamic(_).
流输入/输出相关代码
% The following predicates have to be used together
% NB the following predicates all make the assumption that the predicate
%
% '$$open'(Filename,Mode)
%
% (Filename an atom naming a file and Mode being 'read' or'write') is
% updated dynamically to reflect the files that are currently open for
% input and output.
%
% A stream is represented by a term '$$stream'(F) where F is the atom
% file name. The same structure is used for the streams user_input and
% user_output, with F = user_input or user_output.
%
% Known problems with this group of definitions:
%
% uses the file name inside the stream name - this convention cannot
%
% be relied on in Standard Prolog
%
% can only have one stream open at a time for a given file for input
%
% or output
%
% can't open a file called user_input or user_output
:- dynamic('$$open'/2).

% close/1
%
% Close a currently open stream
%
% Known problems:
%
% Fails if the stream is not open (rather than producing an error)
%
% Closes the file for both input and output (for whichever it is
%
% open for)
close(user_input):-!.
close(user_output):- !.
close('$$stream'(user_input)):- !.
close('$$stream'(user_output)):- !.
close('$$stream'(File)) :-
    '$$open'(File,Mode), !,
    '$$closefiles'(File).
'$$closefiles'(File) :-
    retract('$$open'(File,Mode)),
    '$$closefile'(File,Mode),
    fail.
'$$closefiles'(_).
'$$closefile'(File,read) :- !,
    seeing(Current),
    see(File), seen,
    see(Current).
'$$closefile'(File,write) :-
    telling(Current),
    tell(File), told,
    tell(Current).

% current_input/1
%
% Test what the current input stream is
current_input('$$stream'(F)) :- seeing(F).

% current_output/1
%
% Test what the current output stream is
current_output('$$stream'(F)) :- telling(F).

% open/3
%
% Open a file  for  input or output
%
% Known problems:
open(File,read,'$$stream'(File)) :- !,
    '$$close_if_open'(File,read), % close file if already open
    seeing(Old),
    % remember the current input
    see(File),
    % open again from the start
    assert('$$open'(File,read)),
    see(Old).
    % but don't change the current input
open(File,write,'$$stream'( File)) :-
    '$$close_if_open'(File,write), % close file if already open
    telling(Old),
    % remember the current output
    tell(File),
    % open again from the start
    assert('$$open'(File,write)),
    tell(Old).
    % but don't change the current input
'$$close_if_open'(File,Mode) :-
    retract('$$open'(File,Mode)), !,
    '$$closefile'(File,Mode).
'$$close_if_open'(File,_).

% set_input/1
%
% Change current input
set_input(user_input):- !,
    see(user).
set_input('$$stream'(user_input)):-!,
    see(user).
set_input('$$stream'(F)) :-
    '$$open'(F,read), !,
    see(F).

% set_output/1
%
% Change current output
set_output(user_output):- !,
    tell(user).
set_output('$$stream'(user_output)) :- !,
    tell(user).
set_output('$$stream'(F)) :-
    '$$open'(F,write), !,
    tell(F).
其他杂项代码
% write_canonical/1
%
% Write a term, ignoring operator declarations
write_canonical(X) :- display(X).

% \+/1
%
% Negation as failure
?- op(900,fy,\+).
\+ X :- not(X).

% number/1
%
% Test for  whether something is a number.
% Limitations: only succeeds for integers
number(X) :- integer(X).

% @<, @>, @=<, @>=
%
% Term comparison
%
% Limitations: only succeeds for atoms, assumes the Ascii character set
?- op(700,xfx,@<).
?- op(700,xfx,@>).
?- op(700,xfx,@=<).
?- op(700,xfx,@>=).
X @=< Y :- atom(X), atom(Y), X=Y, !.
X @=< Y :- X @< Y.
X @> Y :- Y @< X.
X @>= Y :- Y @=< X.
X @< Y :- atom(X), atom(Y), name(X,XC), name(Y,YC), '$$aless'(XC,YC).
'$$aless'([],[_|_]):- !.
'$$aless'([C|_],[C1 |_]):- C<C1, !.
'$$aless'([C|Cs],[C|Cs1]) :-
    '$$aless'(Cs,Cs1).
DCG支持代码

Prolog标准并未规定Prolog实现应提供将DCG规则转换为Prolog的功能,但实际上许多实现都提供了该功能。以下Prolog程序对于希望使用DCG但实现不支持此功能的人可能会有用。除了谓词 phrase/2 phrase/3 和(可更新的) C'/3 之外,它还提供了一个谓词 g/1 来咨询包含语法规则(可能还有普通Prolog)的文件。

g/1 谓词的工作方式是读取文件,并将其写入文件 dcg.tmp ,将任何语法规则转换为普通Prolog,然后正常咨询该文件。显然,用户不应将文件 dcg.tmp 用于其他用途。原始文件中的任何指令都将直接复制到 dcg.tmp 中,以便在咨询时遵守。由于在第一次读取文件时不遵守这些指令,因此在调用 g 之前必须进行读取文件所需的任何运算符声明。

% DCG code for Programming in Standard Prolog
%
% The following is part of Standard Prolog, but some Prolog
% systems may need it:
%
?- op(1200,xfx,-->).
%
% {...} is dealt with specially in the syntax of Standard Prolog,
% but in case it is not recognised, the following could be a way
% of having { and } be standard operators. However, any conjunctions
% and disjunctions inside {...} will have to be inside extra
% parentheses with spaces around the { and }, e.g. { (a(X), b(Y)) } .
%
?- op(901,fx,'{').
?- op(900,xf,'}').

% g(File)
%
% Consult a file File that may contain grammar rules. The
% predicate creates a new file dcg.tmp with the translated
% version of the original file and then consults that.
% The file can contain ordinary Prolog clauses as well, but
% any necessary operator declarations must be made before g is called
% (it does not obey any directives when the file is first read).
g(File) :-
    open(File,read,In),
    set_input(In),
    open('dcg.tmp', write,Out),
    set_output(Out),
    repeat,
    read(Term),
    output_with_translation(Term),
    Term = end_of_file,
    !,
    close(Out),
    close(In),
    consult('dcg.tmp').

% output_with_translation(Term)
%
% Outputs Term (in such a way that it can be read in as a clause)
% after translating it (if it happens to be a grammar rule)
output_with_translation(end_of_file):-!.
output_with_translation((X-->Y)) :- !,
    translate((X-->Y),Z),
    write_canonical(Z), write('.'), nl.
output_with_translation(X) :-
    write_canonical(X), write('.'), nl.

% translate(+In,-Out)
%
% Translate a grammar rule
%
translate(((LHS_in1,LHS_in2) --> RHS_in), (LHS_out:- RHS_out)) :- !,
    nonvar(LHS_in1),
    islist(LHS_in2),
    tag(LHS_in1,S0,Sn,LHS_out),
    make_connects(LHS_in2,Sn,S1,Conn),
    dcg_rhs(RHS_in,S0,S1,RHS_1),
    dcg_and(Conn,RHS_1,RHS_2),
    flatten2(RHS_2,RHS_out).
translate((LHS_in --> RHS_in), (LHS_out:- RHS_out)) :-
    nonvar(LHS_in),
    tag(LHS_in,S0,Sn,LHS_out),
    dcg_rhs(RHS_in,S0,Sn,RHS_1),
    flatten2(RHS_1,RHS_out).

% dcg_rhs(+RHS,SO,SI,-Translation)
%
% Translate the RHS of a grammar rule into a
% conjunction of Prolog goals. SO and S1 are
% variables to be used for the input and output
% list arguments of the whole conjunction (these
% are the variables used for the input and output
% list arguments for the head of the clause)
dcg_rhs(X, S0, S, phrase(X,S0,S)) :- var(X), !.
dcg_rhs((RHS_in1,RHS_in2),S0,Sn,RHS_out):- !,
    dcg_rhs(RHS_in1,S0,S1,RHS_out1),
    dcg_rhs(RHS_in2,S1,Sn,RHS_out2),
    dcg_and(RHS_out1,RHS_out2,RHS_out).
dcg_rhs((RHS_in1;RHS_in2),S0,Sn,(RHS_out1;RHS_out2)):- !,
    dcg_or(RHS_in1,S0,Sn,RHS_out1),
    dcg_or(RHS_in2,S0,Sn,RHS_out2).
dcg_rhs({RHS_in},S0,S0,RHS_in) :- !.
dcg_rhs(!,S0,S0,!) :- !.
dcg_rhs(RHS_in,S0,Sn,C) :-
    % terminal(s)
    islist(RHS_in), !,
    make_connects(RHS_in,S0,Sn,C).
dcg_rhs(RHS_in,S0,Sn,RHS_out):-
    % single non-terminal
    tag(RHS_in,S0,Sn,RHS_out).

%
% Auxiliary predicates
%

% dcg_or(+RHS,SO,SI,-Translation)
%
% As dcg_rhs, except for goals that will be part of a
% disjunction. dcg_rhs can instantiate the first list
% argument (SO) (by making it the same as the second
% or a list with some terminals in it), but that can't
% be done here (it will mess
% up the other disjuncts). Instead, if that happens, an
% explicit = goal is included in the output.
dcg_or(In,S0,Sn,Out) :-
    dcg_rhs(In,S1,Sn,Out1), % using new first list argument S1
    ( var(S1),
      \+ S1 == Sn, !,
      % if S1 has not been set
      S0=S1,
      %
      Out=Out1;
      % return what was computed (S1)
      Out=(S0=S1,Out1)). % otherwise link S0,S1 with an = goal

% Create a conjunction, flattening if possible
dcg_and(true,In,In) :- !.
dcg_and(In,true,In) :- !.
dcg_and(In1,In2,(In1,In2)).

% tag(+In,S0,Sl,-Out)
%
% In is a term representing a DCG non-terminal. Out
% is the result of adding S0 and S1 as extra arguments
tag(In,S0,Sn,Out) :-
    In=..[Predicate|Arguments],
    dcg_append(Arguments,[S0,Sn],New_arguments),
    Out=..[Predicate|New_arguments].

% flatten 2 (+Seq,-FSeq)
%
% Given a sequence of terms connected by','/2
% (possibly with embedded sequences), produces a
% "flattened" form
flatten2(In,In) :- var(In), !.
flatten2((In1,In2),Out1) :- !,
    flatten1(In1,Out1,Out2),
    flatten2(In2,Out2).
flatten2(In,In).
flatten1(In1,(In1,In2),In2) :-
    var(In1), !.
flatten1((In1,In2),Out1,In3):- !,
    flatten1(In1,Out1,Out2),
    flatten1(In2,Out2,In3).
flatten1(In1,(In1,In2),In2).

islist([]).
islist([_|_]).
dcg_append([],X,X).
dcg_append([X|L],L1,[X|L2]):- dcg_append(L,L1,L2).

% make_connects(+Terminals,SO,Sl,-Goals)
%
% Create the X' goals for a list of terminals. SO and SI
% are to be instantiated to the input and output list
% arguments.
make_connects([First|Rest],S0,Sn,Conns) :-
    nonvar(Rest), !,
    make_connects(Rest,S1,Sn,Cs),
    dcg_and('C'(S0,First,S1),Cs,Conns).
make_connects([],S,S,true).

% Predicates that can be called/redefined by the user
phrase(T, S):- phrase(T, S, []).
phrase(T, S0, S ) :- tag(T, S0, S, G), call(G).
'C'([W|Ws],W,Ws).

通过以上的代码和分析,我们可以更好地编写可移植的标准Prolog程序,并在实现不支持DCG规则转换时使用提供的代码来支持DCG。在实际应用中,要充分考虑不同实现之间的差异,确保程序的正确性和可移植性。

编写可移植的标准Prolog程序及DCG支持代码解析(续)

DCG支持代码详细解析
代码整体流程

下面通过一个mermaid流程图来展示 g/1 谓词处理包含语法规则文件的整体流程:

graph TD;
    A[开始] --> B[打开输入文件];
    B --> C[设置输入流为输入文件];
    C --> D[打开临时文件dcg.tmp];
    D --> E[设置输出流为临时文件];
    E --> F[循环读取输入文件内容];
    F --> G{是否为语法规则};
    G -- 是 --> H[翻译语法规则];
    G -- 否 --> I[直接写入临时文件];
    H --> J[将翻译后的规则写入临时文件];
    F --> K{是否到达文件末尾};
    K -- 否 --> F;
    K -- 是 --> L[关闭临时文件];
    L --> M[关闭输入文件];
    M --> N[咨询临时文件];
    N --> O[结束];

从这个流程图可以清晰地看到, g/1 谓词首先打开输入文件和临时文件,然后逐行读取输入文件内容。如果遇到语法规则则进行翻译后写入临时文件,普通内容则直接写入。当读取完整个文件后,关闭文件并咨询临时文件。

各关键谓词详细分析
  1. output_with_translation(Term)
    • 该谓词用于输出翻译后的内容。如果输入的 Term 是语法规则( X --> Y 形式),则调用 translate 进行翻译,然后将翻译结果以规范形式写入临时文件并换行。如果不是语法规则,则直接以规范形式写入并换行。
    • 代码示例:
output_with_translation(end_of_file):-!.
output_with_translation((X-->Y)) :- !,
    translate((X-->Y),Z),
    write_canonical(Z), write('.'), nl.
output_with_translation(X) :-
    write_canonical(X), write('.'), nl.
  1. translate(+In,-Out)
    • 此谓词用于翻译语法规则。对于不同形式的语法规则有不同的处理方式。当规则的左部有两个部分时,会进行一系列的处理,包括标记左部、创建连接、翻译右部等操作,最终将结果扁平化。对于普通的语法规则,也会进行类似的处理。
    • 代码示例:
translate(((LHS_in1,LHS_in2) --> RHS_in), (LHS_out:- RHS_out)) :- !,
    nonvar(LHS_in1),
    islist(LHS_in2),
    tag(LHS_in1,S0,Sn,LHS_out),
    make_connects(LHS_in2,Sn,S1,Conn),
    dcg_rhs(RHS_in,S0,S1,RHS_1),
    dcg_and(Conn,RHS_1,RHS_2),
    flatten2(RHS_2,RHS_out).
translate((LHS_in --> RHS_in), (LHS_out:- RHS_out)) :-
    nonvar(LHS_in),
    tag(LHS_in,S0,Sn,LHS_out),
    dcg_rhs(RHS_in,S0,Sn,RHS_1),
    flatten2(RHS_1,RHS_out).
  1. dcg_rhs(+RHS,SO,SI,-Translation)
    • 该谓词用于将语法规则的右部翻译为Prolog目标的合取。根据右部的不同形式(变量、合取、析取、花括号内内容、感叹号、列表、单个非终结符等)进行不同的处理。
    • 代码示例:
dcg_rhs(X, S0, S, phrase(X,S0,S)) :- var(X), !.
dcg_rhs((RHS_in1,RHS_in2),S0,Sn,RHS_out):- !,
    dcg_rhs(RHS_in1,S0,S1,RHS_out1),
    dcg_rhs(RHS_in2,S1,Sn,RHS_out2),
    dcg_and(RHS_out1,RHS_out2,RHS_out).
dcg_rhs((RHS_in1;RHS_in2),S0,Sn,(RHS_out1;RHS_out2)):- !,
    dcg_or(RHS_in1,S0,Sn,RHS_out1),
    dcg_or(RHS_in2,S0,Sn,RHS_out2).
dcg_rhs({RHS_in},S0,S0,RHS_in) :- !.
dcg_rhs(!,S0,S0,!) :- !.
dcg_rhs(RHS_in,S0,Sn,C) :-
    % terminal(s)
    islist(RHS_in), !,
    make_connects(RHS_in,S0,Sn,C).
dcg_rhs(RHS_in,S0,Sn,RHS_out):-
    % single non-terminal
    tag(RHS_in,S0,Sn,RHS_out).
  1. dcg_or(+RHS,SO,SI,-Translation)
    • 该谓词处理析取情况。它使用新的第一个列表参数 S1 调用 dcg_rhs 进行翻译。如果 S1 未被设置且不等于 Sn ,则将 S0 S1 进行绑定并返回计算结果;否则,在输出中添加一个 S0 = S1 的目标。
    • 代码示例:
dcg_or(In,S0,Sn,Out) :-
    dcg_rhs(In,S1,Sn,Out1), % using new first list argument S1
    ( var(S1),
      \+ S1 == Sn, !,
      % if S1 has not been set
      S0=S1,
      %
      Out=Out1;
      % return what was computed (S1)
      Out=(S0=S1,Out1)). % otherwise link S0,S1 with an = goal
  1. dcg_and(In1,In2,Result)
    • 用于创建合取。如果其中一个参数为 true ,则直接返回另一个参数;否则,将两个参数组合成 (In1, In2) 形式。
    • 代码示例:
dcg_and(true,In,In) :- !.
dcg_and(In,true,In) :- !.
dcg_and(In1,In2,(In1,In2)).
  1. tag(+In,S0,Sl,-Out)
    • 该谓词用于为DCG非终结符添加 S0 S1 作为额外参数。通过 =.. 操作将原项拆分为谓词和参数列表,然后将 S0 S1 添加到参数列表末尾,最后重新组合成新的项。
    • 代码示例:
tag(In,S0,Sn,Out) :-
    In=..[Predicate|Arguments],
    dcg_append(Arguments,[S0,Sn],New_arguments),
    Out=..[Predicate|New_arguments].
  1. flatten2(+Seq,-FSeq)
    • 用于将由逗号连接的项序列扁平化。如果输入是变量则直接返回;如果是嵌套的序列,则递归地进行扁平化处理。
    • 代码示例:
flatten2(In,In) :- var(In), !.
flatten2((In1,In2),Out1) :- !,
    flatten1(In1,Out1,Out2),
    flatten2(In2,Out2).
flatten2(In,In).
  1. make_connects(+Terminals,SO,Sl,-Goals)
    • 为终端列表创建目标。如果终端列表不为空,则递归地处理剩余部分,并将当前终端与输入输出列表建立连接。
    • 代码示例:
make_connects([First|Rest],S0,Sn,Conns) :-
    nonvar(Rest), !,
    make_connects(Rest,S1,Sn,Cs),
    dcg_and('C'(S0,First,S1),Cs,Conns).
make_connects([],S,S,true).
编写可移植Prolog程序的最佳实践

在编写可移植的标准Prolog程序时,我们可以遵循以下最佳实践:
1. 兼容性文件的使用
- 当实现提供的谓词含义与标准不同时,创建兼容性文件。在兼容性文件中给出符合标准的定义,并对相关谓词进行重命名,避免冲突。例如,若 \+ 在某些实现中被称为 not ,可以在兼容性文件中定义 \+ X :- not(X)
2. 遵循标准规范
- 尽量使用标准名称的谓词和运算符,依赖标准优先级。如使用标准的 atom_chars/2 而不是非标准的类似谓词,对于运算符优先级,必要时使用 op/3 进行显式设置。
3. 避免依赖特定实现特性
- 不要依赖特定实现的特殊功能,如某些实现对循环项的特殊支持。为了保证可移植性,应遵循标准规定,不使用会导致创建循环项的代码。
4. 处理差异情况
- 对于不同实现可能存在差异的方面,如字符处理、输入输出等,要进行充分的测试和处理。例如,在处理字符时,要考虑不同实现对字符代码和字符原子的处理方式。

总结

编写可移植的标准Prolog程序需要充分考虑不同实现之间的差异。通过创建兼容性文件、遵循标准规范、避免依赖特定实现特性等方法,可以提高程序的可移植性。同时,对于DCG规则的支持,当实现不提供转换功能时,可以使用上述提供的代码进行支持。在实际应用中,要根据具体情况灵活运用这些知识和代码,确保程序在不同的Prolog实现中都能正确运行。

通过本文的详细介绍,我们对编写可移植的标准Prolog程序以及DCG支持代码有了更深入的理解。希望这些内容能帮助开发者在Prolog编程中更加得心应手,编写出高质量、可移植的程序。

根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值