在Prolog中压缩字符串列表

在Prolog中压缩字符串列表,prolog,artificial-intelligence,translation,grammar,dcg,Prolog,Artificial Intelligence,Translation,Grammar,Dcg,我正在编写一个Lisp到C的翻译程序,我在处理字符串方面有问题。这是一段将一元Lisp函数转换为C等效函数的代码: define(F) --> fun_unary(F), !. fun_unary(F) --> "(define (", label(Fun), spaces, label(Arg1), ")", spaces, expr(Body), ")", {swritef(F, "data *%t(data *%t) { return(%t); }", [Fun, Arg

我正在编写一个Lisp到C的翻译程序,我在处理字符串方面有问题。这是一段将一元Lisp函数转换为C等效函数的代码:

define(F) --> fun_unary(F), !.

fun_unary(F) --> "(define (", label(Fun), spaces, label(Arg1), ")", spaces, expr(Body), ")",
  {swritef(F, "data *%t(data *%t) { return(%t); }", [Fun, Arg1, Body])}, !.


funs([F])  --> define(F), !.
funs([F|Fs]) --> define(F), spaces, funs(Fs), !.
现在我想读取任意数量的函数,并将它们作为单个字符串返回。上面的
funs
是我能想到的最好的,但它的工作原理如下:

?- funs(F, "(define (carzero l) (= (car l) 0)) (define (zero n) (= 0 n))", []).
F = ["data *carzero(data *l) { return(eq(car(l), make_atom_int(0))); }", "data *zero(data *n) { return(eq(make_atom_int(0), n)); }"].
虽然我想要这样的东西:

F = "data *carzero(data *l) { return(eq(car(l), make_atom_int(0))); }\n\ndata *zero(data *n) { return(eq(make_atom_int(0), n)); }".
program(P) --> define(F), {swritef(P, "#include \"lisp2c.h\" \n\n%t \nint main() { return 0; }", [F])}, !.
这样我就可以很好地将
swritef
写入一个完整的程序,在
#include
s和main()之间。另一种解决方案是修改最高级别的转换器来处理列表。现在看起来是这样的:

F = "data *carzero(data *l) { return(eq(car(l), make_atom_int(0))); }\n\ndata *zero(data *n) { return(eq(make_atom_int(0), n)); }".
program(P) --> define(F), {swritef(P, "#include \"lisp2c.h\" \n\n%t \nint main() { return 0; }", [F])}, !.

我该怎么做这两件事呢?我使用的是SWI Prolog。

因为Prolog中的字符串实际上是字符代码列表,所以您可以在自定义谓词中使用
append
,该谓词也会插入换行符:

concat_program([], "").
concat_program([L|Ls], Str) :-
    concat_program(Ls, Str0),
    append("\n\n", Str0, Str1),
    append(L, Str1, Str).
用法:

funs(Fs, Lisp, []),
concat_program(Fs, P),
write("#include ...\n"),
writef(P).

使用DCG符号来追加字符串怎么样

concat([]) --> [].
concat([List|Lists]) --> List, "\n\n", concat(Lists).

撇开需要它的用途不谈,让我们编写一个Prolog谓词,将字符串列表连接成一个字符串,在每个连续的字符串对之间放置一个双换行符(但根据Jerry发布的示例判断,不在输出字符串的末尾)

SWI Prolog手册:通常我会向发布“深度”链接,但SWI Prolog网站使用一种URL样式,通过多种浏览器/插件组合触发跨站点脚本(XSS)警告。因此,我将参考相应部分的链接

第4.22节用字符串表示文本(部分)说,“默认情况下,字符串对象没有词法表示,因此只能使用下面的谓词或通过外语接口创建。”这可能会有点混淆,因为SWI Prolog将字符串写为双引号文本,但读取双引号文本(默认情况下)作为字符代码列表

下面是连接列表中字符串的谓词代码,在连续字符串对之间插入另一个字符串分隔符:

strSepCat([ ],_,Empty) :-
    string_to_list(Empty,[ ]).
strSepCat([H|T],Separator,StrCat) :-
    strSepCat(T,Separator,H,StrCat).

strSepCat([ ],_,StrCat,StrCat).
strSepCat([H|T],Sep,Str,Cat) :-
    string_concat(Sep,H,SepH),
    string_concat(Str,SepH,StrSepH),
    strSepCat(T,Sep,StrSepH,Cat).
注意,我们定义了两个谓词,strespcat/3strespcat/4。前者是根据后者定义的,后者是Prolog中的一种典型设计模式,它引入了一个额外的参数作为累加器,当递归完成时,累加器绑定到输出。这种技术通常有助于获得定义

要使用谓词strSepCat/3,我们通常需要使用两个换行符(转义序列)构造分隔符字符串:

?- funs(Fs,Lisp,[ ]), string_to_list(Sep,"\n\n"), strSepCat(Fs,Sep,CProg).
一个比公认答案更简单(更通用)的解决方案是使用reduce和现有字符串_concat作为参数:

reduce3(_, [],  Default, Default).
reduce3(_, [A], _, A).
reduce3(P3, [A,B|T], _, D):-
    call(P3, A, B, C),
    reduce3(P3, [C|T], _, D).

SWISH notebook:

主题行提到了Prolog,而问题的主体部分询问了“Lisp到C”的翻译。帮我弄清楚这是什么。代码片段看起来有点像Prolog,可能是因为特殊的DCG语法与Prolog更基本的规则语法混淆了。虽然主题行询问“Prolog中的字符串列表”,但似乎涉及到对包含Lisp代码的字符串的解析。串接字符串列表在Prolog中是一项相对简单的任务。您的示例谓词funs/2建议您在……个串联的连续字符串之间插入两个换行符。如果这是问题的范围,我可以回答它,我们可以理清语法的混乱(如果需要的话)。该程序是用Prolog编写的,使用DCG语法翻译个别案例。大多数谓词化的parse Lisp代码,其参数是生成的C代码。我想要两个连接字符串之间的换行符。希望如此。所以Lisp-to-C翻译部分并不是问题的一部分:)不过这个主意很酷。您如何处理内存分配?如果我知道您使用的特定Prolog实现,我可以根据这一点定制答案。Prolog共有三种“字符串”的数据表示形式:字符串、原子和字符列表。ISO标准规定,read/1和相关谓词应将双引号文本视为字符串,但由于历史上支持这种语法来表示字符列表,因此实现通常使用不同语法的选项来处理该问题。funs(F),(define(zero x)0)(define(one n)1)”,[]),concat_程序(F,x)。是假的。
F=[“data*carzero(data*l){return(eq(car(l),make_atom_int(0));}”,“data*zero(data*n){return(eq(make_atom_int(0),n));}”,concat_程序(F,X)工作。
。由于您没有给出完整的语法,我看不出代码的其余部分有什么错误。总体思路是正确的,但我怀疑“Prolog中的字符串实际上是字符代码列表”这一前提对于正在使用的Prolog实现可能是错误的。有关SWI Prolog字符串和连接,请参阅以下讨论:@hardmath:这在SWI和SICStus中有效。我不知道Prolog实现以不同的方式处理字符串。OP可能希望用
concat
string\u concat
替换
append
,以查看是否有效。