在 Smalltalk 中,如何 select 数组中的字符串和整数

In Smalltalk, how to select both strings and integers from an array

使用Pharo,我有一个集合,例如

array := #('up' '4' 'b').

并且我想使用 select: 创建一个仅包含数字(前提是它们小于 20)和特定字符串的集合 - 在本例中为 'up' 和'4'

我试过了:

array select: [:each | (each='up') | (each asInteger < 50)].  

这导致 MessageNotUnderstood 因为 receiver of "<" is nil.

我想我必须创建一个局部变量 x:= each asInteger,但无法解决。

使用select:一次只能访问数组的一个元素。从你的代码中我看到你想同时访问数组的两个元素。在这种情况下,您可以使用 pairsDo:。例如,下面的代码将所有以字符串 'up'.

开头的数字放入一个数组中
numbers := OrderedCollection new.
#('up' '5' 'b') pairsDo: [ :first :second |
    ((first = 'up') and: [ second asInteger notNil ])
        ifTrue: [ numbers add: second asInteger ] ]

然后您可以使用 select: 仅获取小于 20 的数字:

numbers select: [:each| each < 20].

您得到 MessageNotUnderstood 是因为您的代码正在尝试测试 'b' asInteger(即 nil,因为 b 不是整数)是否是< 20(或 < 50;您的文本和代码中有不同的数字)。所以你需要做的就是在你把它当作数字之前测试每个数组项是否是一个数字。

这应该适用于 Pharo 工作区:

| array |
array := #('up' '4' 'b' '22').
^array select: [ :each | each = 'up'
    or: [ each isAllDigits and: [ each asInteger < 20 ] ] ]

如预期的那样检查结果 #('up' '4')

请注意,我正在检查每个字符串是否为 "made up of all digits",如果是,则只进行比较。另请注意,我使用的是 or:and:,它们仅在需要时评估块参数,而 |& 对双方都进行评估。

你也可以创建一个局部变量,就像你说的那样,但它看起来有点笨拙(我不会调用变量 x... 除非它是一个 x 坐标) :

| array |
array := #('up' '4' 'b' '22').
^array select: [ :each |
    each = 'up' or: [
        | integerOrNil |
        integerOrNil := each asInteger.
        integerOrNil notNil and: [ integerOrNil < 20 ] ] ]

你得到这个错误是因为第二条比较语句一直在执行:消息 | in pharo(和任何 smalltalk)不能作为同音异义词 C 运算符工作,而是始终执行它,而不管语句第一部分的结果如何(&,顺便说一句,也是如此)。

为了获得类似的结果,您似乎假装(仅当第一个子句为真时才执行 or 子句),我们使用消息 or: and and:

你可以这样做:

array select: [ :each | 
    each = 'up'
    or: [ each = '4' 
    or: [ each isAllDigits and: [ each asInteger < 20 ] ] ] ]

请注意调用 #or: 语句的嵌套以及我首先检查数字是否可以转换为数字的事实。如果我不这样做,当我尝试将字符串转换为数字时,我将得到 nil,因为将字符串解析(转换)为数字失败(然后select 会失败)。

另一种可行的方法是:

array select: [ :each | 
    each = 'up'
    or: [ each = '4' 
    or: [ each asInteger notNil and: [ each asInteger < 20 ] ] ] ]

但我推荐第一种(利用解析器的失败来判断一个字符串是否包含数字,很容易被认为是一种利用:)

最后,如果你必须 select 超过一两个 "constant" 字符串,你可以这样做:

constants := #('up' '4').
array select: [ :each | 
    (constants includes: each)
    or: [ each asInteger notNil and: [ each asInteger < 20 ] ] ]

这可能是一个更干净的实现。