尝试跨越String.CharacterView.Index
指数时,例如大步2
extension String.CharacterView.Index : Strideable { }
let str = "01234"
for _ in str.startIndex.stride(to: str.endIndex, by: 2) { } // fatal error
我得到以下运行时异常
致命错误:无法递增endIndex
仅创建上面的StrideTo<String.CharacterView.Index>
,(let foo = str.startIndex.stride(to: str.endIndex, by: 2)
)不会产生错误,只有在尝试跨步/迭代或对其进行操作时才会产生错误(.next()
?)。< / p>
Stridable
)? 我使用 Swift 2.2 和 Xcode 7.3 。详情如下。
编辑添加:错误来源
在仔细阅读我的问题后,似乎错误确实发生在next()
的{{1}}方法中(参见本文底部),特别是在以下标记的行
StrideToGenerator
即使永远不会返回let ret = current
current += stride // <-- here
return ret
的最后一次更新(在下一次调用current
中),next()
索引的最终前进值也会大于或等于current
。 {1}}产生上面的特定运行时错误(对于_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
而导致的错误?< / LI>
以下Q&amp; A与在+1
以外的步骤中迭代字符的主题相关,并且即使这两个问题不同,也值得包含在此问题中。
特别注意上面帖子中的@Sulthan:s neat solution。
(对我自己的详细信息/调查表示道歉,如果您可以在没有详细说明的情况下回答我的问题,请跳过这些部分)
String.CharacterView.Index
type描述了一个角色位置,并且:
Comparable
(以及Equatable
),advancedBy(_:)
和distanceTo(_:)
的实现。因此,可以直接使其符合protocol Strideable
,使用Stridable
:方法stride(through:by:)
和stride(to:by:)
的默认实现。下面的例子将集中讨论后者(与前者类似的问题):
...
func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
返回值的值序列(self,self + stride,self + stride + stride,... last)其中last是进展中的最后一个值 小于结束。
遵守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
,字符视图索引的步幅会产生运行时错误
// 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 */
我对此的调查让我怀疑错误来自StrideToGenerator
structure的next()
方法,可能是此方法在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)。鉴于以下Q&amp; A:s
我们可能会怀疑我们可能需要使用上面的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
,{1}}会产生上面的特定运行时错误。
(0..<4).startIndex.advancedBy(4) // OK, -> 4
"foo".startIndex.advancedBy(4) // fatal error: cannot increment endIndex
这是否被视为错误,或String.CharacterView.Index
根本不打算(直接)符合Stridable
?
答案 0 :(得分:4)
简单地声明协议一致性
extension String.CharacterView.Index : Strideable { }
编译,因为String.CharacterView.Index
符合
BidirectionalIndexType
和ForwardIndexType/BidirectionalIndexType
具有advancedBy()
和distanceTo()
的默认方法实施
根据{{1}}的要求。
Strideable
具有默认的协议方法实现
Strideable
:
stride()
所以“直接”实现的唯一方法
extension Strideable {
// ...
public func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
}
- 就我所知 - 来自String.CharacterView.Index
的{{1}}和successor()
方法。
正如您已经想到的那样,默认方法实现了
predecessor()
与BidirectionalIndexType
无效。
但总是可以为具体类型定义专用方法。对于使stride()
符合String.CharacterView.Index
的问题,请参阅
下面的Vatsal Manot's answer以及评论中的讨论 - 我花了一段时间才得到他的意思:)
以下是String.CharacterView.Index
的{{1}}方法的可能实现:
Strideable
这似乎按预期工作:
stride(to:by:)
输出
String.CharacterView.Index
答案 1 :(得分:2)
简单回答你的结束问题:这不是一个错误。这是正常行为。
String.CharacterView.Index
永远不会超过父构造的endIndex
(即字符视图),因此在被强制时会触发运行时错误(正如在答案的后半部分中正确指出的那样)。这是设计的。
唯一的解决方案是编写自己的stride(to:by:)
替代方案,避免以任何方式等同或超过endIndex
。
如您所知,您可以在技术上实施Strideable
,但无法阻止该错误。由于stride(to:by:)
不是在协议本身内进行蓝图,而是在扩展中引入,因此您无法使用&#34;自定义&#34;通用范围内的stride(to:by:)
(即<T: Strideable>
等)。这意味着您可能不应该尝试实现它,除非您完全确定不会发生错误;似乎不可能的事情。
解决方案:目前还没有。但是,如果您认为这是一个问题,我建议您在swift-evolution mailing list中启动一个主题,最好能够收到此主题。
答案 2 :(得分:0)
这不是一个真正的答案;只是你的问题让我玩弄了。让我们忽略Stridable并尝试跨越角色视图:
let str = "01234"
var i = str.startIndex
// i = i.advancedBy(1)
let inc = 2
while true {
print(str.characters[i])
if i.distanceTo(str.endIndex) > inc {
i = i.advancedBy(inc)
} else {
break
}
}
正如您所看到的,在之前调用distanceTo
时,使用advancedBy
进行测试至关重要。否则,我们冒险尝试通过最终索引前进,我们将得到“致命错误:无法增加endIndex”炸弹。
所以我的想法是,为了使角色视图的索引具有可争议性,必须有这样的东西。