Functional programming 对于可以';我们不能完成程序吗?

Functional programming 对于可以';我们不能完成程序吗?,functional-programming,scheme,lisp,Functional Programming,Scheme,Lisp,我一直在阅读sicp,试图理解scheme,特别是宏。我注意到sicp根本不谈论宏。我在Paul Graham的网站上看到: Viaweb编辑器的源代码可能是大约20-25%的宏。宏比普通的Lisp函数更难编写,在不需要宏的时候使用宏被认为是不好的风格。所以代码中的每个宏都在那里,因为它必须在那里 所以我非常想知道如何编写宏以及宏的用途,所以我读了这个关于宏的网站: 但该网站只是解释了如何编写“for”宏。我认为Paul Graham谈到了CL,而另一个博客则谈到了scheme,但它们在某种程度

我一直在阅读sicp,试图理解scheme,特别是宏。我注意到sicp根本不谈论宏。我在Paul Graham的网站上看到:

Viaweb编辑器的源代码可能是大约20-25%的宏。宏比普通的Lisp函数更难编写,在不需要宏的时候使用宏被认为是不好的风格。所以代码中的每个宏都在那里,因为它必须在那里

所以我非常想知道如何编写宏以及宏的用途,所以我读了这个关于宏的网站: 但该网站只是解释了如何编写“for”宏。我认为Paul Graham谈到了CL,而另一个博客则谈到了scheme,但它们在某种程度上是相同的。所以,wat可以作为一个例子,说明普通过程无法完成的事情,因此必须使用宏来完成


编辑:我已经看到过类似的例子,但我想问一下,是否有一些疯狂的算法,你可以用宏来实现,而过程却无法实现(除了问题答案中描述的语法糖)

如果您刚刚熟悉宏,您会发现澄清或加速现有表达式的宏最有帮助

你可能接触过的一个这样的宏是回指宏,它在今天仍然很流行,就像保罗·格雷厄姆(Paul Graham)发明这个词的那一天一样:

(define-syntax (aif x)
  (syntax-case x ()
    [(src-aif test then else)
     (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
       [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))
这些宏引入了“回指”变量,即
it
,可以在if语句的结果子句和替代子句中使用,例如:

(aif (+ 2 7)
  (format nil "~A does not equal NIL." it)
  (format nil "~A does equal NIL." it))
这样就省去了键入let语句的麻烦——这可能会在大型项目中累加起来

更广泛地说,改变程序结构、动态生成“模板化”代码或以其他方式“破坏”规则(希望您知道的足够多,可以破坏!)的转换是宏的传统领域。我可以不停地写我如何用宏简化了无数的项目和任务,但我想你会明白的

学习Scheme风格的宏有点让人望而生畏,但我向您保证确实有,随着您对宏越来越多的体验,您可能会得出这样的结论:它们是一个美丽的想法,名副其实的“Scheme”


如果你碰巧使用了Racket(以前称为PLT Scheme)--即使在这一过程中提供了一些巧妙的技巧(我也猜那里写的大部分东西都可以用另一种Scheme方言毫无疑问地写出来)

宏是一个棘手的话题,我个人已经多次颠倒了自己的观点,所以,把一切都放在心上

如果您刚刚熟悉宏,您会发现澄清或加速现有表达式的宏最有帮助

你可能接触过的一个这样的宏是回指宏,它在今天仍然很流行,就像保罗·格雷厄姆(Paul Graham)发明这个词的那一天一样:

(define-syntax (aif x)
  (syntax-case x ()
    [(src-aif test then else)
     (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
       [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))
这些宏引入了“回指”变量,即
it
,可以在if语句的结果子句和替代子句中使用,例如:

(aif (+ 2 7)
  (format nil "~A does not equal NIL." it)
  (format nil "~A does equal NIL." it))
这样就省去了键入let语句的麻烦——这可能会在大型项目中累加起来

更广泛地说,改变程序结构、动态生成“模板化”代码或以其他方式“破坏”规则(希望您知道的足够多,可以破坏!)的转换是宏的传统领域。我可以不停地写我如何用宏简化了无数的项目和任务,但我想你会明白的

学习Scheme风格的宏有点让人望而生畏,但我向您保证确实有,随着您对宏越来越多的体验,您可能会得出这样的结论:它们是一个美丽的想法,名副其实的“Scheme”


如果您碰巧使用了Racket(以前称为PLT Scheme)--即使在这一过程中提供了一些巧妙的技巧(我也猜其中大部分内容都可以很容易地用另一种Scheme方言编写,没有任何问题)

这里有一个标准答案(其中一部分也包含在SICP中-例如,请参阅;同时搜索关键字中的“特殊形式”:在按值调用语言(如Scheme)中,普通过程(函数)必须在主体之前计算其参数。因此,普通过程无法实现特殊形式,如
if
for
while
等(至少在不使用诸如
(lambda()body)
之类的thunk的情况下,引入这些thunk是为了延迟
body
的计算,并且可能会产生性能开销);另一方面,许多thunk可以像在(例如,请参见第69页上定义语法的
的定义)。

这里有一个标准答案(其中一部分也包含在SICP中-例如,请参见;也可以在中搜索关键字“特殊形式”):在类似Scheme的按值调用语言中,普通过程(函数)必须计算其参数因此,特殊形式,如
if
for
while
等,不能通过普通过程实现(至少不使用类似
(lambda()body)的thunks)
,用于延迟对
主体的评估,并可能导致性能开销);另一方面,它们中的许多都可以使用宏实现,就像在中所做的那样(例如,请参见第69页的
by
define syntax
的定义).

简单的答案是,您可以在延迟求值对功能至关重要的地方创建新语法
(define (pif* p c a . rest)
  (if (p)
      (c)
      (if (null? rest)
          (a)
          (apply pif* a rest))))
(define-macro if*
  (syntax-rules ()
    ((_ x) (error "wrong use of if*"))
    ((_ p c a) (if p c a))
    ((_ p c next ...) (if p c (if* next ...)))))
(define-syntax if*
 (syntax-rules ()
   ((_ arg ...) (pif* (lambda () arg) ...)))