【问题标题】:Recursive macro in Common LispCommon Lisp 中的递归宏
【发布时间】:2015-10-15 22:39:59
【问题描述】:

我必须在 Common Lisp 中编写递归宏。

我有一个搜索列表中最大元素的函数。例如它返回 45 用于列表 (2 4 6 42 5 45 3)。现在我要把它转换成递归宏。

这是我的代码:

(defmacro mymax (values)                     
    (cond 
        ((= (length values) 1) `(write (car ,values)))
        ((> (car values) (cadr values)) (mymax (list (car values) (cddr values))))    
        (t (mymax (cdr values)))
    )
)       

它不起作用。编译错误:“LIST 不是实数”。我尝试了 '`' 和 ',' 的不同组合 - 没有结果。如何更正我的代码以使宏的结果为数字?

宏调用: (write (mymax (list 3 5 6 7 8)))

(write (mymax (cons 3 5 6 7 8)))

(write (mymax '(3 5 6 7 8)))

所有这些调用都以相同的结果结束。

【问题讨论】:

  • 为什么必须是宏?这是一个硬件任务吗?你如何调用你的宏?
  • 这没什么意义...
  • 宏获取代码并生成代码。它应该生成什么代码?
  • 它应该如何从列表(list 1 2 3) 中生成最大数量? list 不是数字。
  • (mymax a) 应该如何工作?宏应该如何计算符号a 的最大值。 a 不是数字列表。请记住,宏获取源代码,这里是a,而不是评估参数。

标签: recursion macros lisp common-lisp


【解决方案1】:

(a b c)

  • 如果您阅读上面的表格,它是一个包含 3 个符号的列表。
  • 当您评估它时,如果a 未绑定到宏,则表单是对a 的函数调用,其中bc 被评估。

'(a b c),又名(quote (a b c))

  • 如果您阅读这个,您会得到一个包含两个元素的列表,第一个是quote,然后是一个包含 3 个符号的列表。
  • 如果您评估表单,则返回值是 3 个符号的列表:(a b c)

Lisp 阅读器以 read 的形式给出未评估的源代码。

(mymax '(1 2 3)) 中,'(1 2 3) 是未评估的形式(quote (1 2 3))。在(mymax (list 1 2 3)) 中,未计算的参数有 4 个元素,第一个是符号。您应该为宏提供什么以便它具有以下列表(1 2 3)

如有疑问,请进行宏展开。

【讨论】:

    【解决方案2】:

    这是函数MAX 的宏版本,当每个参数都是数字时,它会做一些有用的事情:

    (defmacro mymax (&rest numbers)
      (flet ((all-number-list-p (list)                     ; local function
               (and (listp list)                           ; it's a list
                    (numberp (first list))                 ; the first number exists
                    (every #'numberp list))))              ; everything is a number
        (if (all-number-list-p numbers)                    ; only numbers? 
            (cond ((null (rest numbers)) (first numbers))  ; one number
                  ((> (first numbers) (second numbers))    : first is greater
                   `(mymax ,@(cons (first numbers)         ; get rid of the second
                                   (cddr numbers))))
                  (t                                       ; second is greater
                   `(mymax ,@(rest numbers))))             ; get rid of the first
          `(max ,@numbers))))                              ; fallback, use MAX
    

    例子:

    CL-USER 23 > (macroexpand-1 '(mymax 4 1 3 2))
    (MYMAX 4 3 2)
    T
    
    CL-USER 24 > (macroexpand-1 '(mymax 4 3 2))
    (MYMAX 4 2)
    T
    
    CL-USER 25 > (macroexpand-1 '(mymax 4 2))
    (MYMAX 4)
    T
    
    CL-USER 26 > (macroexpand-1 '(mymax 4))
    4
    T
    
    CL-USER 27 > (mymax 4 1 3 2)
    4
    

    请注意,当不是每个参数都是数字时,它只使用MAX

    CL-USER 28 > (macroexpand-1 '(mymax a b 8 d))
    (MAX A B 8 D)
    

    【讨论】:

    • (and (consp list) (every #'numberp list)) 不够吗?
    • @coredump:是的,不过我希望它更明确。为了清楚起见,至少需要一个数字。这隐含在您的条件中。
    • 我发现(numberp (first list)) 更间接,因为如果没有第一个元素或者因为第一个不是数字,它可能是假的,而cons 对我来说似乎更明确。我想这是一个品味问题。
    • @coredump:这正是我想做的:谈论内容,而不是作为链接单元格的表示。
    • 哇,这很有用,谢谢!它让我对 Lisp 中的宏有不同的看法。然而,像 ( defmacro Power (values) `( if (= (second ,values) 0) 1 ( * (first ,values) (Power (list (first ,values) (- (second ,values) 1))) ) ) ) 这样定义的宏在通过键入 clisp name.lisp 从 cmd 运行时可以完美运行。使用 clisp 编译然后运行 ​​name.fas 文件时,它不起作用。
    猜你喜欢
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 2018-05-16
    • 2016-08-09
    • 2013-07-26
    • 1970-01-01
    相关资源
    最近更新 更多