假设我们正在谈论Int类型的元素(但问题仍适用于任何类型)
我有一些功能需要遍历一系列Ints。但我不在乎,如果在幕后将这个序列实现为数组,或者Set或任何其他异乎寻常的结构,唯一的要求就是我们可以遍历它们。
Swift标准库将协议SequenceType定义为"可以使用for ... in循环"迭代的类型。所以我的直觉是定义这样的协议:
protocol HasSequenceOfInts {
var seq : SequenceType<Int> { get }
}
但这不起作用。 SequenceType不是可以专用的泛型类型,它是一种协议。任何特定的SequenceType 都具有特定类型的元素,但它仅作为关联类型提供:SequenceType.Generator.Element
所以问题是:
我们如何定义需要特定类型序列的协议?
以下是我尝试的其他一些事项以及为什么他们不对:
失败1
protocol HasSequenceOfInts {
var seq : SequenceType { get }
}
协议&#39; SequenceType&#39;只能用作通用约束 因为它有自我或相关的类型要求
失败2
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
class ArrayOfInts : HasSequenceOfInts {
var seq : [Int] = [0,1,2]
}
我认为这个可行,但是当我尝试使用数组的具体实现时,我们得到了
输入&#39; ArrayOfInts&#39;不符合协议&#39; HasSequenceOfInts&#39;
这是因为Array不是AnySequence(令我惊讶的是......我的期望是AnySequence会匹配任何Ints序列)
失败3
protocol HasSequenceOfInts {
typealias S : SequenceType
var seq : S { get }
}
编译,但序列seq的元素没有义务Int
失败4
protocol HasSequenceOfInts {
var seq : SequenceType where 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)
}
}
}
答案 0 :(得分:8)
这个问题有两个方面:
接受任意顺序的整数
返回或存储任意序列的整数
在第一种情况下,答案是使用泛型。例如:
func iterateOverInts<SeqInt: SequenceType where SeqInt.Generator.Element == Int>(xs: SeqInt) {
for x in xs {
print(x)
}
}
在第二种情况下,您需要一个类型橡皮擦。类型擦除器是一个包装器,它隐藏了某些底层实现的实际类型,并且只显示了接口。 Swift在stdlib中有几个,大多以Any
为前缀。在这种情况下,您需要AnySequence
。
func doubles(xs: [Int]) -> AnySequence<Int> {
return AnySequence( xs.lazy.map { $0 * 2 } )
}
有关AnySequence
和类型 - 橡皮擦的更多信息,请参阅A Little Respect for AnySequence。
如果你需要协议形式(通常你不需要;你只需要使用iterateOverInts
中的泛型),橡皮擦类型也是那里的工具:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
但seq
必须返回AnySequence<Int>
。它无法返回[Int]
。
你可以采用更深层次的一层,但有时它会产生比它解决的更多麻烦。你可以定义:
protocol HasSequenceOfInts {
typealias SeqInt : IntegerType
var seq: SeqInt { get }
}
但现在HasSequenceOfInts
有一个typealias
,其中包含所有限制。 SeqInt
可以是任何类型的IntegerType
(不只是Int
),因此看起来就像受约束的SequenceType
,并且通常需要自己的类型橡皮擦。所以偶尔这种技术很有用,但在你的具体情况下它只能让你基本上回到你开始的地方。您无法在此处SeqInt
约束Int
。它必须是一个协议(当然你可以发明一个协议并使Int
成为唯一符合的类型,但这并没有太大变化。)
Box
类来保存具有通用关联值的枚举。现在用indirect
半自动完成。我们可以想象添加一个类似的机制,以便在编译器需要时自动创建AnySequence
。所以我不认为这是一个很深的“Swift的设计不允许它。”我认为这只是“Swift编译器尚未处理它。”
答案 1 :(得分:2)
(经过测试并在Swift 4 which introduces the associatedtype
constraints needed for this中工作)
声明您的原始协议符合以下规定:
protocol HasSequenceOfInts {
associatedType IntSequence : Sequence where IntSequence.Element == Int
var seq : IntSequence { get }
}
现在,您可以编写
class ArrayOfInts : HasSequenceOfInts {
var seq : [Int] = [0,1,2]
}
像您一直想要的。
但是,如果尝试创建类型为[HasSequenceOfInts]
的数组,或将其分配给变量(或基本上对其执行任何操作),则会收到一条错误消息,提示
协议“ HasSequenceOfInts”仅具有通用或相关类型要求,因此只能用作通用约束
现在是有趣的部分了。
我们将创建另一个协议HasSequenceOfInts_
(随意选择一个更具描述性的名称),该协议将不具有关联的类型要求,和将自动被{{ 1}} :
HasSequenceOfInts
请注意,您不需要显式符合protocol HasSequenceOfInts: HasSequenceOfInts_ {
associatedType IntSequence : Sequence where IntSequence.Element == Int
var seq : IntSequence { get }
}
protocol HasSequenceOfInts_ {
var seq : AnySequence<Int> { get }
}
extension HasSequenceOfInts_ where Self : HasSequenceOfInts {
var seq_ : AnySequence<Int> {
return AnySequence(seq)
}
}
,因为HasSequenceOfInts_
已经符合它,并且您可以从扩展中免费获得完整的实现。
现在,如果您需要创建数组或将符合该协议的内容的实例分配给变量,请使用HasSequenceOfInts
作为类型而不是HasSequenceOfInts_
,然后访问{{1} }属性(注意:,因为允许函数重载,所以如果您使用函数HasSequenceOfInts
而不是实例变量,则可以使用相同的名称,并且可以使用):
seq_
与接受的答案相比,这需要更多的设置,但是这意味着您不必记住将返回值包装在实现协议的每种类型的seq()
中。
答案 2 :(得分:1)
我相信你需要将它的要求降低到只有Int并使用泛型来解决它:
protocol HasSequence {
typealias S : SequenceType
var seq : S { get }
}
struct A : HasSequence {
var seq = [1, 2, 3]
}
struct B : HasSequence {
var seq : Set<String> = ["a", "b", "c"]
}
func printSum<T : HasSequence where T.S.Generator.Element == Int>(t : T) {
print(t.seq.reduce(0, combine: +))
}
printSum(A())
printSum(B()) // Error: B.S.Generator.Element != Int
在斯威夫特目前的状态下,你可能无法做到你想要的,也许在未来。
答案 3 :(得分:0)
这是Daniel Howard要求的非常具体的例子
1)符合SequenceType协议的类型几乎可以是任何序列,即使Array或Set都符合SequenceType协议,它们的大多数功能都来自CollectionType上的继承(符合SequenceType)
丹尼尔,在你的游乐场尝试这个简单的例子
import Foundation
public struct RandomIntGenerator: GeneratorType, SequenceType {
public func next() -> Int? {
return random()
}
public func nextValue() -> Int {
return next()!
}
public func generate() -> RandomIntGenerator {
return self
}
}
let rs = RandomIntGenerator()
for r in rs {
print(r)
}
如您所见,它符合SequenceType协议并产生无限的Int数字流。在你尝试实施某些事情之前,你必须回答几个问题
protocol P {
typealias Type: SequenceType
var value: Type { get set }
}
extension P {
func foo() {
for v in value {
dump(v)
}
}
}
struct S<T: CollectionType>: P {
typealias Type = T
var value: Type
}
var s = S(value: [Int]())
s.value.append(1)
s.value.append(2)
s.foo()
/*
- 1
- 2
*/
let set: Set<String> = ["alfa", "beta", "gama"]
let s2 = S(value: set)
s2.foo()
/*
- beta
- alfa
- gama
*/
// !!!! WARNING !!!
// this is NOT possible
s = s2
// error: cannot assign value of type 'S<Set<String>>' to type 'S<[Int]>' (aka 'S<Array<Int>>')