Scala样例类集合字段复制问题:foldLeft失效原因及实现方案
问题解答
1. 为什么foldLeft的缓冲区始终关联原state而非前一次迭代的缓冲区?
问题出在你addABCfold方法里的匿名函数写法:
bufferABC.foldLeft(this){case (state,str) => state.copy(list = str::list) }
这里的list没有明确指定归属,它直接引用的是调用addABCfold方法的当前SimpleState实例的list成员(也就是this.list),而不是foldLeft累加器参数state中的list。
举个具体例子:当你调用state.addA.addB.addC.addABCfold时,this对应的实例的list是List(6,5,4)。foldLeft的每次迭代都会发生:
- 第一次迭代(str="A"):你创建的新state是
state.copy(list = "A" :: this.list)→ 也就是List(A,6,5,4) - 第二次迭代(str="B"):你仍然用
this.list(还是List(6,5,4))来拼接,新state变成List(B,6,5,4) - 第三次迭代(str="C"):同样用
this.list,最终得到List(C,6,5,4)
每一次迭代都没有用上前一次返回的state的list,而是始终基于原实例的list,所以最终只有最后一个元素被保留下来,看起来缓冲区没在累积。
2. 如何正确实现addABC方法?
你只需要在匿名函数里明确引用累加器state的list即可,这样每次迭代都会基于前一次的结果进行累积:
方式一:修正foldLeft实现
def addABC = bufferABC.foldLeft(this) { case (currentState, str) => currentState.copy(list = str :: currentState.list) }
这样调用后,得到的state的list会是List(C,B,A,6,5,4),符合你"顺序无关"的要求。如果希望顺序和addABCiter一致(A、B、C在前),可以把拼接方式改成currentState.list :+ str,或者反转bufferABC再foldLeft:
// 保持A、B、C的顺序在原有元素前面 def addABC = bufferABC.reverse.foldLeft(this) { case (currentState, str) => currentState.copy(list = str :: currentState.list) }
方式二:如果允许直接拼接(参考实现)
其实你的addABCiter已经是正确的,如果你必须用遍历的方式(比如后续逻辑更复杂),上面的foldLeft方式就是标准的函数式实现。
测试一下修正后的代码:
val state = SimpleState() val addABCfoldFixed = state.addA.addB.addC.addABC // 输出:SimpleState(List(C, B, A, 6, 5, 4))
内容的提问来源于stack exchange,提问作者Eldar Nezametdinov
相关产品推荐
相关产品推荐

