Swift: 调用枚举值转换器函数作为第一个 class 函数
Swift: Calling an Enum value converter function as a first class function
enum SolarSystemPlanet: String, CaseIterable {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
value.rawValue
}
}
使用上面的枚举,获取行星名称数组的一种方法是调用
SolarSystemPlanet.allCases.map { [=13=].rawValue }
但是 Swift 支持 first-class 函数,将函数视为 "first class citizens",这使我们可以像调用任何其他对象或值一样调用函数。
所以通过这个得到一个名字数组会很好
SolarSystemPlanet.allCases.map(.toRawValue)
但是,编译器似乎需要更多上下文。它无法在编译时推断 map
中的类型,所以我做了
SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)
编译器不再报错,但我没有得到字符串数组。 return 上面的行是一个 [(SolarSystemPlanet) -> String]
类型的值
如果我把上面的打印出来,而不是得到
["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]
我得到了
[(Function), (Function), (Function), (Function), (Function), (Function), (Function), (Function)]
如果我强制 return 类型为 [String]
像这样
var planets: [String] = SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)
Xcode 会抱怨 [(SolarSystemPlanet) -> String]
无法转换为 [String]
到底有没有可能实现我想要做的事情?我是漏了什么还是做错了什么?
如果不可能,我也非常感谢对原因的一些解释。
感谢您花时间阅读我的问题!
编辑 感谢 @sweeper 的回答。
对于那些感兴趣的人,我稍微进一步确保每个字符串枚举都有 toRawValue
extension RawRepresentable where RawValue == String {
static func toRawValue(_ value: Self) -> PlanetName {
value.rawValue
}
}
注:这是Swift5.1.3
请注意 toRawValue
不需要是实例方法。可以是静态的:
static func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
value.rawValue
}
现在您可以使用 SolarSystemPlanet.toRawValue
作为 map
的参数。
或者,在这种情况下,您还可以使用 \.rawValue
的 keypath 作为 map
的参数:
SolarSystemPlanet.allCases.map(\.rawValue)
这是 Swift 5.2 的新功能。
编辑:实例方法为何不起作用的解释
在 Swift 中,当从静态上下文访问时,具有签名 (U) -> R
的类型 T
上的实例方法变为具有签名 [=20= 的静态方法].实例方法需要一个封闭类型的实例来调用,对吗?因此,当您将 T
传递给它时,它会返回原始实例函数 (U) -> R
。
因此,非静态SolarSystemPlanet.toRawValue
的类型是
(SolarSystemPlanet) -> ((SolarSystemPlanet) -> String)
这解释了为什么在应用 map
之后,数组变成了 [(SolarSystemPlanet) -> String]
。
enum SolarSystemPlanet: String, CaseIterable {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
value.rawValue
}
}
使用上面的枚举,获取行星名称数组的一种方法是调用
SolarSystemPlanet.allCases.map { [=13=].rawValue }
但是 Swift 支持 first-class 函数,将函数视为 "first class citizens",这使我们可以像调用任何其他对象或值一样调用函数。
所以通过这个得到一个名字数组会很好
SolarSystemPlanet.allCases.map(.toRawValue)
但是,编译器似乎需要更多上下文。它无法在编译时推断 map
中的类型,所以我做了
SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)
编译器不再报错,但我没有得到字符串数组。 return 上面的行是一个 [(SolarSystemPlanet) -> String]
如果我把上面的打印出来,而不是得到
["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]
我得到了
[(Function), (Function), (Function), (Function), (Function), (Function), (Function), (Function)]
如果我强制 return 类型为 [String]
像这样
var planets: [String] = SolarSystemPlanet.allCases.map(SolarSystemPlanet.toRawValue)
Xcode 会抱怨 [(SolarSystemPlanet) -> String]
无法转换为 [String]
到底有没有可能实现我想要做的事情?我是漏了什么还是做错了什么?
如果不可能,我也非常感谢对原因的一些解释。
感谢您花时间阅读我的问题!
编辑 感谢 @sweeper 的回答。
对于那些感兴趣的人,我稍微进一步确保每个字符串枚举都有 toRawValue
extension RawRepresentable where RawValue == String {
static func toRawValue(_ value: Self) -> PlanetName {
value.rawValue
}
}
注:这是Swift5.1.3
请注意 toRawValue
不需要是实例方法。可以是静态的:
static func toRawValue(_ value: SolarSystemPlanet) -> PlanetName {
value.rawValue
}
现在您可以使用 SolarSystemPlanet.toRawValue
作为 map
的参数。
或者,在这种情况下,您还可以使用 \.rawValue
的 keypath 作为 map
的参数:
SolarSystemPlanet.allCases.map(\.rawValue)
这是 Swift 5.2 的新功能。
编辑:实例方法为何不起作用的解释
在 Swift 中,当从静态上下文访问时,具有签名 (U) -> R
的类型 T
上的实例方法变为具有签名 [=20= 的静态方法].实例方法需要一个封闭类型的实例来调用,对吗?因此,当您将 T
传递给它时,它会返回原始实例函数 (U) -> R
。
因此,非静态SolarSystemPlanet.toRawValue
的类型是
(SolarSystemPlanet) -> ((SolarSystemPlanet) -> String)
这解释了为什么在应用 map
之后,数组变成了 [(SolarSystemPlanet) -> String]
。