Smalltalk 数组中项的条件交换
我有一个类型a、B和C的项目集合。 我想处理集合并交换所有A和B对,但是如果有C(也是集合),我想递归地处理它 所以 将被翻译成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实现 我正在寻找可读的,而不是高性能的解决方案。这里有一个没有递归的解决方案,它使用
#(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以某种方式实现”—我不会这么快就忽略了基本的问题。毕竟,如果你看一下(
)的实现,那正是它们所做的,因为你在标签中提出了问题,所以非常欢迎你为“Collections API”贡献你的新方法,这样我们都可以从中受益#overlappingPairsDo:
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:
进行迭代来确定是否应交换两个项| 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