在Smalltalk中检测数组中x次相同对象序列的惯用法?

时间:2015-01-16 14:31:43

标签: arrays smalltalk pharo idiomatic

在OrderedCollection或Array中检测相同对象(或具有特定匹配参数的对象)的x次序列的惯用方法是什么?

E.g。数组是否包含连续数字5的10倍?

3 个答案:

答案 0 :(得分:4)

我喜欢Uko的答案,并希望提供一个解决"匹配参数的不同解决方案"你问题的一部分。在SequenceableCollection中定义:

contains: m consecutiveElementsSatisfying: block
    | n i |
    self isEmpty ifTrue: [^m = 0].
    n := self size - m + 1.
    i := 1.
    [i <= n] whileTrue: [| j |
        (block value: (self at: i)) ifTrue: [
            j := 2.
            [j <= m and: [block value: (self at: i + j - 1)]]
                whileTrue: [j := j + 1].
            j > m ifTrue: [^true]].
        i := i + 1].
    ^false

现在,例如,以下两个表达式将评估为true

#(2 1 1 1 2) contains: 3 consecutiveElementsSatisfying: [:e | e = 1]
#(2 1 0 1 2) contains: 3 consecutiveElementsSatisfying: [:e | e squared = e]

注意:如果您希望此方法的startingAt: n版本只是在主循环之前初始化i := n而不是i := 1

编辑:

当然,我们可以在SequenceableCollection中使用以下方法完成协议:

contains: m consecutiveTimes: anObject
    ^self contains: m consecutiveElementsSatisfying: [:e | e = anObject]

示例:

#(2 1 1 1 2) contains: 3 consecutiveTimes: 1

答案 1 :(得分:3)

我说你必须遵循这样的模式:

(collectionToTest
  indexOfSubCollection: (
    Array
      new:     numberOfRepetitions
      withAll: desiredObject)
    startingAt: 1
) isZero not

也许我不了解Pharo中的一些有用方法,但是如果你定义的那些方法如下:

SequenceableCollection >> indexOfSubCollection: aSubCollection
  ^ aSubCollection indexOfSubCollection: aSubCollection startingAt: 0

SequenceableCollection >> containsSubCollection: aSubCollection
  ^ (aSubCollection indexOfSubCollection: aSubCollection) isZero not

Object >> asArrayOf: aLength
  ^ Array new: aLength withAll: self

然后可以将定义展平为:

collectionToTest containsSubCollection:
  (desiredObject asArrayOf: numberOfRepetitions)

或您的例子:

anArray containsSubCollection: (5 asArrayOf: 10)

P.S。我不确定方法名称。也许inArrayOf:可能比asArrayOf:更好,依此类推。

答案 2 :(得分:3)

获取重复对象的序列非常简单:

({ 1. 1. 2. 2. 2. 5. 5. 3. 9. 9. 9. 9. } as: RunArray) runs 
=> #(2 3 2 1 4)

如果您想测试是否有满足特定约束的运行,您可以执行以下操作:

meetsConstraint := false.
({ 1. 1. 2. 2. 2. 5. 5. 3. 9. 9. 9. 9. } as: RunArray) runsAndValuesDo: [:run :value | 
    meetsConstraint := (value = 9 and: [run > 3])].

如果要测试对象的某个属性而不是对象相等,可以通过对其执行RunArray轻松创建此属性的collect:

因此,通用解决方案看起来像这样:

SequenceableCollection >> containsRunOf: anElement withAtLeast: nElements
    (self as: RunArray) runsAndValuesDo: [:run :value | 
        (value = anElement and: [run >= nElements]) ifTrue: [^ true]].
    ^ false

然后:

({ 'aa'. 'bb'. 'c'. 'ddd'. } collect: [:each | each size])
    containsRunOf: 2 withAtLeast: 3
=> false