问题描述
假设我们正在讨论Int类型的元素(但问题仍然适用于任何类型)。我有一些功能需要循环遍历Ints的序列。但我并不在乎在幕后这个序列是以数组,或者集合还是任何其他奇特类型的结构实现的,唯一的要求就是我们可以在它们上面循环。
Swift标准库将协议SequenceType定义为可以用for ... in循环迭代的类型。所以我的直觉是定义一个像这样的协议:
protocol HasSequenceOfInts {
var seq:SequenceType< Int> {get}
}
但这不起作用。 SequenceType不是可以专用的通用类型,而是一种协议。任何特定的SequenceType does 都有一个特定类型的元素,但它只能作为关联的类型使用:SequenceType.Generator.Element
是:
我们如何定义需要特定类型序列的协议?
以下是我尝试过的其他一些事情,以及他们为什么不对:
失败1
protocol HasSequenceOfInts {
var seq:SequenceType {get}
}
$ p'p>
不合格2
protocol HasSequenceOfInts {
var seq:AnySequence< Int> {get}
}
class ArrayOfInts:HasSequenceOfInts {
var seq:[Int] = [0,1,2]
}
我认为这个可以工作,但是当我尝试使用Array的具体实现时,我们得到了
这是因为Array是不是AnySequence(令我惊讶......我的期望是AnySequence会匹配任何Ints序列)
$ b失败3
协议HasSequenceOfInts {
typealias S:SequenceType
var seq:S {get}
}
编译,但没有义务序列seq的元素具有类型Int
失败4
协议HasSequenceOfInts {
var seq:SequenceType其中S.Generator.Element == Int
}
不能在那里使用where子句
所以现在我完全没有想法。我可以很容易地让我的协议需要一个Int数组,但是我没有任何理由限制实现,而且这种感觉非常迅速。
更新成功
请参阅@ 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)
}
}
}
解决方案这个问题有两个方面:
-
接受任意的ints序列
-
返回或存储任意的ints序列
/ ul>
在第一种情况下,答案是使用泛型。例如:
func iterateOverInts< SeqInt:SequenceType其中SeqInt.Generator.Element == Int>(xs:SeqInt){
for x in xs {
print(x)
}
}
在第二种情况下,您需要使用类型擦除器。 type-eraser是一个隐藏某些底层实现的实际类型并仅显示接口的包装器。 Swift在stdlib中有几个,大部分都以 Any 为前缀。在这种情况下,您需要 AnySequence 。
func doubles(xs:[ Int]) - > AnySequence<诠释> {
return AnySequence(xs.lazy.map {$ 0 * 2})
}
有关 AnySequence 和类型 - 橡皮擦的更多信息,请参阅对AnySequence的一点尊重 iterateOverInts
协议HasSequenceOfInts {
var seq / code>),类型橡皮擦也是那里的工具: :AnySequence< Int> {get}
}
但 seq 必须返回 AnySequence< Int> 。它不能返回 [Int] 。
还有一层可以做得更深,但有时它会造成比解决问题更多的麻烦。您可以定义:
protocol HasSequenceOfInts {
typealias SeqInt:IntegerType
var seq:SeqInt {
}
但现在 HasSequenceOfInts 有一个 typealias 具有所有的隐含限制。 SeqInt 可以是任何一种 IntegerType (不只是 Int ),所以看起来就像一个约束 SequenceType ,并且通常需要它自己的类型橡皮擦。所以偶尔这个技巧是有用的,但在你的具体情况下,它只是让你基本回到你开始的地方。您无法在此限制 SeqInt 至 Int 。它必须是一个协议(当然你可以创建一个协议并使 Int 唯一符合的类型,但是这并没有太大的改变)。
顺便说一句,关于类型橡皮擦,你可能会看到它们非常机械。他们只是一个转发给别的东西的盒子。这表明将来编译器将能够为我们自动生成这些类型的橡皮擦。随着时间的推移,编译器已经修复了其他的装箱问题。例如,你以前必须创建一个 Box 类来存放具有通用关联值的枚举。现在已经半自动完成了 indirect 。我们可以想象一个类似的机制被添加来自动创建 AnySequence ,当它被编译器需要时。所以我不认为这是一个很深的Swift的设计不允许它。我认为这只是Swift编译器尚未处理它。
Suppose for example we're talking about elements of type Int (but the question still applies to any type)
I have some functionality which needs to loop over a sequence of Ints. But I don't care if behind the scenes this sequence is implemented as an Array, or a Set or any other exotic kind of structure, the only requirement is that we can loop over them.
Swift standard library defines the protocol SequenceType as "A type that can be iterated with a for...in loop". So my instinct is to define a protocol like this:
protocol HasSequenceOfInts { var seq : SequenceType<Int> { get } }
But this doesn't work. SequenceType is not a generic type which can be specialized, it's a protocol. Any particular SequenceType does have a specific type of element, but it's only available as an associated type: SequenceType.Generator.Element
So the question is:
How can we define a protocol which requires a specific type of sequence?
Here's some other things I've tried and why they aren't right:
Fail 1
protocol HasSequenceOfInts { var seq : SequenceType { get } }
Fail 2
protocol HasSequenceOfInts { var seq : AnySequence<Int> { get } } class ArrayOfInts : HasSequenceOfInts { var seq : [Int] = [0,1,2] }
I thought this one would work, but when I tried a concrete implementation using an Array we get
This is because Array is not AnySequence (to my surprise... my expectation was that AnySequence would match any sequence of Ints)
Fail 3
protocol HasSequenceOfInts { typealias S : SequenceType var seq : S { get } }
Compiles, but there's no obligation that the elements of the sequence seq have type Int
Fail 4
protocol HasSequenceOfInts { var seq : SequenceType where S.Generator.Element == Int }
Can't use a where clause there
So now I'm totally out of ideas. I can easily just make my protocol require an Array of Int, but then I'm restricting the implementation for no good reason, and that feels very un-swift.
Update Success
See answer from @rob-napier which explains things very well. My Fail 2 was pretty close. Using AnySequence can work, but in your conforming class you need to make sure you convert from whatever kind of sequence you're using to AnySequence. For example:
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) } } }
There are two sides to this problem:
Accepting an arbitrary sequence of ints
Returning or storing an arbitrary sequence of ints
In the first case, the answer is to use generics. For example:
func iterateOverInts<SeqInt: SequenceType where SeqInt.Generator.Element == Int>(xs: SeqInt) { for x in xs { print(x) } }
In the second case, you need a type-eraser. A type-eraser is a wrapper that hides the actual type of some underlying implementation and presents only the interface. Swift has several of them in stdlib, mostly prefixed with the word Any. In this case you want AnySequence.
func doubles(xs: [Int]) -> AnySequence<Int> { return AnySequence( xs.lazy.map { $0 * 2 } ) }
For more on AnySequence and type-erasers in general, see A Little Respect for AnySequence.
If you need it in protocol form (usually you don't; you just need to use a generic as in iterateOverInts), the type eraser is also the tool there:
protocol HasSequenceOfInts { var seq : AnySequence<Int> { get } }
But seq must return AnySequence<Int>. It can't return [Int].
There is one more layer deeper you can take this, but sometimes it creates more trouble than it solves. You could define:
protocol HasSequenceOfInts { typealias SeqInt : IntegerType var seq: SeqInt { get } }
But now HasSequenceOfInts has a typealias with all the limitations that implies. SeqInt could be any kind of IntegerType (not just Int), so looks just like a constrained SequenceType, and will generally need its own type eraser. So occasionally this technique is useful, but in your specific case it just gets you basically back where you started. You can't constrain SeqInt to Int here. It has to be to a protocol (of course you could invent a protocol and make Int the only conforming type, but that doesn't change much).
BTW, regarding type-erasers, as you can probably see they're very mechanical. They're just a box that forwards to something else. That suggests that in the future the compiler will be able to auto-generate these type-erasers for us. The compiler has fixed other boxing problems for us over time. For instance, you used to have to create a Box class to hold enums that had generic associated values. Now that's done semi-automatically with indirect. We could imagine a similar mechanism being added to automatically create AnySequence when it's required by the compiler. So I don't think this is a deep "Swift's design doesn't allow it." I think it's just "the Swift compiler doesn't handle it yet."
这篇关于定义一个需要特定类型序列的Swift协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!