LoginSignup
16
4

More than 3 years have passed since last update.

ズンドコキヨシ with Combine

Last updated at Posted at 2019-10-26

TL; DR

  • 元ネタはこちら

0. ゴールの確認

  • 「ずん」「どこ」「き・よ・し!」の出力はすべてreceiveValueで受け取るStringとする(= assignしようと思えばできる状態を目指す)。記事ではinput:○○○○○○で表現する。
  • Publisherのprint(_:to:)はデバグ用に使う(回答としてカウントしない)
  • 一度「き・よ・し!」を出力したら処理を終了する

1. ずんどこ川の水源を作る

まずは「ずん」または「どこ」が湧き出る水源を作っていく。
勢いよく湧き出て欲しいので、0.1秒間隔で出力していこう。

iOS 13から追加されたTimer.publish(every:tolerance:on:in:options:)を使っていく。

    let ずんどこ水源 =
        Timer.publish(every: 0.1, on: .main, in: .default)
            .autoconnect()
            .map { _ in Int.random(in:0..<2) == 0 ? "ずん" : "どこ" }
            .share()

    self.cancellable =
        ずんどこ水源
        .sink(receiveCompletion: { _ in
            print("===完===")
        }, receiveValue: {
            print("input: \($0)")
        })

結果はこちら。
zundoko-fountain

良い。すごく良い。勢いある湧出量だ。

2. センサー付きの水門づくり

上述のとおり、ずんどこ水源からは「ずん」または「どこ」が1語ずつ流れてくる。

検知したいのは「ずん、ずん、ずん、ずん、どこ」の5要素なので、FIFO方式でずんどこが5つだけたまる溜池を作る。
そして、溜まった要素がお目当てのパターンか判定するセンサーを作る。
もしパターンを検知できたら水門を閉じる。
いたずらにずんどこを溜めない。

    let センサー付き水門 =
        ずんどこ水源
            .scan([], { (reservoir: [String], element: String) -> [String] in
                // 溜池
                var newReservoir = Array(reservoir + [element])
                if newReservoir.count > 5 {
                    newReservoir.removeFirst()
                }
                return newReservoir.prefix(5).map { $0 }
            })
            .allSatisfy { $0 != ["ずん", "ずん", "ずん", "ずん", "どこ"] }
            .map { _ in  }
            .share()

3. 検知後「き・よ・し!」を知らせる

()が流れてくる仕組みを活用し、「き・よ・し!」と流れるスピーカーを作ろう。

   let スピーカー = センサー付き水門.map { _ in "き・よ・し!" }

これでも悪くはない。

しかし、贅沢を言えば「どこ」と「き・よ・し!」の間には"ため"が欲しい。
そこで1秒くらい"ため"を作ろう。
結果には影響を与えない形で"ため"を確認できるようprintしておこう。

    let スピーカー = センサー付き水門.map { _ in "き・よ・し!" }
                                .handleEvents(receiveOutput: { _ in
                                    print("(せーの...!)")
                                })
                                .delay(for: .seconds(1.0), scheduler: RunLoop.main)

4. 整備されたずんどこ川の全貌

野川状態だった、ずんどこ川にも必要な設備が揃った。
あとはこれらをうまく組み合わせていく。
これで、河川管理もバッチリだ。

let ずんどこ水源 =
    Timer.publish(every: 0.1, on: .main, in: .default)
        .autoconnect()
        .map { _ in Int.random(in:0..<2) == 0 ? "ずん" : "どこ" }
        .share()

let センサー付き水門 =
    ずんどこ水源
        .scan([], { (reservoir: [String], element: String) -> [String] in
            // 溜池
            var newReservoir = Array(reservoir + [element])
            if newReservoir.count > 5 {
                newReservoir.removeFirst()
            }
            return newReservoir.prefix(5).map { $0 }
        })
        .allSatisfy { $0 != ["ずん", "ずん", "ずん", "ずん", "どこ"] }
        .map { _ in  }
        .share()

let スピーカー = センサー付き水門.map { _ in "き・よ・し!" }
                            .handleEvents(receiveOutput: { _ in
                                print("(せーの...!)")
                            })
                            .delay(for: .seconds(1.0), scheduler: RunLoop.main)

self.cancellable =
    ずんどこ水源
    .prefix(untilOutputFrom: センサー付き水門) // センサーが検知するまで、ずんどこを流す
    .merge(with: スピーカー) // 検知したら、き・よ・し!を流す
    .sink(receiveCompletion: { _ in
        print("===完===")
    }, receiveValue: {
        print("input: \($0)")
    })

よし、最後にこれを動かしてみよう。
...

....

.....

output

💪💪💪💪💪💪💪💪💪💪💪💪💪💪💪

あとがき

ふざけているようですが、トライしてみるとオペレーターの挙動に詳しくなります
もっと良い実装例があったら@mshrwtnbまで教えていただけると勉強になります
https://github.com/mshrwtnb/zundoko-combine にソースを置きました。

以上

16
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
4