Dan*_*ard 12 generics protocols swift
假设我们正在谈论Int类型的元素(但问题仍然适用于任何类型)
我有一些功能需要循环一系列Ints.但我不在乎幕后这个序列是作为一个数组,或一个Set或任何其他奇特的结构实现,唯一的要求是我们可以循环它们.
Swift标准库将协议SequenceType定义为"可以使用for ... in循环迭代的类型".所以我的直觉是定义这样的协议:
protocol HasSequenceOfInts {
var seq : SequenceType<Int> { get }
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用.SequenceType不是可以专用的泛型类型,它是一种协议.任何特定的SequenceType 都具有特定类型的元素,但它仅作为关联类型提供:SequenceType.Generator.Element
所以问题是:
我们如何定义需要特定类型序列的协议?
这是我尝试过的其他一些事情,以及为什么他们不对:
失败1
protocol HasSequenceOfInts {
var seq : SequenceType { get }
}
Run Code Online (Sandbox Code Playgroud)
协议'SequenceType'只能用作通用约束,因为它具有Self或相关类型要求
失败2
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
class ArrayOfInts : HasSequenceOfInts {
var seq : [Int] = [0,1,2]
}
Run Code Online (Sandbox Code Playgroud)
我认为这个可行,但是当我尝试使用数组的具体实现时,我们得到了
类型'ArrayOfInts'不符合协议'HasSequenceOfInts'
这是因为Array不是AnySequence(令我惊讶的是......我的期望是AnySequence会匹配任何Ints序列)
失败3
protocol HasSequenceOfInts {
typealias S : SequenceType
var seq : S { get }
}
Run Code Online (Sandbox Code Playgroud)
编译,但序列seq的元素没有义务类型为Int
失败4
protocol HasSequenceOfInts {
var seq : SequenceType where S.Generator.Element == Int
}
Run Code Online (Sandbox Code Playgroud)
不能在那里使用where子句
所以现在我完全没有想法.我可以很容易地让我的协议需要一个Int of Array,但是后来我没有充分的理由限制实现,这感觉非常迅速.
更新成功
请参阅@ rob-napier的回答,它解释得非常好.我的失败2非常接近.使用AnySequence可以工作,但是在你的符合要求的类中,你需要确保从你正在使用的任何序列转换为AnySequence.例如:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
class ArrayOfInts : HasSequenceOfInts {
var _seq : [Int] = [0,1,2]
var seq : AnySequence<Int> {
get {
return AnySequence(self._seq)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这个问题有两个方面:
接受任意顺序的整数
返回或存储任意序列的整数
在第一种情况下,答案是使用泛型.例如:
func iterateOverInts<SeqInt: SequenceType where SeqInt.Generator.Element == Int>(xs: SeqInt) {
for x in xs {
print(x)
}
}
Run Code Online (Sandbox Code Playgroud)
在第二种情况下,您需要一个类型橡皮擦.类型擦除器是一个包装器,它隐藏了某些底层实现的实际类型,并仅显示接口.Swift在stdlib中有几个,大部分都以这个词为前缀Any.在这种情况下你想要AnySequence.
func doubles(xs: [Int]) -> AnySequence<Int> {
return AnySequence( xs.lazy.map { $0 * 2 } )
}
Run Code Online (Sandbox Code Playgroud)
有关更多on AnySequence和类型擦除器的信息,请参阅对AnySequence的一点尊重.
如果你需要协议形式(通常你不需要;你只需要使用泛型iterateOverInts),类型橡皮擦也是那里的工具:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
Run Code Online (Sandbox Code Playgroud)
但seq必须回归AnySequence<Int>.它无法返回[Int].
你可以采用更深层次的一层,但有时它会产生比它解决的更多麻烦.你可以定义:
protocol HasSequenceOfInts {
typealias SeqInt : IntegerType
var seq: SeqInt { get }
}
Run Code Online (Sandbox Code Playgroud)
但现在HasSequenceOfInts有一个typealias具有所有限制意味着.SeqInt可以是任何一种IntegerType(不仅仅是Int),所以看起来就像一个约束SequenceType,并且通常需要它自己的类型橡皮擦.所以偶尔这种技术很有用,但在你的具体情况下它只能让你基本上回到你开始的地方.你不能限制SeqInt在Int这里.它必须是一个协议(当然你可以发明一个协议,并使其Int成为唯一符合的类型,但这并没有太大变化).
顺便说一下,关于型号橡皮擦,你可能会发现它们非常机械.他们只是一个转发其他东西的盒子.这表明将来编译器将能够为我们自动生成这些类型擦除器.编译器已经为我们修复了其他拳击问题.例如,您以前必须创建一个Box类来保存具有通用关联值的枚举.现在,这是半自动完成的indirect.我们可以想象添加一个类似的机制,以便AnySequence在编译器需要时自动创建.所以我不认为这是一个很深的"Swift的设计不允许它." 我认为这只是"Swift编译器尚未处理它".
| 归档时间: |
|
| 查看次数: |
2904 次 |
| 最近记录: |