如何从无限序列中获得“Single <t>”?

时间:2017-11-15 13:54:19

标签: swift macos rx-swift

问题

给定Observable<NaturalNumber>代表所有自然数的序列。

示例1 (在this answer中解决,请查看下一个示例)

如何获得包含范围内所有数字的和表达式和结果的Single<String>? 例如:10至13的范围 - > “11 + 12 = 23”

到目前为止,这是我所拥有的(并且不喜欢):

let min = 10
let max = 15
//No special meaning, just to add an "error" case
let maxAllowed = 20

Single<String>.create(subscribe: { (complete) -> Disposable in
    return Observable.range(start: 0, count: Int.max)
        .filter { min < $0 && max > $0 }
        .scan([], accumulator: { (acc: [Int], number) in
            guard number < maxAllowed else {
                complete(.error(SomeError()))
                return []
            }
            return acc + [number]
        })
        .single { $0.count == (max - min - 1) }
        .map { range in
            let sum = range.reduce(0, +)
            return range.reduce("", { (acc, number) -> String in
                if number == (max - 1) {
                    return acc + " + \(number) = \(sum)"
                } else {
                    return acc + "\(acc != "" ? " + " : "")\(number)"
                }
        })}
        .subscribe(onNext: { complete(.success($0)) })
}).subscribe(onSuccess: { (s) in
    print("Success: \(s)") // "Success: 11 + 12 + 13 + 14 = 50"
}, onError: { (e) in
    print("Error")
}).disposed(by: d)

示例2

给定struct Point3D需要从大小为3的数组构造。如何获得输入向量中的值应该在指定范围内的Single<Point3D>

let min = 10
let max = 15
//No special meaning, just to add an "error" case
let maxAllowed = 20

struct Point3D {
    let x: Int
    let y: Int
    let z: Int

    init(vector: [Int]) {
        x = vector[0]
        y = vector[1]
        z = vector[2]
    }
}

Single<Point3D>.create(subscribe: { (complete) -> Disposable in
    return Observable.range(start: 0, count: Int.max)
        .filter { min < $0 && max > $0 }
        .scan([], accumulator: { (acc: [Int], number) in
            guard number < maxAllowed else {
                complete(.error(SomeError()))
                return []
            }
            return acc + [number]
        })
        .single { $0.count == 3 }
        .map(Point3D.init(vector:))
        .subscribe(onNext: { complete(.success($0)) })
}).subscribe(onSuccess: { (s) in
    print("Success: \(s)") // "Success: Point3D #1(x: 11, y: 12, z: 13)"
}, onError: { (e) in
    print("Error")
}).disposed(by: d)

请记住: 这是我所遇到的一个真正问题的抽象,涉及来自蓝牙连接的无限值序列。所以请尽量避免改变问题而不是解决方案的答案,除非你认为上述情况有意义。

目的

我想要提取的模式是:从无限序列中选择一些值,并构造一个只包含这些值的新值。收集完所有值后,使用.success(allValues)

完成

我想要改进的例子是:

  • 需要Single<T>.create()包装器。只有在某些情况下能够完成和出错(示例中的maxAlloed)。此外,我不确定asSingle()运算符是否在返回值(?)后完成返回的Single
  • 使用filterscansingle从原始序列中获取值并控制有限序列应何时完成。想一想示例2

由于

1 个答案:

答案 0 :(得分:0)

如果我已正确理解问题(将无限序列的连续部分缩减为Single),我将撰写以下内容:

  • 跳过元素直到开始
  • 将元素带到最后
  • 减少
  • 转换为单张

在用于求和的代码中(但对于任何无限序列,即使是蓝牙事件也应该如此):

let sum = Observable.range(start: 0, count: Int.max)
    .skipWhile({ $0 <= min })
    .takeWhile({ $0 < max })
    .reduce(0, accumulator: +)
    .asSingle()

// Can't have any errors in the above code, but to demonstrate the general case
sum.subscribe(onSuccess: { print($0) },
              onError: { (e) in print("Error: \(e)") })
    .disposed(by: d)

我不太清楚maxAllowed在原始代码中做了什么(这可能是复杂性),但如果值超出范围则抛出错误,那么你只是扔进reduce累加器。或者您可以在中间添加一个标识map,只查找无效值并在这些情况下抛出错误。