Falling Max Swift Combine 出版商

问题描述

我正在为 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()
    }
}