作为函数参数传递时,如何停止评估Lisp格式? [英] How to stop evaluating lisp form when passed as function parameter?

查看:64
本文介绍了作为函数参数传递时,如何停止评估Lisp格式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习Lisp.现在,我正在尝试创建一个函数,该函数采用一些有效的Lisp形式作为参数,并返回一个在调用时执行Lisp形式的函数.例如:

I am learning Lisp. Now I am trying to create a function that takes some valid Lisp form as argument and returns a function that executes the Lisp forms when called. For example:

(defun fn (name action)
  (setf (symbol-function name)
        #'(lambda () action)))

当我通过时说(+ 4 5 6)时,该函数将使用特定名称创建,并在调用时返回总和.

When I am passing say (+ 4 5 6) the function is getting created with specific name and when called returning the sum.

(fn 'add (+ 4 5 6))
(add) ==> 15

但是,如果我调用(fn 'error (assert (= 2 3)),则会抛出错误(= 2 3) must evaluate to a non-NIL value.,并且不会创建名称为error的函数.

But if I invoke (fn 'error (assert (= 2 3)) it is throwing error (= 2 3) must evaluate to a non-NIL value. and the function with name error is not created.

当作为函数参数传递时,如何停止对此assert的求值?

How can I stop this evaluation of assert when passed as function parameter?

推荐答案

您无法编写此函数;它必须是宏运算符.如果fn是函数,则调用:

You cannot write this function; it has to be a macro operator. If fn is a function, then the call:

(fn 'add (+ 4 5 6))

计算参数(+ 4 5 6),将其减小为值15.该函数接收15,而不接收表达式.我们可以通过引用以下代码修复"此问题:

evaluates the argument (+ 4 5 6), reducing it to the value 15. The function receives 15, and not the expression. We can "fix" this by quoting the code:

(fn 'add '(+ 4 5 6))

,但是这样的问题是代码不会与词汇环境交互.例如,这将不起作用,因为xfn内部不可见:

but then we have the problem that the code doesn't interact with the lexical environment. For instance, this won't work, because x is not visible inside fn:

(let ((x 40)) (fn 'add '(+ x 2)))

要创建一个在适当的环境中求值(+ x 2)的函数,我们必须在相同的词法范围内正确使用lambda运算符:

To create a function which evaluates (+ x 2) in the proper environment, we must the lambda operator right in that same lexical scope:

(let ((x 40)) (lambda () (+ x 2)))

您的fn运算符可以写为生成lambda(没有任何名称)的语法糖:

Your fn operator can be written as a syntactic sugar that generates the lambda (without any name):

(defmacro fn (expr) `(lambda () ,expr))

现在我们可以写:

(let ((x 40)) (fn (+ x 2))) ;; returns a function which returns 42

要做命名的事情:

(defmacro fn (name expr) `(setf (symbol-function ',name) (lambda () ,expr)))

但是,这是一个很糟糕的主意;我们在函数中引入了讨厌的全局副作用.更好的"fn"可能是在某些形式上为函数引入词法绑定的函数.也就是说,它可以像这样使用:

However, this is a quite a poor idea; we're introducing a nasty global side effect into a function. A better "named fn" might be one which introduces a lexical binding for a function over some forms. That is, it can be used like this:

(fn (foo (+ x 2)) (foo))
             ;;  ^^^^^^  foo is a lexical function in this scope
             ;;          denoting the function (lambda () (+ x 2))

可以这样做:

(defmacro fn ((name expr) &rest forms)
   `(flet ((,name () ,expr)) ,@forms)))

或者,如果您希望将名称作为变量绑定而不是函数绑定,则用法为(fn (foo (+ x 2)) (funcall foo)):

Or, if you want the name as a variable binding rather than a function binding, so that the usage is (fn (foo (+ x 2)) (funcall foo)):

(defmacro fn ((name expr) &rest forms)
  `(let ((,name (lambda () ,expr))) ,@forms))

这篇关于作为函数参数传递时,如何停止评估Lisp格式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆