语言是否可以使用Lisp强大的宏而没有括号?
答案 0 :(得分:41)
答案 1 :(得分:20)
绝对。如果你必须处理复杂的语法,它只需要几个数量级的复杂程度。正如Peter Norvig noted:
Python确实可以访问 程序的抽象语法树,但是 这不适合胆小的人。上 好的方面,模块很容易 了解,并与五分钟和 我能得到五行代码 这样:
>>> parse("2 + 2")
['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison',
['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term',
['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor',
['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]
这对我来说是一种失望。等价表达式的Lisp解析是
(+ 2 2)
。似乎只有真正的专家才能操纵Python解析树,而Lisp解析树对任何人来说都很简单。通过连接字符串仍然可以在Python中创建类似于宏的东西,但它没有与语言的其余部分集成,因此在实践中没有完成。
由于我不是超级天才(甚至是Peter Norvig),我会坚持使用(+ 2 2)
。
答案 2 :(得分:13)
以下是Rainer答案的缩短版本:
为了拥有lisp风格的宏,您需要一种在数据结构中表示源代码的方法。在大多数语言中,唯一的“源代码数据结构”是一个字符串,它没有几乎足够的结构来允许你进行真正的宏。有些语言提供了真正的数据结构,但它太复杂了,就像Python一样,因此编写真正的宏是非常复杂的,并不值得。
Lisp的名单和括号在中间达到了最佳位置。足够的结构使其易于处理,但不是太多,所以你淹没了复杂性。作为奖励,当您嵌套列表时,您会得到一棵树,这恰好是编程语言自然采用的结构(在实际解释/编译之前,几乎所有编程语言都首先被解析为“抽象语法树”或AST) )。
基本上,编程Lisp是直接编写AST,而不是编写其他语言,然后由计算机转换为AST。您可能会放弃这些问题,但您只需要一些其他方法将事物分组到列表/树中。你可能不会因此而获得太多收益。
答案 3 :(得分:9)
括号与宏无关。这只是Lisp的做事方式。
例如,Prolog有一个非常强大的宏机制称为“术语扩展”。基本上,每当Prolog读取术语T时,如果尝试特殊规则term_expansion(T, R)
。如果成功,则解释R的内容而不是T.
答案 4 :(得分:5)
更不用说Dylan language,它有一个非常强大的语法宏系统,它具有参考透明度,其中包括中缀(Algol风格)语言。
答案 5 :(得分:5)
是。 Lisp中的括号以经典方式使用,作为分组机制。缩进是表达群体的另一种方式。例如。以下结构是等效的:
A ((B C) D)
和
A
B
C
D
答案 6 :(得分:3)
在Tcl中以与Lisp宏类似的方式重写代码是一种常见的技术。例如,这是(普通的)代码,可以更容易地编写始终导入某组全局变量的过程:
proc gproc {name arguments body} {
set realbody "global foo bar boo;$body"
uplevel 1 [list proc $name $arguments $realbody]
}
这样,使用gproc xyz
而不是proc xyz
声明的所有过程都可以访问foo
,bar
和boo
全局变量。整个关键是uplevel
接受命令并在调用者的上下文中对其进行评估,而list
是(除其他事项外)替换安全代码的理想构造函数片段。
答案 7 :(得分:3)
看看Sweet-expressions。 Wheeler提出了一个很好的例子,因为中缀符号之类的东西以前没有用过的原因是典型的符号也试图增加优先级,这会增加复杂性,这会导致编写宏的困难。
出于这个原因,他建议使用中缀语法,如{1 + 2 + 3}和{1 + {2 * 3}}(注意符号之间的空格),将其转换为(+ 1 2)和(+ 1) (* 2 3))。他补充说,如果有人写{1 + 2 * 3},它应该变成(nfx 1 + 2 * 3),如果你真的希望提供优先权,它可以被捕获,但是默认情况下,是一个错误。
他还建议缩进应该是重要的,建议函数可以被称为fn(ABC)以及(fn ABC),希望数据[A]转换为(bracketaccess数据A),并且整个系统应该与s表达式兼容。
总的来说,这是一套有趣的建议,我想广泛试验。 (但是不要告诉comp.lang.lisp上的任何人:他们会因为你的好奇心而把你烧死: - )。
答案 8 :(得分:2)
Erlang的解析转换与Lisp宏的功能类似,但它们编写和使用起来比较棘手(它们应用于整个源文件,而不是按需调用)。
Lisp本身与M-expressions形式的非括号语法有短暂的联系。它没有与社区一起使用,虽然这个想法的变体已经进入现代Lisps,所以你得到Lisp强大的宏而没有括号......在Lisp中!
答案 9 :(得分:2)
是的,你绝对可以在没有括号的情况下使用Lisp宏。
看一下“sweet-expressions”,它为传统的s表达式提供了一组额外的缩写。他们添加缩进,一种做中缀的方法,以及传统的函数调用,如f(x),但是以向后兼容的方式(你可以自由地混合格式良好的s表达式和sweet-expressions),泛型和homoiconic
Sweet {表达式是在http://readable.sourceforge.net上开发的,并且有一个示例实现。
对于Scheme,有一个用于甜言蜜语的SRFI,SRFI-110:http://srfi.schemers.org/srfi-110/
答案 10 :(得分:1)
不,没有必要。任何能够让你对解析树进行某种访问的东西都足以让你以与在Common Lisp中相同的方式操作宏体。但是,由于在lisp中对AST的操作与列表的操作相同(在lisp系列中很容易接受),如果没有“解析树”和“书面形式”本质上可能就不那么自然了同样的。
答案 11 :(得分:1)
我认为没有提及。
C ++模板是Turing-complete并在编译时执行处理。
有众所周知的expression templates机制允许转换, 不是来自任意代码,而是来自c ++运算符的子集。
所以想象你有3个1000个元素的向量,你必须执行:
(A + B + C)[0]
您可以在表达式模板中捕获此树并随意操作它 在编译时。
使用此树,在编译时,您可以转换表达式。
例如,如果您的域名表达式为A[0] + B[0] + C[0]
,则可以
避免正常的c ++处理:
并替换为另一个转换的表达式模板树:
我认为它并不比lisp好,但它仍然非常强大。
答案 12 :(得分:0)
Boo有一个很好的“引用”宏语法,它使用[| |]作为分隔符,并且具有某些替换,这些替换实际上是由编译器管道使用$ variables在语法上验证的。虽然使用简单且相对轻松,但在编译器端实现比s表达式复杂得多。 Boo的解决方案可能有一些限制,但没有影响我自己的代码。还有一种替代语法更像普通的OO代码,但它属于“不适合胆小者”的类别,比如处理Ruby或Python解析树。
答案 13 :(得分:0)
是的,这当然是可能的。特别是如果它仍然是发动机罩下的Lisp:
答案 14 :(得分:0)
Javascript' s template strings提供了另一种解决此类问题的方法。例如,Mark S. Miller的quasiParserGenerator实现了解析器的语法语法。
答案 15 :(得分:0)
继续并输入Elixir编程语言。
Elixir是一种功能性编程语言,在宏方面看起来像Lisp,但它是在Ruby的衣服上运行的,并且在Erlang VM上运行。
对于那些不喜欢括号但希望他们的语言具有强大宏的人来说,Elixir是一个不错的选择。
答案 16 :(得分:0)
您可以在R(类似于Algol语法)中编写具有LISP宏中的延迟表达概念的宏。您可以调用substitute()
或quote()
来不评估延迟的表达式,而是获取实际的表达式并遍历其源代码,就像在LISP中一样。表达式源代码的结构甚至类似于LISP。运算符是列表中的第一项。例如:input$foo
将从列表foo
获得属性input
,就像在LISP中一样,表达式被表示为['$', 'input', 'foo']
。
您可以查看电子书Metaprogramming in R,其中也显示了如何在R中创建宏(这不是您通常可以做的,但是有可能)。它基于2001年的Programmer’s Niche: Macros in R文章,该文章解释了如何在R中编写LIPS宏。