Common Lisp调用函数

Common Lisp call function

我是 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))))

你很接近,但两个函数定义都有小问题。在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 测试的操作(即另一种形式),但我不确定您在这种情况下想要做什么)

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

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

A 必须是一个列表,因为它在第一个 cond 子句 (atom A).

中失败

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

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

也一样, 在将列表 A 复制到局部符号 A(let ((A A)) ...) 部分)后,funcall 应用于列表 A 的第一个元素(即mrg),然后列表 A 中的以下两个元素作为 mrg funcall 调用的参数:

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

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

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

(相比之下,在宏调用中,参数不会像在函数调用中那样默认计算。相反,您可以在函数体中完全控制何时计算每个参数或将其视为符号)。

在您对@blihp 的回答的评论中,您在 let 形式中引用了 A ('A) 两次,这阻止了 A 被视为它实际代表的列表。

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

Invalid number of arguments: (EQUAL MRG)

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

另请注意,您经常重复代码。

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

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

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

与您的代码的不同之处在于:

  • 每个功能都在head位置检查,有利于安全,可以用授权符号列表复制。
  • 您丢弃剩余的参数,如果它们存在(例如 (+ 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))))))