Блок smalltalk — могу ли я явно установить возвращаемое значение и остановить выполнение блока?

Возвращаемое значение #value: message при отправке в блок — это значение последнего предложения в этом блоке. Таким образом, [ 1 + 2. 3 + 4. ] value оценивается как 7. Иногда мне трудно его использовать. Есть ли способ явно установить возвращаемое значение и остановить выполнение блока?

В качестве упражнения попробуйте переписать этот блок, не используя мое воображаемое сообщение #return:, и посмотрите, насколько уродливым оно получится. Я должен что-то упустить.

[ :one :two |
  one isNil ifTrue: [ two isNil ifTrue: [ self return: nil ] ifFalse: [ self return: true ] ].
  two ifNil: [ self return: false ].

 (one > two)
  ifTrue: [ self return: true ]
  ifFalse: [ (one < two)
              ifTrue: [ self return: false ]
              ifFalse: [ self return: nil ]
            ].
]

РЕДАКТИРОВАТЬ: self return: sth действительно ерунда, но она имеет смысл на некотором уровне :)


person milan    schedule 25.09.2011    source источник


Ответы (3)


Внутри блока нет ничего похожего на защитное предложение — blah ifTrue: [^ foo], потому что ^ — это нелокальный возврат, возвращаемый из метода, вызывающего блок, а не из самого блока.

Большие блоки — как и большие объекты — должны быть реорганизованы в более мелкие, более понятные/удобные части, но иногда это не всегда возможно. Я имею в виду этот ответ, чтобы предложить варианты, которые можно попробовать, когда вы не можете действительно упростить обычными способами.

Если ваш блок действительно настолько сложен, и вы не можете сделать его проще (например, его разбиение слишком делокализует информацию), то, возможно, вы можете использовать явное возвращаемое значение. В частности, если ваш блок не возвращает nil, вы можете сделать что-то вроде

[:one :two | | result |
    result := (one isNil and: [two isNil]) ifTrue: [false].
    result ifNil: ["do one thing, possibly setting result"].
    result]

Если ваш блок может возвращать nil, вам понадобится другое сигнальное значение:

[:one :two | | result marker |
    result := marker := Object new.
    (result == marker) ifTrue: ["do one thing, possibly setting result"].
    result]

Наконец - и я не решаюсь предложить это - вы могли бы сделать это:

[1 + 2.
thisContext return: 5.
3 + 4] value

который возвращает 5.

(Проверка того, как это взаимодействует с ^ и встроенными селекторами, такими как #ifTrue:ifFalse:, оставлена ​​читателю в качестве упражнения.)

person Frank Shearar    schedule 25.09.2011

Похоже, ваш код пытается обрабатывать ноль как значение бесконечность при сравнении один и два. Следующий код может быть более читаемым в зависимости от контекста:

a := [:one :two |
    | x y |
    x := one ifNil: [Float infinity].
    y := two ifNil: [Float infinity].
    (x = y) ifTrue: [nil] ifFalse: [x > y]]

Полезная функция #ifTrue:ifFalse:, #ifNil:ifNotNil: и подобных методов тестирования заключается в том, что они возвращают значение оцениваемого блока. например (4 > 1) ifTrue: ['greater'] ifFalse: ['not-greater'] оценивается как 'больше'. Эта функция часто позволяет вернуть значение из вложенного блока в хвостовой позиции.

Когда код внутри блока становится слишком сложным, я предлагаю реорганизовать его в метод. Но посмотрите ответ Фрэнка для обходных путей.

Изменить:

Как указано в комментариях, приведенный выше код предполагает числа. Я также придумал кое-что, что работает с другими сопоставимыми объектами:

a:=
[ :one :two |
  true caseOf: {
    [one = two]->[nil].
    [one isNil]->[true].
    [two isNil]->[false]
  } otherwise: [one>two]]

Эта конструкция #caseOf: используется редко, но она определенно лучше, чем thisContext return:

person Alex Jasmin    schedule 25.09.2011
comment
Если вы оцените этот блок с помощью value: nil value: nil, вы получите nil, а не infinity. - person milan; 26.09.2011
comment
@milan Когда один и два равны нулю, вы получаете ноль как с моим, так и с вашим кодом. В вашем коде: [ :one :two | one isNil ifTrue: [ two isNil ifTrue: [ self return: nil ] ... в моем коде (бесконечность = бесконечность), поэтому он возвращает ноль - person Alex Jasmin; 26.09.2011
comment
Ох, хорошо. Вы почти правильно поняли, не ответив на настоящий вопрос, который я задал :) Но мой код работает для строк и всего остального. - person milan; 26.09.2011
comment
Верно. Я думал только о числах. ;-) - person Alex Jasmin; 26.09.2011
comment
@milan Я добавил еще одну версию, используя #caseOf:otherwise: - person Alex Jasmin; 26.09.2011

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

Object>>exitThru: aBlock
    ^aBlock value: [:result | ^result]

Теперь давайте посмотрим, как его использовать:

| aBlock |
aBlock :=   [ :one :two |
    self exitThru: [:exit |
        one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
        two isNil ifTrue: [ exit value: false ].
        one > two ifTrue: [ exit value: true ].
        one < two ifTrue: [ exit value: false ].
        exit value: nil] ].

#(('abc' nil) (nil nil) (nil 'def') ('y' 'abc') ('y' 'y') ('y' 'z'))
    collect:
        [:pair |
        aBlock value: pair first value: pair last ]
-> #(false nil true true nil false)

EDIT моя первая версия была излишне сложной, не могу вспомнить, что привело меня к дополнительной косвенности:

| aBlock |
aBlock :=  [:wrapOne :wrapTwo |
    self exitThru: [:exit |
        [ :one :two |
        one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
        two isNil ifTrue: [ exit value: false ].
        one > two ifTrue: [ exit value: true ].
        one < two ifTrue: [ exit value: false ].
        exit value: nil ]
            value: wrapOne value: wrapTwo ] ].

Что ж, скорее смешно, чем полезно, надеюсь, вы найдете более простой и выразительный способ кодирования.

person aka.nice    schedule 17.07.2012
comment
+1 Умная штука. Люди должны проверить ссылку для лаконичный пример. - person Alex Jasmin; 27.07.2012