演习是写我自己的 map()
功能结束 Collection
(不使用任何功能原语,如 reduce()
)。它应该处理这样的情况:
func square(_ input: Int) -> Int {
return input * input
}
let result = input.accumulate(square) // [1,2,3] => [1,4,9]
我的第一次尝试是:
extension Collection {
func accumulate(_ transform: (Element) -> Element) -> [Element] {
var array: [Element] = []
for element in self {
array.append(transform(element))
}
return array
}
}
这在游乐场中运行良好,但无法针对测试构建,给出错误:
Value of type '[Int]' has no member 'accumulate'
解决方案是将通用化 accumulate
方法:
extension Collection {
func accumulate<T>(_ transform: (Element) -> T) -> [T] {
var array: [T] = []
for element in self {
array.append(transform(element))
}
return array
}
}
我认识到通用版本限制较少(不要求转换返回相同的类型),但鉴于测试不需要这种通用性,为什么编译器?
出于好奇我试过:
extension Collection {
func accumulate<Element>(_ transform: (Element) -> Element) -> [Element] {
var array: [Element] = []
for element in self {
array.append(transform(element))
}
return array
}
}
这会引发迷人的构建错误: '(Self.Element) -> Element' is not convertible to '(Element) -> Element'
在 append()
声明。
所以编译器(当然)知道第一个Element是Self.Element,但不会将其他Element类型视为相同。为什么?
更新:
根据答案,似乎拒绝第一个版本是一个编译器错误,修复在XCode 9.2(我在9.1)。
但我仍然想知道是否在
func accumulate(_ transform: (Element) -> Element) -> [Element]
它会看到两种类型(Self.Element
和 Element
)或者认识到它们是一样的。
所以我做了这个测试:
let arr = [1,2,3]
arr.accumulate {
return String(describing: $0)
}
果然,得到了预期的错误: error: cannot convert value of type 'String' to closure result type 'Int'
所以正确的答案是: 只要不存在重载名称的泛型类型,编译器就会将对Element的引用视为相同。
奇怪的是,这成功了:
[1,2,3].accumulate {
return String(describing: $0)
}
PS。感谢大家的投入!赏金是自动授予的。