【发布时间】:2020-01-10 05:09:42
【问题描述】:
我想重构一些惰性函数 swift。 为了解释这种情况,我先解释一下等价的急切情况:
let numbers = 1...10
do {
print("==================== EAGER INLINE =============================")
/// We start with a series of transformation on an array:
let result
= numbers
.filter { $0 >= 5 } /// Drop numbers less than 5
.map { $0 * $0 } /// Square the numbers
.flatMap { [$0, $0+1] } /// Insert number+1 after each number
.filter { $0 % 3 != 0 } /// Drop multiples of 3
print(result)
/// [25, 26, 37, 49, 50, 64, 65, 82, 100, 101]
/// which is [5^2, 5^2+1, 6^2+1, 7^2, 7^2+1, 8^2, 8^2+1, 9^2+1, 10^2, 10^2+1]
/// (Note 6^2 and 9^2 missing because they are divisible by 3)
}
我们可以将 map 和 flatMap 重构为一个单独的函数:
extension Array where Element == Int {
func squareAndInsert() -> [Int] {
self
.map { $0 * $0 }
.flatMap { [$0, $0+1] }
}
}
do {
print("==================== EAGER REFACTOR =============================")
let result
= numbers
.filter { $0 >= 5 }
.squareAndInsert()
.filter { $0 % 3 != 0 }
print(result)
/// Gives exactly the same result: [25, 26, 37, 49, 50, 64, 65, 82, 100, 101]
}
所以现在我们将懒惰地重复这个过程。 第一个内联:
do {
print("==================== LAZY INLINE =============================")
let result: some LazySequenceProtocol /// ": some LazySequenceprotocol" not strictly
/// required but without it my compiler grumbled about complexity so this is to give the
/// compiler a nudge in the right direction.
= numbers
.lazy /// Note the ".lazy" added here to make the array lazy.
.filter { $0 >= 5 }
.map { $0 * $0 }
.flatMap { [$0, $0+1] }
.filter { $0 % 3 != 0 }
print(result)
}
哪些打印:
LazyFilterSequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<ClosedRange<Int>>, Int>, Array<Int>>>>(_base: Swift.FlattenSequence<Swift.LazyMapSequence<Swift.LazyMapSequence<Swift.LazyFilterSequence<Swift.ClosedRange<Swift.Int>>, Swift.Int>, Swift.Array<Swift.Int>>>(_base: Swift.LazyMapSequence<Swift.LazyMapSequence<Swift.LazyFilterSequence<Swift.ClosedRange<Swift.Int>>, Swift.Int>, Swift.Array<Swift.Int>>(_base: Swift.LazyMapSequence<Swift.LazyFilterSequence<Swift.ClosedRange<Swift.Int>>, Swift.Int>(_base: Swift.LazyFilterSequence<Swift.ClosedRange<Swift.Int>>(_base: ClosedRange(1...10), _predicate: (Function)), _transform: (Function)), _transform: (Function))), _predicate: (Function))
哎呀!
乍一看看起来相当惊人,但这是正确的,因为与急切的结果不同,它是一个 Ints 数组,惰性结果是一个迭代器,当我们要求它时它会为我们提供下一个数字,这需要知道如何将所有函数调用返回到初始序列。这就是这种类型所描述的。很好,现在我们像过去一样有了“some”关键字,如果我们想输入一个显式类型,我们必须输入上面的所有内容,这有点拗口!!
要查看数字列表,我们需要强制计算它们,我们可以将惰性序列放入数组中:print(Array(result))
这给出了与以前完全相同的结果:[25, 26, 37, 49, 50, 64, 65, 82, 100, 101]
现在是挑战。
我想以与处理 Eager 代码相同的方式重构惰性代码。
squareAndInsert 需要将LazySequenceProtocol<Int> 转换为some LazySequenceProtocol,所以我尝试了下面的代码,但得到了各种编译错误:
extension LazySequenceProtocol where Element == Int {
func squareAndInsertLazy() -> some LazySequenceProtocol {
self
.map { $0 * $0 }
.flatMap { [$0, $0+1] }
}
}
do {
print("==================== LAZY REFACTOR =============================")
let result: some LazySequenceProtocol // Error 1: Property declares an opaque return type, but cannot infer the underlying type from its initializer expression
= numbers
.lazy
.filter { $0 >= 5 }
.squareAndInsertLazy() // Error 2: Value of type '[Int]' has no member 'squareAndInsertLazy'
.filter { $0 % 3 != 0 } // Error 3: Protocol type 'Any' cannot conform to 'LazySequenceProtocol' because only concrete types can conform to protocols
// Error 4: Value of type 'Any' has no member 'filter'
print(result)
}
如果我修复其他错误,我认为错误 1 可能会消失。 我想知道错误 2 是否意味着试图将惰性序列传递给 squareAndInsertLazy 会迫使人们急于求成,这意味着 [Int] 被呈现给 squareAndInsertLazy。 我不知道如何前进。
任何帮助表示赞赏。
【问题讨论】:
标签: swift functional-programming refactoring lazy-evaluation lazy-sequences