您可以将函数存储在变量中吗?

Can you store functions in variables?

我目前正在学习一些 Smalltalk。

有没有办法将函数或方法存储在变量中?

例如,在 Python 中可以说:

x = someobject.method
x() # calling it

这在 Smalltalk 中可行吗?将 something.method 本身作为参数传递给另一个方法怎么样?

您可以通过向 class 对象询问具有给定名称的方法来获取方法对象。 >> 下方是发送到 class Object 的消息,方法名称作为参数。

Object >> #asString

此 returns 类型 CompiledMethod 的一个实例。这是一个代表方法的对象。与任何对象一样,您可以将其存储在变量中。

copyMethod := Object>>#asString.

例如,如果您在 Pharo 中打开一个 Playground 并检查上面的代码,您会得到:

executing code in playground

然后您可以使用消息 #valueWithReceiver:arguments: 执行该方法。这需要接收者 (self/this) 和可能的参数作为参数。

result := copyMethod 
    valueWithReceiver: Object new arguments: #().

executing the method

但是,这是执行方法的低级方式。

我会说更常见的是直接在对象上执行具有给定名称的方法。这可以使用 perform:perform:withArguments: 来完成。您可以将方法名称作为参数

发送到对象
receiver := Object new.
receiver perform: #asString.

上面的代码在接收者对象上执行名为 asString 的方法。这通常用于传递我们需要执行的方法的名称。区别在于 CompiledMethod 的实例是不可变的。如果您更改该方法的代码,则会创建一个新实例,您最终可能会执行该方法的旧版本。

通常情况下,您在 Smalltalk 中应该做的就是发送消息。你永远不会执行一个方法,因为它违反了封装并且通常不是正确的抽象级别。

当您发送消息时,接收者对象在其 class methodDictionary 中查找以将 CompiledMethod 关联到消息选择器,如果 none,则它在 superclass methodDictionary 中查找, 等等... 或者最后发送 doesNotUnderstand: 如果没有找到相应的方法则消息。所有这些机器都在 VM 中执行。这不是程序员的工作。

当你想发送一个变量的消息时,你可以使用 perform: perform:with: perform:withArgument: 正如@andrei-chis 所解释的那样。

selector:= #(printString storeString asString) at: index.
^self perform: selector

对于低级机器的访问,我们可以通过发送消息valueWithReceiver:arguments:直接调用该方法,但我强烈建议不要使用它...这种对低级机器的访问专门用于调试支持,正如我所说,这不是正确的抽象级别 - 除非您试图在 Smalltalk VM 之上实现另一种语言并寻求如此低级别的构造。

如果你忽略了这个警告,那么你有责任提供一个正确的 class 的实例作为接收者(或者如果你想破解,至少提供一个分配了足够空间的对象,例如变量等...)。这样做的失败最少,您可能会损坏对象内存并让 VM 迟早崩溃,如果您有时间在致命崩溃之前保存损坏的图像,则会丢失一些工作...

所以总而言之,从向对象发送消息而不是调用方法的角度考虑;让对象自己决定执行哪个方法,那不关我们的事,Smalltalk 一路往下就是消息