3

私はまだ ReactiveCocoa と関数型リアクティブ プログラミングの概念を使い始めているので、これはばかげた質問かもしれません。

ReactiveCocoa は、ライブ データのストリーム、タッチ イベント、加速度計センサー入力などに反応するように自然に設計されているようです。

ReactiveCocoa で有限インパルス応答フィルターを簡単にリアクティブな方法で適用することは可能ですか? または、そうでない場合、これを行うための最も醜いハック方法は何でしょうか? 単純な移動平均のようなものを実装するにはどうすればよいでしょうか?

理想的には Swift 2 + RA4 ソリューションを探していますが、Objective C および RA2/RA3 でこれが可能かどうかにも関心があります。

4

2 に答える 2

4

実際に必要なのは、ある種のピリオド バッファーです。これは、一定期間の値をバッファーに保持し、バッファーが容量に達したときにのみ送信を開始します (以下のコードは、takeLast オペレーターにインスパイアされています)。

extension SignalType {
    func periodBuffer(period:Int) -> Signal<[Value], Error> {
        return Signal { observer in
            var buffer: [Value] = []
            buffer.reserveCapacity(period)

            return self.observe { event in
                switch event {
                case let .Next(value):
                    // To avoid exceeding the reserved capacity of the buffer, we remove then add.
                    // Remove elements until we have room to add one more.
                    while (buffer.count + 1) > period {
                        buffer.removeAtIndex(0)
                    }

                    buffer.append(value)

                    if buffer.count == period {
                        observer.sendNext(buffer)
                    }
                case let .Failed(error):
                    observer.sendFailed(error)
                case .Completed:
                    observer.sendCompleted()
                case .Interrupted:
                    observer.sendInterrupted()
                }
            }
        }
    }
}

それに基づいて、必要なアルゴリズムにマップできます

let pipe = Signal<Int,NoError>.pipe()

pipe.0
    .periodBuffer(3)
    .map { Double($0.reduce(0, combine: +))/Double($0.count) } // simple moving average
    .observeNext { print($0) }

pipe.1.sendNext(10) // does nothing
pipe.1.sendNext(11) // does nothing
pipe.1.sendNext(15) // prints 12
pipe.1.sendNext(7) // prints 11
pipe.1.sendNext(9) // prints 10.3333
pipe.1.sendNext(6) // prints 7.3333
于 2016-01-13T13:56:06.043 に答える
1

おそらくscanシグナル演算子が探しているものです。Andy Jacobs の回答に触発されて、次のようなものを思いつきました (単純な移動平均の実装)。

  let (signal, observer) = Signal<Int,NoError>.pipe()

  let maxSamples = 3

  let movingAverage = signal.scan( [Int]() ) { (previousSamples, nextValue)  in 
    let samples : [Int] =  previousSamples.count < maxSamples ? previousSamples : Array(previousSamples.dropFirst())
    return samples + [nextValue]
  }
  .filter { $0.count >= maxSamples }
  .map { $0.average }

  movingAverage.observeNext { (next) -> () in
    print("Next: \(next)")
  }

  observer.sendNext(1)
  observer.sendNext(2)
  observer.sendNext(3)
  observer.sendNext(4)
  observer.sendNext(42)

注:averageメソッドをプロトコル拡張に移動する必要がありました。そうしないと、コンパイラは式が複雑すぎると文句を言うでしょう。私はこの答えから素敵な解決策を使用しました:

extension Array where Element: IntegerType {
    var total: Element {
        guard !isEmpty else { return 0 }
        return reduce(0){$0 + $1}
    }
    var average: Double {
        guard let total = total as? Int where !isEmpty else { return 0 }
        return Double(total)/Double(count)
    }
}
于 2016-01-16T13:30:13.107 に答える