您可以将函数存储在变量中吗?
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 一路往下就是消息
我目前正在学习一些 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 一路往下就是消息