练习是编写我自己的map()函数Collection(不使用任何功能原语,如reduce()).它应该处理这样的情况:
func square(_ input: Int) -> Int {
return input * input
}
let result = input.accumulate(square) // [1,2,3] => [1,4,9]
Run Code Online (Sandbox Code Playgroud)
我的第一次尝试是:
extension Collection {
func accumulate(_ transform: (Element) -> Element) -> [Element] {
var array: [Element] = []
for element in self {
array.append(transform(element))
}
return array
}
}
Run Code Online (Sandbox Code Playgroud)
这在游乐场中运行良好,但无法针对测试构建,给出错误:
Value of type '[Int]' has no member 'accumulate'
Run Code Online (Sandbox Code Playgroud)
解决方案是将accumulate方法泛化:
extension Collection {
func accumulate<T>(_ transform: (Element) -> T) -> [T] {
var array: [T] = []
for element in self {
array.append(transform(element))
}
return array
}
}
Run Code Online (Sandbox Code Playgroud)
我认识到通用版本限制较少(不要求转换返回相同的类型),但鉴于测试不需要这种通用性,为什么编译器?
出于好奇我试过:
extension Collection {
func accumulate<Element>(_ transform: (Element) -> Element) -> [Element] {
var array: [Element] = []
for element in self {
array.append(transform(element))
}
return array
}
}
Run Code Online (Sandbox Code Playgroud)
这引发了令人着迷的构建错误:'(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]
Run Code Online (Sandbox Code Playgroud)
它会看到两种类型(Self.Element和Element)或认识到它们是相同的.
所以我做了这个测试:
let arr = [1,2,3]
arr.accumulate {
return String(describing: $0)
}
Run Code Online (Sandbox Code Playgroud)
果然,得到了预期的错误: error: cannot convert value of type 'String' to closure result type 'Int'
所以正确的答案是:只要不存在重载名称的泛型类型,编译器就会将对Element的引用视为相同.
奇怪的是,这成功了:
[1,2,3].accumulate {
return String(describing: $0)
}
Run Code Online (Sandbox Code Playgroud)
PS.感谢大家的投入!赏金是自动授予的.
| 归档时间: |
|
| 查看次数: |
405 次 |
| 最近记录: |