考虑这个javascript:
function addX(n)
{
return 3 + n;
}
alert(addX(6)); //alerts 9
eval('var newFunc = ' + addX.toString().replace("3", "5") + ';');
alert(newFunc(10)); //alert 15
Run Code Online (Sandbox Code Playgroud)
请忽略这样一个事实:它使用和方法可疑,危险,难以在大型代码库中使用,等等.它允许您根据用户的输入动态修改功能.我没有证明,但我也很容易.
我希望你能告诉我如何在lisp中做到这一点.我已经阅读了很多教程,阅读了很多关于宏的内容,提出了一个更广泛的问题,尝试了很多东西,但最终还是做得很短.
我想知道,在lisp中,我可以在运行时修改此函数,而不是添加5.或者用户可能输入的任何其他内容.
(define (addX n)
(+ 3 n))
Run Code Online (Sandbox Code Playgroud)
我不是在寻找currying!我知道我可以这样做:
(define addXCurry
(lambda (x)
(lambda (n)
(+ x n))))
(define add5 (addXCurry 5))
(add5 10)
Run Code Online (Sandbox Code Playgroud)
但这是在创建一个功能工厂.
我正在使用一个简单的例子,因为我想完全理解一些简单的东西.
编辑 谢谢大家的答案.我想我围绕宏的大笨蛋(据我所知),是因为我没有看到一个完全将修改与写作分开的东西.javascript示例很简单 - 但您可以根据用户输入执行更复杂的重写.
我见过的宏都是基于"编译时"(或者我编写的程序员编写的时间).就像在C++中一样,您不能拥有动态模板参数 - 必须在编译时知道它.
(看起来)在lisp中,你无法在运行时根据javascript中的方式从根本上改变过程,因为你丢失了源代码.您可以评估并重新定义它,但不能遍历列表的元素(列表是函数定义),检查每个元素并决定是否更改它.例外情况似乎是Rainer答案中的例子,这些例子都是不稳定的.
Rai*_*wig 16
困难的部分是Common Lisp(以及其他一些Lisps)摆脱了源代码.特别是涉及编译器时.默认情况下,源代码已经消失,剩下的就是机器代码了.如何恢复Lisp源和什么形状?
这背后的原因:为什么要求CL程序保留源代码?它可以完全编译为机器代码或C代码,并且在运行时没有编译器/ EVAL.该程序可以在没有太多开发环境(没有编译器等)的情况下运行.还不要求Common Lisp环境能够将代码"解编"为某种重构源代码.
它通常也很复杂.
(let ((n 0) (step 2)) (defun foo () (incf n step)))
Run Code Online (Sandbox Code Playgroud)
以上是什么来源以及如何改变STEP?该功能取决于词法绑定.
另一个复杂因素:
(defun foo (n) (+ n #.(random 1.0)))
Run Code Online (Sandbox Code Playgroud)
如何恢复?每当Lisp读取源文本时,将读取随机数.
另一个复杂因素:
(setf (symbol-function 'foo) (compute-function))
Run Code Online (Sandbox Code Playgroud)
您可以使用某个任意计算的函数或预定义的函数(如SIN)设置函数值.如果将它们编译为机器代码,加载为机器代码等,如何恢复它们?
如果Common Lisp实现保留源代码,则FUNCTION-LAMBDA-EXPRESSION将检索它.
有两种方法:
a)告诉Lisp源代码或记住源代码.
提供来源.
(let* ((fs (copy-list '(lambda (n) (+ n 3))))
(fc (compile nil fs)))
(print (funcall fc 6))
(setf (third (third fs)) 5)
(setf fc (compile nil fs))
(funcall fc 6))
Run Code Online (Sandbox Code Playgroud)
扩展示例:
写一个宏DEFINE,它记住源并定义函数.
(defmacro define (&rest source)
`(progn (setf (get ',(first source) :source) (list* 'defun ',source))
(defun ,@source)))
Run Code Online (Sandbox Code Playgroud)
上面将源代码放在符号属性列表下:SOURCE.
现在我们可以编写一个修改源代码并编译它的函数:
(defun modify (fname modifier)
(let ((source (get fname :source)))
(when source
(setf (get fname :source) (funcall modifier source))
(eval (get fname :source))
(compile fname))))
Run Code Online (Sandbox Code Playgroud)
示例定义:
(define addx (n) (+ n 3))
Run Code Online (Sandbox Code Playgroud)
重写示例:
(modify 'addx (lambda (source) (setf (third (fourth source)) 6) source))
Run Code Online (Sandbox Code Playgroud)
b)一些Common Lisp实现实现了一个名为FUNCTION-LAMBDA-EXPRESSION的函数(在ANSI Common Lisp中定义).
此函数返回三个值:源为Lisp数据,closure-p和名称.它允许您更改源代码,编译它并使用COMPILE将名称设置为新函数.代码示例留作练习.
问题:在Common Lisp中,宏DEFUN定义了函数.宏在幕后做什么(IDE的簿记,代码重写......)是依赖于实现的.因此,FUNCTION-LAMBDA-EXPRESSION返回的代码(如果实现返回源代码)对于每个实现可能看起来不同.
这是一个LispWorks示例:
CL-USER 12 > (function-lambda-expression #'addx)
(LAMBDA (N)
(DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 217874D3>))
(DECLARE (LAMBDA-NAME ADDX))
(+ N 3))
NIL
ADDX
Run Code Online (Sandbox Code Playgroud)
因此,您可以操纵源表达式并进行更改.
例外情况似乎是Rainer答案中的例子,这些例子都是不稳定的.
为什么?这是合乎逻辑的结论.您不能依赖编译器保留源代码,因此您只需自己存储它.
之后,您可以正确使用函数的定义(而不是Javascript,你只是破解字符串表示,这是一个摇摇欲坠的事情的一个主要的例子).