好回答直接的问题,
您可以根据之前的定义重新定义方法/函数/子例程
...基本上任何语言,只要它支持两个功能:
- 可以保存函数值的可变变量
- 某种闭包形成运算符,它实际上相当于创建新函数值的能力
所以你不能在 C 中做到这一点,因为即使它允许变量存储函数指针,C 中也没有可以计算新函数值的操作;而且你不能在 Haskell 中这样做,因为 Haskell 不允许你在定义变量后对其进行变异。但是你可以在例如 JavaScript 中做到这一点:
var f1 = function(x) {
console.log("first version got " + x);
};
function around(f, before, after) {
return function() {
before(); f.apply(null, arguments); after();
};
}
f1 = around(f1,
function(){console.log("added before");},
function(){console.log("added after");});
f1(12);
或方案:
(define (f1 x) (display "first version got ") (display x) (newline))
(define (around f before after)
(lambda x
(before) (apply f x) (after) ))
(set! f1 (around
f1
(lambda () (display "added before") (newline))
(lambda () (display "added after") (newline))))
(f1 12)
...或一大堆其他语言,因为这些确实是相当常见的功能。操作(我认为通常称为“advice”)基本上类似于 ubiquitous x = x + 1
,除了 value 是一个函数,而“addition”是围绕它的额外操作的包装,以创建一个新的函数值。
这样做的原因是,通过将旧函数作为参数(到around
,或只是 alet
或其他)传递,新函数正在关闭它,通过本地范围的名称引用它;如果新函数引用全局名称,则旧值将丢失,新函数将递归。
从技术上讲,您可以说这是后期绑定的一种形式- 函数是从变量中检索而不是直接链接 - 但通常该术语用于指代更多动态行为,例如 JS 字段访问甚至可能实际上并不存在。在上述情况下,编译器至少可以确定变量 f1
将存在,即使它被证明是持有null
或其他东西,所以查找速度很快。
假设调用该名称的其他函数f1
将按照您期望的方式工作。如果您在调用var f3 = f1;
之前这样做,则around
定义为调用的函数f3
不会受到影响;类似地,f1
通过将其作为参数或其他东西传入而获得的对象。基本词汇范围规则适用。如果你希望这些功能也受到影响,你可以使用 PicoLisp 之类的东西来完成它......但你也在做一些你可能不应该做的事情(这不再是任何类型的绑定:这是 a 的直接突变函数对象)。
除此之外,我不确定这是否符合文学编程的精神——或者就此而言,是一个描述规则的程序。规则是否应该根据您阅读本书的程度或阅读章节的顺序而改变?识字程序不是-就像一段文本通常意味着一件事(您可能不理解它,但它的含义是固定的),无论您是先阅读还是最后阅读它,真正的识字程序中的声明也应该如此,对? 一个人通常不会像小说一样从头到尾阅读参考资料——比如一本规则书。
尽管这样设计,但程序的含义高度依赖于以特定顺序阅读语句。这是一个非常机器友好的系列指令......与其说是参考书。