Smalltalk 数组中项的条件交换

Smalltalk 数组中项的条件交换,smalltalk,pharo,Smalltalk,Pharo,我有一个类型a、B和C的项目集合。 我想处理集合并交换所有A和B对,但是如果有C(也是集合),我想递归地处理它 所以 将被翻译成 #(A1 B1 A2 B2 A3 #(A4 B3 A5) ) 交换不是可传递的,因此#(B1 B1 B2)将被转换为#(B1 A1 B2),而不是#(B1 B2 A1) 我想使用重叠pairsdo:,但问题是第二个元素总是被处理两次 这是否可以通过集合API而不用原语forloops实现 我正在寻找可读的,而不是高性能的解决方案。这里有一个没有递归的解决方案,它使用

我有一个类型a、B和C的项目集合。 我想处理集合并交换所有A和B对,但是如果有C(也是集合),我想递归地处理它

所以

将被翻译成

#(A1 B1 A2 B2 A3 #(A4 B3 A5) )
交换不是可传递的,因此
#(B1 B1 B2)
将被转换为
#(B1 A1 B2)
,而不是
#(B1 B2 A1)

我想使用重叠pairsdo:,但问题是第二个元素总是被处理两次

这是否可以通过集合API而不用原语forloops实现


我正在寻找可读的,而不是高性能的解决方案。

这里有一个没有递归的解决方案,它使用了两步方法:

result := OrderedCollection new.
#(1 3 4 6 7) 
    piecesCutWhere: [ :a :b | a even = b even ]
    do: [ :run |
        result addAll: ((result isEmpty or: [ result last even ~= run first even ])
            ifTrue: [ run ]
            ifFalse: [ run reverse ]) ].

result asArray = #(1 4 3 6 7) "--> true"
因此,首先,我们将集合拆分到任何可以交换的地方。然后,在第二步中,我们仅在结果集合的最后一个元素仍然允许交换时交换

向其中添加递归应该是直截了当的。

我认为我下面的解决方案应该满足您的要求,但前面有一些注意事项:

  • “需求”似乎有点人为——比如,我很难想象一个用例,在这个用例中,您需要这种交换。当然,这可能是因为我缺乏想象力,也可能是因为你试图简化问题
  • 我认为,一个合适的解决方案应该创建所需的对象,以便代码可以移动到它所属的位置。我的解决方案只是(主要)将其放入类端方法中进行演示
  • 您要求“在不使用[-]循环的基元的情况下,通过[Collection API以某种方式实现”—我不会这么快就忽略了基本的问题。毕竟,如果你看一下(
    #overlappingPairsDo:
    )的实现,那正是它们所做的,因为你在标签中提出了问题,所以非常欢迎你为“Collections API”贡献你的新方法,这样我们都可以从中受益
为了提供帮助,我添加了一个带有两个类端方法的类
SwapPairsDemo
。第一个只是一个助手,因为出于演示目的,我们正在使用示例中的
数组
对象,它们包含
ByteSymbol
实例作为
a
B
类型,我们只想将它们与
C
集合类型区分开来,
ByteSymbol
s本身当然是集合,所以让我们假装它们不仅仅是为了这个练习

isRealCollection: anObject

^anObject isCollection
    and: [anObject isString not
    and: [anObject isSymbol not]]
第二个方法保存代码以显示交换并允许递归:

swapPairsIn: aCollection ifTypeA: isTypeABlock andTypeB: isTypeBBlock

| shouldSwapValues wasJustSwapped |
shouldSwapValues := OrderedCollection new: aCollection size - 1 withAll: false.
aCollection overlappingPairsWithIndexDo: [:firstItem :secondItem :eachIndex |
    (self isRealCollection: firstItem)
        ifTrue: [self swapPairsIn: firstItem ifTypeA: isTypeABlock andTypeB: isTypeBBlock]
        ifFalse: [
            shouldSwapValues at: eachIndex put: ((self isRealCollection: secondItem) not
                and: [(isTypeABlock value: firstItem)
                and: [isTypeBBlock value: secondItem]])
            ]
    ].
(self isRealCollection: aCollection last)
    ifTrue: [self swapPairsIn: aCollection last ifTypeA: isTypeABlock andTypeB: isTypeBBlock].
wasJustSwapped := false.
shouldSwapValues withIndexDo: [:eachBoolean :eachIndex |
    (eachBoolean and: [wasJustSwapped not])
        ifTrue: [
            aCollection swap: eachIndex with: eachIndex + 1.
            wasJustSwapped := true
            ]
        ifFalse: [wasJustSwapped := false]
    ]
这有点难,我通常会重构一个这么大的方法,加上你可能想处理nil、空列表等,但希望你能找到解决问题的方法。该代码包括三个步骤:

  • 构建布尔值集合(大小小于主集合的大小),通过使用
    重叠pairswithindexdo:
    进行迭代来确定是否应交换两个项
  • 这个迭代本身不处理最后一个元素,所以我们需要在单独的步骤中处理这个元素,可能是一个集合
  • 最后,我们使用我们的布尔集合来执行交换,但是如果我们上一次刚刚交换过,我们就不会再交换(我想这就是你所说的交换不可传递的意思)
  • 要运行代码,您需要提供您的集合和一种方法来判断事物是类型“a”还是“B”-我刚刚使用了您的示例,所以我只是问他们是否以这些字母开头-显然,可以用适合您的用例的任何东西来代替

    | collection target isTypeA isTypeB  |
    collection := #(A1 A2 B1 B2 A3 #(A4 A5 B3) ).
    target := #(A1 B1 A2 B2 A3 #(A4 B3 A5) ).
    isTypeA := [:anItem | anItem beginsWith: 'A'].
    isTypeB := [:anItem | anItem beginsWith: 'B'].
    SwapPairsDemo swapPairsIn: collection ifTypeA: isTypeA andTypeB: isTypeB.
    ^collection = target
    

    在工作区中检查此项将返回
    true
    ,即
    集合上的交换已执行,因此它现在与
    目标

    相同。抱歉,我还不清楚协议的具体内容。至于
    #重叠pairsDo:
    ,您是否考虑过使用
    #组:atATimeDo:
    #pairsDo:
    ?每个项目只通过一次。协议?
    pairsDo:
    的问题是它可能会跳过:如果我有
    #(a1a2b1b2)
    ,那么它将被配对为
    #(a1a2)
    #(b1b2)
    谢谢你详尽的回答。我认为你的最初观点在这里也许是最重要的。考虑到您的解决方案有多么复杂,我认为我应该退一步,调查我的数据结构。因为如果这一步过于复杂,也许我应该退一步,更早地解决或准备数据。很高兴您能这样看——通常,为您的特定业务模型提供正确的数据结构会大大简化您的解决方案,因为您可以“抽象”基本的细节,所以他们不会妨碍你的方式。祝你好运
    | collection target isTypeA isTypeB  |
    collection := #(A1 A2 B1 B2 A3 #(A4 A5 B3) ).
    target := #(A1 B1 A2 B2 A3 #(A4 B3 A5) ).
    isTypeA := [:anItem | anItem beginsWith: 'A'].
    isTypeB := [:anItem | anItem beginsWith: 'B'].
    SwapPairsDemo swapPairsIn: collection ifTypeA: isTypeA andTypeB: isTypeB.
    ^collection = target