作为函数参数传递时,如何停止评估Lisp格式? [英] How to stop evaluating lisp form when passed as function parameter?
问题描述
我正在学习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))
,但是这样的问题是代码不会与词汇环境交互.例如,这将不起作用,因为x
在fn
内部不可见:
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屋!