方案:用于代码复制的宏或高阶函数?

Scheme: macros or higher order functions for code duplication?

我想获得调用 shell 命令和 returns 字符串的函数的结果。 我正在使用球拍,这是我的第一次尝试:

(define (run-function)
  (let*
    ([stdout (some-function)]
     [output (process-string stdout)])
  ;; many more lines...
  output))

它似乎工作得很好,但假设我想为许多其他 shell 命令编写类似于 run-function 的函数。

为了避免代码重复,我可以像这样定义一个更通用的函数:

(define (shell cmd)
  (let*
    ([stdout (cmd)]
     [output (process-string stdout)])
  ;; many more lines...
  output))

然后调用 (shell ls)(shell pwd).

另一种方法是使用简单的宏:

(define-syntax shell
  (syntax-rules ()
    [(shell cmd)
     (let*
       ([stdout (cmd)]
        [output (process-string stdout)])
       ;; many more lines...
       output)]))

这还有一个优点是允许使用更通用的语法,例如我可以轻松更改宏,以便它可以根据需要使用尽可能多的参数(命令),但我确信可以复制相同的行为通过更明智地编写高阶函数。

问。 pros/cons 编写高阶函数与宏有什么区别?两者之间有明显的赢家吗?

我同意 @MLavrentyev 的观点,“如果可以,请使用函数”。

the Racket style guide也表示:

Define functions when possible, Or, do not introduce macros when functions will do.

但是为什么呢?一个原因是,如果将 shell 写成函数,则可以将 shell 传递给其他函数。您可以通过 identifier macro 功能对宏执行相同的操作,但这样做要困难得多(无论如何您最终都会有效地创建一个函数)。

另一个原因是使用宏会使编译后的代码比使用函数更大。这是因为宏在编译时被展开,然后展开的代码被编译(在 Racket BC 中编译成字节码,在 Racket CS 中编译成机器码)。所以就好像你一遍又一遍地写 (let* ...) 一样。如果您分发已编译或可执行的 Racket 程序,您不会希望大小很大。

事实上,编写宏的一个好习惯是尽量将代码塞进函数中。而不是写:

(define-syntax-value (debug code)
  (let ([val code])
    (printf "~a evaluates to ~a\n" (quote code) val)
    val))

最好写成:

(define (debug-core expr val)
  (printf "~a evaluates to ~a\n" expr val)
  val)

(define-syntax-value (debug code)
  (debug-core (quote code) code))