【问题标题】:Common Lisp call functionCommon Lisp 调用函数
【发布时间】:2018-09-30 17:00:05
【问题描述】:

我是 Common Lisp 的新手。最近开始学习它。我有一个小问题如何在另一个函数中调用一个函数?我有一个函数mrg 和函数my_eval。以及如何通过键入例如(print (my_eval '(mrg '(1 1 2 1 2 1 3) '(5 3 3 1 2 2))))my_eval 中调用此函数mrg。我试过了,但我遇到了一些错误,例如 it's not a real numberundefined function A。请帮帮我。

这是我的代码:

(defun mrg (w v)
  (merge 'list (sort w #'<) (sort v #'<) #'<))

(defun my_eval (A)
  (cond
    ((atom A) A)
    ((equal 'car (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
    ((equal 'cdr (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
    ((equal 'atom (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
    ((equal 'cons (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal 'list (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal 'equal (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal '* (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal '/ (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal '+ (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal '- (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal '= (car A))
     (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
    ((equal 'mrg    ))
    (T A)))

(print (my_eval '(mrg '(1 1 2 1 2 1 3) '(5 3 3 1 2 2))))

【问题讨论】:

  • A 的 let 绑定背后的意图是什么?

标签: function common-lisp eval


【解决方案1】:

如果查看其他调用,'mrg 的条件子句必须是

((equal 'mrg (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))

A 必须是一个列表,因为它失败了第一个 cond-clause (atom A)

由于mrg 在此实现中需要两个参数, 就像在这个 eval 中的内置函数一样

`cons`
`list`
`equal`
`*`
`/`
`+`
`-`
`=`

也可以, 将列表A 复制到本地符号A(let ((A A)) ...) 部分)后,funcall 应用于 ist A(即mrg)的第一个元素,然后是以下两个元素在列表中A 被作为mrg funcall-call 的参数给出:

  • (cadr A)(是(second A)的同义词)和
  • (caddr A)(与 (third A) 同义)。

由于每个参数本身都可以是原子或其他函数调用或特殊形式,因此您必须在它们周围包裹一个my_eval 调用并评估每个参数。

-- 顺便说一下,如果你调用 Lisp 函数,总是会发生这种情况——每个参数(它是一个表达式)在将结果处理给主函数调用之前,都会对其自身进行完全评估。

(相比之下,在宏调用中,默认情况下不会对参数进行评估,就像在函数调用中一样。相反,您可以在函数体中完全控制何时评估每个参数或将其视为符号) .

在您对@blihp 的回答的评论中,您在let-form 中引用了A ('A) 两次,这可以防止将A 作为它实际代表的列表。

我看到的另一个问题是您的my_eval 没有寻找quote,我也不确定您的my_eval 实现是一个非常初级的实现,是否可以正确处理'。因此在测试mrg 调用中,我建议使用(list 1 3 4 2 4 ...) 而不是'(1 3 4 2 4 ...) 以防止进一步的复杂化。

【讨论】:

  • 好的,我明白了。谢谢你的解释。
【解决方案2】:

你很接近,但两个函数定义都有小问题。在mrg函数定义中,需要在函数声明后传递一个表单(即括号内要执行的内容):

(defun mrg (w v)
    (merge 'list (sort w #'<) (sort v #'<) #'<))

而且你的 my_eval 函数对于 mrg 条件是不完整的:

(defun my_eval(A)
    (cond
        ((atom A) A)
        ((equal 'car    (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
        ((equal 'cdr    (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
        ((equal 'atom   (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)))))
        ((equal 'cons   (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal 'list   (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal 'equal  (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal '*      (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal '/      (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal '+      (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal '-      (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal '=      (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (my_eval (caddr A)))))
        ((equal 'mrg    (car A)) (let ((A A)) (funcall (car A) (my_eval (cadr A)) (merge 'list (sort A #'<) (sort A #'<) #'<))))
        (T A)
    )
) 

(看起来您仍然缺少 mrg 测试的操作(即另一种形式),但我不确定您在这种情况下想要做什么)

【讨论】:

  • 好吧,我试过这个((equal 'mrg (let ('A 'A)(funcall (A)(my_eval(cadr A)(merge 'list (sort A #'&lt;) (sort A #'&lt;) #'&lt;)))))),但我遇到了一个错误。并添加了这个((equal 'mrg (funcall mrg ('A 'A)))) 并出现了错误。而且我不知道如何在((equal 'mrg )) 这一行中调用这个mrg。也许我应该以某种方式解析它?
  • 我已经更新了答案中的代码以包含一个 listp 测试(因为您正在递归调用 my_eval,因此您也需要对此进行测试,因为它将通过列表)并添加了 mrg行动。
  • "((atom A) A)" 之后的所有内容都已经/正在假设 A 是一个列表(隐含地,因为它不是原子),所以添加一个 "(listp... " 条件首先防止任何子句触发 - 前两个条件涵盖了所有可能的输入。
  • 感谢@DarrenRinger 的捕获......因为已经接受了另一个答案,所以我刚刚删除了 listp 条件。正如您正确指出的那样,它错误地绕过了其余的条件。
【解决方案3】:

参数数量无效:(EQUAL MRG)

正如其他答案所指出的,存在数量不匹配:EQUAL 接受 2 个参数,但在 (EQUAL MRG) 中使用 1 个参数调用。

还请注意,您正在大量重复代码。

在所有情况下,您将列表的头部与一个常量符号进行比较,然后通过将调用 my_eval 的结果作为参数提供给同一个列表的第一个元素,有时是第二个元素,来调用它。 基本上,您的代码正在这样做:

(apply (first list) (mapcar #'my-eval (rest list)))

APPLY 函数采用 函数指示符 并使用任意数量的参数调用它。在这里,该参数列表是将my_eval 应用于列表中的每个剩余元素的结果。

与您的代码的区别是:

  • 您检查头部位置的每个功能,这有利于安全,并且可以使用授权符号列表进行复制。
  • 如果存在剩余的参数,则丢弃它们(例如,(+ 1 4 9)my_eval 下的计算结果为 5)。恕我直言,my_eval 在这种情况下应该大声失败,因为这可能不是任何人所期望的。
  • 另请注意,将 A 重新绑定到局部变量名称 Alet 在这里没有用处。

如果您想保留这种方法但删除一些代码重复,您可以试试这个;应该调用以下函数,您确定要评估的表单是一个 cons-cell。

(defun my-eval/apply (cons)
  (check-type cons cons)
  (destructuring-bind (head . tail) cons
    (let ((size (length tail))
          (arity (case head
                   ((car cdr atom) 1)
                   ((cons list equal * / + - = mrg) 2))))
      (cond
        ((not arity) (error "Unknown function ~a" head))
        ((= size arity) (apply head (mapcar #'my_eval tail)))
        (t (error
            "Arity mismatch: ~a takes ~d parameter~:p but was ~
             called with ~d argument~:p in ~s" head arity size cons))))))

【讨论】:

    猜你喜欢
    • 2018-09-23
    • 2014-11-28
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多