问题描述
我正在为 Swift/Combine 开发一个发布商
给定输入流,我想记录最大值。 如果下一个数字较小,则从最后记录的最大值中取出一个并发出。
Input: [1,2,3,4,5,1]
Output: [1,2]
我可以使用以下代码轻松完成此操作,但是,我真的不喜欢实例变量
var lastMaxInstanceValue: Float = 0
publisher
.map { newValue
if newValue > lastMaxInstanceValue {
lastMaxInstanceValue = newValue
} else {
lastMaxInstanceValue = max(0,lastMaxInstanceValue - 1)
}
}
.assign(to: \.percentage,on: self)
.store(in: &cancellables)
所以我在这里写了一个发布者/订阅者,它封装了上面的 map
部分:
https://github.com/nthState/FallingMaxPublisher
对于我的发布者,代码变成:
publisher
.fallingMax()
.assign(to: \.percentage,on: self)
.store(in: &cancellables)
我的问题是,是否需要我的 GitHub 发布者?可以在没有额外变量的情况下计算我想要的值吗?
解决方法
您可以使用 scan
运算符来实现此目的。 Scan
存储从每个上游值和当前累积值计算的累积值。但是,您需要给它一个初始值;根据您的示例,我使用了 0
:
publisher
.scan(0,{ max,value in
value >= max ? value : max - 1
})
您可以为 fallingMax
实现 SignedInteger
运算符 - 就像在您的 github 中一样 - 像这样:
extension Publisher where Output: SignedInteger {
func fallingMax(initial: Output = 0,fadeDownAmount: Output = 1
) -> AnyPublisher<Output,Failure> {
self.scan(initial,value in
value >= max ? value : max - fadeDownAmount
})
.eraseToAnyPublisher()
}
}
根据@Rob 的建议,如果您不想提供初始值,而是使用第一个值作为初始输出,您可以使用 Optional
(注意 .compactMap
将其恢复为非可选):
extension Publisher where Output: SignedInteger {
func fallingMax(initial: Output? = .none,value in
max.map { value >= $0 ? value : $0 - fadeDownAmount } ?? value
})
.compactMap { $0 }
.eraseToAnyPublisher()
}
}