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

Я работаю над простой настольной игрой в Pharo, и у меня на доске есть метод, добавляющий объекты в ячейку. Ячейки — это просто словарь точек на объектах.

В рамках метода я хотел, чтобы Point был больше нуля, но меньше ширины и высоты доски, другими словами, он должен быть на доске. Как лучше всего это сделать?

Моя текущая попытка выглядит так:

at: aPoint put: aCell

((((aPoint x > self numberOfRows) 
    or: [aPoint x <= 0]) 
    or: [aPoint y > self numberOfColumns ]) 
    or: [aPoint y <= 0]) 
    ifTrue: [ self error:'The point must be inside the grid.' ].

self cells at: aPoint put: aCell .

Какая-то шепелявость со всеми этими скобками! Но я не могу использовать короткое замыкание or: без закрытия каждого выражения, чтобы оно оценивалось как логическое, а не как блок (или как сообщение or:or:or:or:). Вместо этого я мог бы использовать бинарный оператор | и отказаться от короткого замыкания, но это не кажется правильным.

Итак, как правильно справиться с этим в стиле Smalltalk?


person CodexArcanum    schedule 10.12.2010    source источник
comment
Помните, что вы можете автоматически отформатировать код, удалив все лишние скобки с помощью Cmd+r или Ctrl+r или щелкнув правой кнопкой мыши тело метода и выбрав формат.   -  person unom    schedule 18.02.2015


Ответы (3)


Обычно or: вложены следующим образом:

(aPoint x > self numberOfRows 
    or: [ aPoint x <= 0  
    or: [ aPoint y > self numberOfColumns
    or: [ aPoint y <= 0 ] ] ])
        ifTrue: [ self error: 'The point must be inside the grid.' ].

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

В качестве альтернативы вы можете использовать assert: или assert:description:, которые определены в Object:

self
    assert: (aPoint x > self numberOfRows 
        or: [ aPoint x <= 0  
        or: [ aPoint y > self numberOfColumns
        or: [ aPoint y <= 0 ] ] ])
    description: 'The point must be inside the grid.'
person Lukas Renggli    schedule 10.12.2010
comment
Ах, это прекрасно. Спасибо и за синтаксис assert, я думал, что assert может быть полезен. - person CodexArcanum; 11.12.2010

Каждый раз, когда вещи настолько сильно вложены, пора вызывать другой метод.

isValidPoint: aPoint
  aPoint x > self numberOfRows ifTrue: [^ false].
  aPoint x <= 0 ifTrue: [^ false].
  aPoint y > self numberOfColumns ifTrue: [^ false].
  aPoint y <= 0 ifTrue: [^ false].
  ^ true.

В общем, ваши методы должны быть относительно плоскими. Если нет, то время для рефакторинга.

person Randal Schwartz    schedule 11.12.2010
comment
Это отличный совет, которым я действительно в конечном итоге воспользовался. Особенно, когда более поздний метод требует двух действительных точек, и мне нужно было проверить их обе! - person CodexArcanum; 14.12.2010

Вы можете просто предварительно заполнить словарь «ячейки» всеми точками, которые допустимы в пределах диапазона, то есть где-то в инициализации, которую вы поместили:

1 to: numberOfRows do: [:y |
  1 to: numberOfCols do: [:x |
     cells at: x@y put: dummy "or nil " ] ]

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

at: aPoint put: aCell

   self cells at: aPoint ifAbsent: [ self error: 'The point must be inside the grid.' ].
   self cells at: aPoint put: aCell .

Также есть вспомогательный метод #between:and: , который можно использовать для минимизации беспорядка в коде:

((aPoint x between: 1 and: self numCols) and: [
 aPoint y between: 1 and: self numRows ]) ifFalse: [ ... bummer ... ]
person Igor Stasenko    schedule 11.12.2010
comment
Я забыл о between:and:, это гораздо более чистый способ выразить вещи. Спасибо! - person CodexArcanum; 11.12.2010