在 swift 中添加对范围运算符的支持? (例如,对于红色...紫色的 x)
Adding support for the range operator in swift? (e.g. for x in red...violet)
在Swift中,我们可以使用range operators(...
和..<
)循环范围:
for i in 0...4 { ... }
假设我有很多颜色:
let red: MyColor
let orange: MyColor
let yellow: MyColor
let green: MyColor
let blue: MyColor
let indigo: MyColor
let violet: MyColor
let black: MyColor
let brown: MyColor
如何支持范围运算符以便我可以按彩虹顺序遍历颜色?
for rainbowColor in red...violet { ... }
所以...您可以对任何类型执行 for
-in
循环。您所要做的就是实现符合 SequenceType
的东西。然后,一旦您实现了 class 或结构,它就像创建一个 ...
运算符一样简单 return 是 SequenceType
.
假设如下:
struct RainbowColor {
let name: String
}
let red: RainbowColor = RainbowColor(name: "red")
let orange: RainbowColor = RainbowColor(name: "orange")
let yellow: RainbowColor = RainbowColor(name: "yellow")
let green: RainbowColor = RainbowColor(name: "green")
let blue: RainbowColor = RainbowColor(name: "blue")
let indigo: RainbowColor = RainbowColor(name: "indigo")
let violet: RainbowColor = RainbowColor(name: "violet")
我们可以这样创建一个RainbowColorSequence
:
struct RainbowColorSequence: SequenceType {
let startColor: RainbowColor
let endColor: RainbowColor
init(start: RainbowColor, end: RainbowColor) {
startColor = start
endColor = end
}
func generate() -> AnyGenerator<RainbowColor> {
return AnyGenerator<RainbowColor> {
// TODO: Implement your generator logic.
return nil
}
}
}
(请注意,此处省略了 return 中颜色的顺序以及序列结束时间的实际逻辑......因为它会很混乱。阅读底部以获取示例然而,这个实现可能是什么样子。)
一旦我们有了这个 RainbowColorSequence
,...
函数就非常简单了:
func ...(lhs: RainbowColor, rhs: RainbowColor) -> RainbowColorSequence {
return RainbowColorSequence(start: lhs, end: rhs)
}
现在,您可以编写以下代码:
for color in red...violet {
print(color.name)
}
有关生成器逻辑的示例,请查看 this question。我可以很容易地在 link 后面的代码中添加一个 ...
运算符,因为所有其他逻辑都已经到位,并且 ...
实际上只是围绕SequenceType
.
重要的是,您必须自己编写序列的逻辑。鉴于您的条件,编译器没有简单的方法来推断序列中的内容或序列的顺序。并且该逻辑可能会变得非常混乱,具体取决于您想要迭代的类型。
(注意这里是Swift3,如果要Swift2.2,看Senseful的回答,和老协议基本一样。)
您希望 Color
成为 Strideable
,Stride
为 Int
。然后你就可以免费获得 ...
和 ..<
。让我们这样做吧。
首先,我假设您的类型是这样的。这只是一个例子。你可以做所有 种 的事情,只要你能实现 Strideable
。你似乎想要结构而不是枚举(这很好),所以让我们做结构吧。这只是意味着任何人都可以添加其他颜色,您必须在设计中考虑到这一点。颜色需要一些东西来区分它们,所以我们会给它们一个名字。如果他们是类,那也可以区分他们。
struct Color {
let name: String
static let red = Color(name: "red")
static let orange = Color(name: "orange")
static let yellow = Color(name: "yellow")
static let green = Color(name: "green")
static let blue = Color(name: "blue")
static let indigo = Color(name: "indigo")
static let violet = Color(name: "violet")
}
Strideable 的第一条规则是 Equatable。
extension Color: Equatable {
static func == (lhs: Color, rhs: Color) -> Bool {
return lhs.name == rhs.name
}
}
现在可以等同了,我们应该想出我们所说的 "order." 的意思 这是一种方法。由于结构使意想不到的值成为可能,我已经说过,在我的任何彩虹色之前,它们都是相等的和有序的。如果这是一个枚举,我们就不必担心这种情况。
extension Color {
private static let _order: [Color] = [red, orange, yellow, green, blue, indigo, violet]
private var _indexValue: Int {
return Color._order.index(of: self) ?? Int.min
}
}
因此,我们可以回答问题 #2:可比较:
extension Color: Comparable {
static func < (lhs: Color, rhs: Color) -> Bool {
return lhs._indexValue < rhs._indexValue
}
}
迈向 Strideable 仅需一小步:
extension Color: Strideable {
func advanced(by n: Int) -> Color {
return Color._order[self._indexValue + n]
}
func distance(to other: Color) -> Int {
return other._indexValue - _indexValue
}
}
还有你的 ...
:
for rainbowColor: Color in .red ... .violet { print(rainbowColor) }
现在我碰巧很喜欢枚举,如果你试图用枚举来实现它,它们恰好有一点技巧,所以值得注意。例如,假设您有:
enum Color {
case red
case orange
case yellow
case green
case blue
case indigo
case violet
}
原来你不能用index(of:)
来计算_indexValue
。您会陷入无限循环,因为它在默认 ==
实现中调用 distance(to:)
(我实际上不确定为什么会发生这种情况)。所以你必须这样实现_indexValue
:
private var _indexValue: Int {
switch self {
case .red: return 0
case .orange: return 1
case .yellow: return 2
case .green: return 3
case .blue: return 4
case .indigo: return 5
case .violet: return 6
}
}
嗯;这看起来非常像原始值。是的。整个方法基本上是从侧面附加原始值,而无需修改类型本身。所以这就是为什么它看起来真的很相似。其余部分与结构相同,并且该方法可以应用于您可以弄清楚如何跨步的任何其他内容。
(注意:这是针对 Swift 2.2 的。如果您想要 Swift 3 版本,请参阅 。)
Swift 已经定义了以下函数:
/// Forms a closed range that contains both `start` and `end`.
/// - Requires: `start <= end`.
@warn_unused_result
public func ...<Pos : ForwardIndexType where Pos : Comparable>(start: Pos, end: Pos) -> Range<Pos>
/// Forms a closed range that contains both `minimum` and `maximum`.
@warn_unused_result
public func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos>
/// Returns a closed interval from `start` through `end`.
@warn_unused_result
public func ...<Bound : Comparable>(start: Bound, end: Bound) -> ClosedInterval<Bound>
这意味着我们真正需要做的就是符合 ForwardIndexType
。正如 Rob 提到的,它不适用于 Comparable
协议,因为 returns 一个间隔,我们需要一个 for/in
循环的范围。
假设这是我们的共享代码:
import UIKit
struct MyColor {
let title: String
init(title: String) {
self.title = title
}
}
let red = MyColor(title: "Red")
let orange = MyColor(title: "Orange")
let yellow = MyColor(title: "Yellow")
let green = MyColor(title: "Green")
let blue = MyColor(title: "Blue")
let indigo = MyColor(title: "Indigo")
let violet = MyColor(title: "Violet")
let white = MyColor(title: "White")
let lightGray = MyColor(title: "Light Gray")
let darkGray = MyColor(title: "Dark Gray")
let black = MyColor(title: "Black")
// TODO: insert protocol conformance here!
print("Rainbow colors:")
for color in red...violet {
print(color.title)
}
print("")
print("Grayscale colors:")
for color in white...black {
print(color.title)
}
输出为:
Rainbow colors:
Red
Orange
Yellow
Green
Blue
Indigo
Violet
Grayscale colors:
White
Light Gray
Dark Gray
Black
我们可以通过遵守 ForwardIndexType
协议使 ...
工作:
let nullColor = MyColor(title: "Null")
extension MyColor: Equatable {}
@warn_unused_result
func ==(lhs: MyColor, rhs: MyColor) -> Bool {
return lhs.title == rhs.title
}
extension MyColor: ForwardIndexType {
@warn_unused_result
func successor() -> MyColor {
switch self {
case red: return orange
case orange: return yellow
case yellow: return green
case green: return blue
case blue: return indigo
case indigo: return violet
case violet: return nullColor
case white: return lightGray
case lightGray: return darkGray
case darkGray: return black
case black: return nullColor
case nullColor: fatalError()
default: fatalError()
}
}
}
注意:即使您只从 red...violet
循环,也需要 nullColor
(或某些终止符)。似乎系统实际上会执行 violet
情况,即使范围明显结束于 violet
.
另请注意,这仅适用于结构、枚举或最终 classes。如果您尝试将上述结构更改为 class,您将收到以下错误:
execution failed: protocol 'ForwardIndexType' requirement '_failEarlyRangeCheck(_:bounds:)' cannot be satisfied by a non-final class ('MyColor') because it uses 'Self' in a non-parameter, non-result type positionmethod 'advancedBy' in non-final class 'MyColor' must return `Self` to conform to protocol 'ForwardIndexType'method 'advancedBy(_:limit:)' in non-final class 'MyColor' must return `Self` to conform to protocol 'ForwardIndexType'
error: method 'successor()' in non-final class 'MyColor' must return `Self` to conform to protocol '_Incrementable'
func successor() -> MyColor {
^
有关为什么这不起作用的更多信息,请参阅 。
理论上你也应该能够通过实施 Comparable
协议来让它工作,但是当我尝试时,我得到:
error: binary operator '...' cannot be applied to two 'MyColor' operands
for color in red...violet {
~~~^ ~~~~~~
note: overloads for '...' exist with these partially matching parameter lists: (Bound, Bound), (Pos, Pos)
for color in red...violet {
^
在Swift中,我们可以使用range operators(...
和..<
)循环范围:
for i in 0...4 { ... }
假设我有很多颜色:
let red: MyColor
let orange: MyColor
let yellow: MyColor
let green: MyColor
let blue: MyColor
let indigo: MyColor
let violet: MyColor
let black: MyColor
let brown: MyColor
如何支持范围运算符以便我可以按彩虹顺序遍历颜色?
for rainbowColor in red...violet { ... }
所以...您可以对任何类型执行 for
-in
循环。您所要做的就是实现符合 SequenceType
的东西。然后,一旦您实现了 class 或结构,它就像创建一个 ...
运算符一样简单 return 是 SequenceType
.
假设如下:
struct RainbowColor {
let name: String
}
let red: RainbowColor = RainbowColor(name: "red")
let orange: RainbowColor = RainbowColor(name: "orange")
let yellow: RainbowColor = RainbowColor(name: "yellow")
let green: RainbowColor = RainbowColor(name: "green")
let blue: RainbowColor = RainbowColor(name: "blue")
let indigo: RainbowColor = RainbowColor(name: "indigo")
let violet: RainbowColor = RainbowColor(name: "violet")
我们可以这样创建一个RainbowColorSequence
:
struct RainbowColorSequence: SequenceType {
let startColor: RainbowColor
let endColor: RainbowColor
init(start: RainbowColor, end: RainbowColor) {
startColor = start
endColor = end
}
func generate() -> AnyGenerator<RainbowColor> {
return AnyGenerator<RainbowColor> {
// TODO: Implement your generator logic.
return nil
}
}
}
(请注意,此处省略了 return 中颜色的顺序以及序列结束时间的实际逻辑......因为它会很混乱。阅读底部以获取示例然而,这个实现可能是什么样子。)
一旦我们有了这个 RainbowColorSequence
,...
函数就非常简单了:
func ...(lhs: RainbowColor, rhs: RainbowColor) -> RainbowColorSequence {
return RainbowColorSequence(start: lhs, end: rhs)
}
现在,您可以编写以下代码:
for color in red...violet {
print(color.name)
}
有关生成器逻辑的示例,请查看 this question。我可以很容易地在 link 后面的代码中添加一个 ...
运算符,因为所有其他逻辑都已经到位,并且 ...
实际上只是围绕SequenceType
.
重要的是,您必须自己编写序列的逻辑。鉴于您的条件,编译器没有简单的方法来推断序列中的内容或序列的顺序。并且该逻辑可能会变得非常混乱,具体取决于您想要迭代的类型。
(注意这里是Swift3,如果要Swift2.2,看Senseful的回答,和老协议基本一样。)
您希望 Color
成为 Strideable
,Stride
为 Int
。然后你就可以免费获得 ...
和 ..<
。让我们这样做吧。
首先,我假设您的类型是这样的。这只是一个例子。你可以做所有 种 的事情,只要你能实现 Strideable
。你似乎想要结构而不是枚举(这很好),所以让我们做结构吧。这只是意味着任何人都可以添加其他颜色,您必须在设计中考虑到这一点。颜色需要一些东西来区分它们,所以我们会给它们一个名字。如果他们是类,那也可以区分他们。
struct Color {
let name: String
static let red = Color(name: "red")
static let orange = Color(name: "orange")
static let yellow = Color(name: "yellow")
static let green = Color(name: "green")
static let blue = Color(name: "blue")
static let indigo = Color(name: "indigo")
static let violet = Color(name: "violet")
}
Strideable 的第一条规则是 Equatable。
extension Color: Equatable {
static func == (lhs: Color, rhs: Color) -> Bool {
return lhs.name == rhs.name
}
}
现在可以等同了,我们应该想出我们所说的 "order." 的意思 这是一种方法。由于结构使意想不到的值成为可能,我已经说过,在我的任何彩虹色之前,它们都是相等的和有序的。如果这是一个枚举,我们就不必担心这种情况。
extension Color {
private static let _order: [Color] = [red, orange, yellow, green, blue, indigo, violet]
private var _indexValue: Int {
return Color._order.index(of: self) ?? Int.min
}
}
因此,我们可以回答问题 #2:可比较:
extension Color: Comparable {
static func < (lhs: Color, rhs: Color) -> Bool {
return lhs._indexValue < rhs._indexValue
}
}
迈向 Strideable 仅需一小步:
extension Color: Strideable {
func advanced(by n: Int) -> Color {
return Color._order[self._indexValue + n]
}
func distance(to other: Color) -> Int {
return other._indexValue - _indexValue
}
}
还有你的 ...
:
for rainbowColor: Color in .red ... .violet { print(rainbowColor) }
现在我碰巧很喜欢枚举,如果你试图用枚举来实现它,它们恰好有一点技巧,所以值得注意。例如,假设您有:
enum Color {
case red
case orange
case yellow
case green
case blue
case indigo
case violet
}
原来你不能用index(of:)
来计算_indexValue
。您会陷入无限循环,因为它在默认 ==
实现中调用 distance(to:)
(我实际上不确定为什么会发生这种情况)。所以你必须这样实现_indexValue
:
private var _indexValue: Int {
switch self {
case .red: return 0
case .orange: return 1
case .yellow: return 2
case .green: return 3
case .blue: return 4
case .indigo: return 5
case .violet: return 6
}
}
嗯;这看起来非常像原始值。是的。整个方法基本上是从侧面附加原始值,而无需修改类型本身。所以这就是为什么它看起来真的很相似。其余部分与结构相同,并且该方法可以应用于您可以弄清楚如何跨步的任何其他内容。
(注意:这是针对 Swift 2.2 的。如果您想要 Swift 3 版本,请参阅
Swift 已经定义了以下函数:
/// Forms a closed range that contains both `start` and `end`.
/// - Requires: `start <= end`.
@warn_unused_result
public func ...<Pos : ForwardIndexType where Pos : Comparable>(start: Pos, end: Pos) -> Range<Pos>
/// Forms a closed range that contains both `minimum` and `maximum`.
@warn_unused_result
public func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos>
/// Returns a closed interval from `start` through `end`.
@warn_unused_result
public func ...<Bound : Comparable>(start: Bound, end: Bound) -> ClosedInterval<Bound>
这意味着我们真正需要做的就是符合 ForwardIndexType
。正如 Rob 提到的,它不适用于 Comparable
协议,因为 returns 一个间隔,我们需要一个 for/in
循环的范围。
假设这是我们的共享代码:
import UIKit
struct MyColor {
let title: String
init(title: String) {
self.title = title
}
}
let red = MyColor(title: "Red")
let orange = MyColor(title: "Orange")
let yellow = MyColor(title: "Yellow")
let green = MyColor(title: "Green")
let blue = MyColor(title: "Blue")
let indigo = MyColor(title: "Indigo")
let violet = MyColor(title: "Violet")
let white = MyColor(title: "White")
let lightGray = MyColor(title: "Light Gray")
let darkGray = MyColor(title: "Dark Gray")
let black = MyColor(title: "Black")
// TODO: insert protocol conformance here!
print("Rainbow colors:")
for color in red...violet {
print(color.title)
}
print("")
print("Grayscale colors:")
for color in white...black {
print(color.title)
}
输出为:
Rainbow colors:
Red
Orange
Yellow
Green
Blue
Indigo
Violet
Grayscale colors:
White
Light Gray
Dark Gray
Black
我们可以通过遵守 ForwardIndexType
协议使 ...
工作:
let nullColor = MyColor(title: "Null")
extension MyColor: Equatable {}
@warn_unused_result
func ==(lhs: MyColor, rhs: MyColor) -> Bool {
return lhs.title == rhs.title
}
extension MyColor: ForwardIndexType {
@warn_unused_result
func successor() -> MyColor {
switch self {
case red: return orange
case orange: return yellow
case yellow: return green
case green: return blue
case blue: return indigo
case indigo: return violet
case violet: return nullColor
case white: return lightGray
case lightGray: return darkGray
case darkGray: return black
case black: return nullColor
case nullColor: fatalError()
default: fatalError()
}
}
}
注意:即使您只从 red...violet
循环,也需要 nullColor
(或某些终止符)。似乎系统实际上会执行 violet
情况,即使范围明显结束于 violet
.
另请注意,这仅适用于结构、枚举或最终 classes。如果您尝试将上述结构更改为 class,您将收到以下错误:
execution failed: protocol 'ForwardIndexType' requirement '_failEarlyRangeCheck(_:bounds:)' cannot be satisfied by a non-final class ('MyColor') because it uses 'Self' in a non-parameter, non-result type positionmethod 'advancedBy' in non-final class 'MyColor' must return `Self` to conform to protocol 'ForwardIndexType'method 'advancedBy(_:limit:)' in non-final class 'MyColor' must return `Self` to conform to protocol 'ForwardIndexType'
error: method 'successor()' in non-final class 'MyColor' must return `Self` to conform to protocol '_Incrementable'
func successor() -> MyColor {
^
有关为什么这不起作用的更多信息,请参阅
理论上你也应该能够通过实施 Comparable
协议来让它工作,但是当我尝试时,我得到:
error: binary operator '...' cannot be applied to two 'MyColor' operands
for color in red...violet {
~~~^ ~~~~~~
note: overloads for '...' exist with these partially matching parameter lists: (Bound, Bound), (Pos, Pos)
for color in red...violet {
^