在 Smalltalk 中对消息参数强制执行某些值的最佳方法是什么?

发布于 2024-10-06 18:39:32 字数 663 浏览 9 评论 0原文

我正在 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 方式是什么?

I'm working on a simple board game in Pharo, and I've got a method on my Board that adds objects to a cell. Cells are simply a dictionary of Points on Objects.

As part of the method, I wanted to enforce that a Point should be greater than zero, but less than the width and height of the board, in other words, it should actually be on the board. What is the best way to do this?

My current attempt looks like this:

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 .

Kind of lisp-y with all those parens! But I can't use the short-circuiting or: without closing off each expression so it evaluates as a boolean and not a block (or as the or:or:or:or: message). I could use the binary operator | instead and for-go short circuiting, but that doesn't seem right.

So what is the properly Smalltalk-ish way to handle this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

两相知 2024-10-13 18:39:32

通常 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.' ].

您的嵌套是短路的,但由于对第一个参数的重复测试而效率较低(检查字节码以查看差异)。

您也可以使用在 Object 上定义的 assert:assert:description:

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.'

Typically the or: are nested like this:

(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.' ].

Your nesting is short-circuting but less efficient because of repeated tests of the first argument (check the bytecode to see the difference).

Alternative you can use assert: or assert:description: that is defined on 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.'
毁虫ゝ 2024-10-13 18:39:32

每当事情嵌套得如此严重时,就该调用另一个方法了。

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.

一般来说,你的方法应该相对平坦。如果没有,是时候重构了。

Any time things are that heavily nested, it's time to call another method.

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.

In general, your methods should be relatively flat. If not, time to refactor.

无边思念无边月 2024-10-13 18:39:32

您可以简单地用范围内有效的所有点预填充“单元格”字典,即您放置的初始化中的某个位置:

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:和: ,您可以使用它来最大程度地减少代码混乱:

((aPoint x between: 1 and: self numCols) and: [
 aPoint y between: 1 and: self numRows ]) ifFalse: [ ... bummer ... ]

You can simply prefill the 'cells' dictionary with all points which is valid within the range, i.e. somewhere in initialization you put:

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

then your method for adding a cell at given point will look as simple as:

at: aPoint put: aCell

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

There's also a helper method #between:and: , which you can use to minimize the code clutter:

((aPoint x between: 1 and: self numCols) and: [
 aPoint y between: 1 and: self numRows ]) ifFalse: [ ... bummer ... ]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文