Можете ли вы хранить функции в переменных? - PullRequest
3 голосов
/ 03 мая 2019

Я сейчас немного изучаю Smalltalk.

Есть ли способ сохранить функцию или метод внутри переменной?

Например, в Python можно сказать:

x = someobject.method
x() # calling it

Возможно ли это в Smalltalk?Как насчет передачи something.method в качестве аргумента другому методу?

Ответы [ 2 ]

4 голосов
/ 03 мая 2019

Вы можете получить объект метода, запросив у объекта класса метод с заданным именем. Ниже >> - сообщение, отправленное классу Object с именем метода в качестве параметра.

Object >> #asString

Возвращает экземпляр типа CompiledMethod. Это объект, представляющий метод. Как и любой объект, вы можете хранить его внутри переменной.

copyMethod := Object>>#asString.

Например, если вы откроете в Pharo Playground и осмотрите код, указанный выше, вы получите:

выполнение кода на детской площадке

Затем вы можете выполнить метод, используя сообщение #valueWithReceiver:arguments:. Для этого нужны в качестве параметра получатель (self / this) и возможные аргументы.

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

выполнение метода

Однако это низкоуровневый способ выполнения метода.

Я бы сказал, что более распространенным является непосредственное выполнение метода с заданным именем объекта. Это можно сделать с помощью perform: или perform:withArguments:. Вы можете отправить это объекту с именем метода в качестве аргумента

receiver := Object new.
receiver perform: #asString.

Приведенный выше код выполняет метод с именем asString для объекта-получателя. Это часто используется для передачи имен методов, которые нам нужно выполнить. Разница в том, что экземпляры CompiledMethod неизменны. Если вы измените код этого метода, будет создан новый экземпляр, и вы можете в итоге выполнить более старую версию метода.

1 голос
/ 03 мая 2019

Обычно все, что вы должны делать в Smalltalk, это отправлять сообщения. Вы никогда не выполняете метод, потому что это нарушение инкапсуляции и, как правило, неправильный уровень абстракции.

Когда вы отправляете сообщение, объект-получатель выполняет поиск в своем классе methodDictionary для связывания CompiledMethod с селектором сообщений, если его нет, он затем ищет в суперклассе methodDictionary и т. Д. ... или, наконец, отправляет didNotUnderstand: сообщение, если нет соответствующего метода были найдены. Вся эта техника выполнена в ВМ. Это не работа программиста.

Когда вы хотите отправить сообщение, которое является переменной, вы можете использовать perform: perform:with: perform:withArgument:, как объяснено @ andrei-chis.

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

Для предоставления доступа к оборудованию низкого уровня мы можем напрямую вызвать метод, отправив сообщение valueWithReceiver:arguments:, но я настоятельно советую его не использовать ... Такой доступ к оборудованию низкого уровня предназначен для поддержки отладки и, как я сказал, что не правильный уровень абстракции - если вы не пытаетесь реализовать другой язык выше виртуальной машины Smalltalk и не ищете такие низкоуровневые конструкции.

Если вы пропустите это предупреждение, то вы обязаны предоставить экземпляр правильного класса в качестве получателя (или, если вы хотите взломать, по крайней мере, предоставить объекту достаточно места, выделенного для переменных экземпляра и т. Д.) , Наименьшая ошибка при этом: вы можете испортить память объекта и рано или поздно вывести из строя виртуальную машину, а также потерять часть своей работы, если у вас было время сохранить поврежденное изображение до фатального сбоя ...

Итак, одним словом, подумайте о том, чтобы отправлять сообщения объектам, а не вызывать метод; пусть объекты сами решают, какой метод выполнять, это не наше дело, Smalltalk - это сообщения до конца.

...