问题:
当尝试通过例如跨越 String.CharacterView.Index
索引时2
的一大步
extension String.CharacterView.Index : Strideable { }
let str = "01234"
for _ in str.startIndex.stride(to: str.endIndex, by: 2) { } // fatal error
我收到以下运行时异常
fatal error: cannot increment endIndex
但是,仅在创建上面的
StrideTo<String.CharacterView.Index>
时,( let foo = str.startIndex.stride(to: str.endIndex, by: 2)
)不会产生错误,仅在尝试跨步/迭代或对其进行操作时( .next()
?)。Stridable
)? 我正在使用 Swift 2.2 和 Xcode 7.3 。详情如下。
编辑补充:错误源位于
仔细阅读我的问题后,似乎错误确实发生在
next()
的 StrideToGenerator
方法中(参见本文底部),特别是在以下标记行let ret = current
current += stride // <-- here
return ret
即使永远不会返回
current
的最后一次更新(在下一次调用 next()
中),current
索引的最终推进值大于或等于 _end
的值会产生上述特定的运行时错误(对于 Index
类型 String.CharacterView.Index
)。(0..<4).startIndex.advancedBy(4) // OK, -> 4
"foo".startIndex.advancedBy(4) // fatal error: cannot increment endIndex
但是,仍然存在一个问题:
next()
的 StrideToGenerator
方法中的错误,还是只是由于误用 String.CharacterView.Index
与 Stridable
的一致性而弹出的错误? 有关的
以下问答与在
+1
以外的步骤中迭代字符的主题有关,即使两个问题不同,也值得包含在此问题中。特别注意上面线程中的 @Sulthan:s neat solution 。
细节
(对于我自己的大量细节/调查表示歉意,如果您可以在没有此处详细信息的情况下回答我的问题,请跳过这些部分)
String.CharacterView.Index
type 描述了一个字符位置,并且:Comparable
(因此, Equatable
), advancedBy(_:)
和 distanceTo(_:)
的实现。 因此,它可以直接符合 protocol
Strideable
,利用 Stridable
:s 方法 stride(through:by:)
和 stride(to:by:)
的默认实现。下面的例子将侧重于后者(与前者类似的问题):...
func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
Returns the sequence of values (self, self + stride, self + stride + stride, ... last) where last is the last value in the progression that is less than end.
符合
Stridable
并通过 1
大步前进:一切都很好 将
String.CharacterView.Index
扩展到 Stridable
并通过 1
跨步工作正常:extension String.CharacterView.Index : Strideable { }
var str = "0123"
// stride by 1: all good
str.startIndex.stride(to: str.endIndex, by: 1).forEach {
print($0,str.characters[$0])
} /* 0 0
1 1
2 2
3 3 */
对于上面
str
中的偶数个索引(索引 0..<4
),这也适用于 2
的步幅:// stride by 2: OK for even number of characters in str.
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
} /* 0 0
2 2 */
但是,对于某些通过
>1
跨步的情况:运行时异常 但是,对于奇数个索引和
2
的步幅,字符 View 索引上的步幅会产生运行时错误// stride by 2: fatal error for odd number of characters in str.
str = "01234"
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
} /* 0 0
2 2
fatal error: cannot increment endIndex */
我自己的调查
我自己对此的调查使我怀疑错误来自
next()
structure 的 StrideToGenerator
方法,可能是当此方法在 stridable 元素上调用 +=
时public func += <T : Strideable>(inout lhs: T, rhs: T.Stride) {
lhs = lhs.advancedBy(rhs)
}
(来自
swift/stdlib/public/core/Stride.swift
的 Swift 源代码版本,它在某种程度上与 Swift 2.2 相对应)。鉴于以下问答:我们可以怀疑我们可能需要使用
String.CharacterView.Index.advancedBy(_:limit:)
而不是上面的 ...advancedBy(_:)
。然而,据我所知,next()
中的 StrideToGenerator
方法防止索引超过限制。编辑补充: 错误的来源似乎确实位于
next()
中的 StrideToGenerator
方法中:// ... in StrideToGenerator
public mutating func next() -> Element? {
if stride > 0 ? current >= end : current <= end {
return nil
}
let ret = current
current += stride /* <-- will increase current to larger or equal to end
if stride is large enough (even if this last current
will never be returned in next call to next()) */
return ret
}
即使
current
的最后一次更新永远不会返回(在下一次调用 next()
时),current
索引的最终推进值大于或等于 end
的值会产生上面的特定运行时错误,对于 Index
类型 String.CharacterView.Index
。(0..<4).startIndex.advancedBy(4) // OK, -> 4
"foo".startIndex.advancedBy(4) // fatal error: cannot increment endIndex
这是否被视为错误,还是
String.CharacterView.Index
根本不打算(直接)符合 Stridable
?
最佳答案
简单地声明协议(protocol)一致性
extension String.CharacterView.Index : Strideable { }
编译是因为
String.CharacterView.Index
符合BidirectionalIndexType
和 ForwardIndexType/BidirectionalIndexType
有 advancedBy()
和 distanceTo()
的默认方法实现根据
Strideable
的要求。Strideable
有默认的协议(protocol)方法实现对于
stride()
:extension Strideable {
// ...
public func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
}
所以唯一“直接”实现的方法
String.CharacterView.Index
是——据我所知——来自 successor()
的 predecessor()
和 BidirectionalIndexType
方法。正如您已经发现的,默认方法实现
stride()
不适用于 String.CharacterView.Index
。但是总是可以为具体类型定义专用方法。对于使
String.CharacterView.Index
符合Strideable
的问题参见下面的 Vatsal Manot's answer 和评论中的讨论——我花了一段时间才明白他的意思:)
这是
stride(to:by:)
的 String.CharacterView.Index
方法的可能实现:extension String.CharacterView.Index {
typealias Index = String.CharacterView.Index
func stride(to end: Index, by stride: Int) -> AnySequence<Index> {
precondition(stride != 0, "stride size must not be zero")
return AnySequence { () -> AnyGenerator<Index> in
var current = self
return AnyGenerator {
if stride > 0 ? current >= end : current <= end {
return nil
}
defer {
current = current.advancedBy(stride, limit: end)
}
return current
}
}
}
}
这似乎按预期工作:
let str = "01234"
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
}
输出
0 0
2 2
4 4
关于swift - 使 String.CharacterView.Index 符合 Strideable : fatal error when using stride(to:by:): "cannot increment endIndex ",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36205309/