尝试扩展 IntegerType(和 FloatingPointType);为什么不能将所有 Int 类型都转换为 NSTimeInterval
Trying to extend IntegerType (and FloatingPointType); Why can't all Int types be converted to NSTimeInterval
(这可能需要一个更好的标题...)
我想要一组可以在代码中使用的访问器来快速表达持续时间。例如:
42.seconds
3.14.minutes
0.5.hours
13.days
说明您不能只使用简单的新协议、扩展并强制 IntegerType
和 FloatingPointType
采纳它。所以我想我会走更冗余的路线,直接扩展 IntegerType
(然后重复 FloatingPointType
的代码)。
extension IntegerType {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
}
生成的错误令人困惑:
Playground execution failed: /var/folders/2k/6y8rslzn1m95gjpg534j7v8jzr03tz/T/./lldb/9325/playground131.swift:31:10: error: cannot invoke initializer for type 'NSTimeInterval' with an argument list of type '(Self)'
return NSTimeInterval(self)
^
/var/folders/2k/6y8rslzn1m95gjpg534j7v8jzr03tz/T/./lldb/9325/playground131.swift:31:10: note: overloads for 'NSTimeInterval' exist with these partially matching parameter lists: (Double), (UInt8), (Int8), (UInt16), (Int16), (UInt32), (Int32), (UInt64), (Int64), (UInt), (Int), (Float), (Float80), (String), (CGFloat), (NSNumber)
return NSTimeInterval(self)
让我感到困惑的是,它似乎说我不能用 (Self) 做一个 NSTimeInterval() 初始值设定项,但是 Self 代表的所有内容都列在下一行,它显示了 NSTimeInterval 的所有可能初始值设定项.我在这里错过了什么?
旁白:如果有关于 Swift 的类型系统和做这些事情的写得很好的教程,我会很高兴。 intermediate/advanced Apple 稀疏的 Swift 文档
中没有很好地涵盖这些内容
Update/Clarification:
我想要的是能够计算以上任何一个表达式:
42.seconds --> 42
3.14.minutes --> 188.4
0.5.hours --> 1800
13.days --> 1123200
此外,我希望它们的 return 类型为 NSTimeInterval(Double 的类型别名),这样:
42.seconds is NSTimeInterval --> true
3.14.minutes is NSTimeInterval --> true
0.5.hours is NSTimeInterval --> true
13.days is NSTimeInterval --> true
我知道我可以通过简单地扩展 Double
和 Int
来实现这一点:
extension Int {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
var minutes:NSTimeInterval {
return NSTimeInterval(self * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self * 3600 * 24)
}
}
extension Double {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
var minutes:NSTimeInterval {
return NSTimeInterval(self * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self * 3600 * 24)
}
}
但我还希望以下表达式起作用:
let foo:Uint = 4242
foo.minutes --> 254520
foo.minutes is NSTimeInterval --> true
但这行不通,因为我只扩展了 Int
,而不是 UInt
。我可以冗余扩展Uint
,然后UInt16
,然后Int16
,等等....
我想将 Int
的扩展概括为 IntegerType
,如原始列表中所示,这样我就可以获得所有整数类型的一般转换。然后对 FloatingPointType
而不是具体 Double
执行相同的操作。但是,这会产生原始错误。我想知道为什么我不能像通常显示的那样扩展 IntegerType。除了列表中显示的那些之外,是否还有其他 IntegerType
采纳者导致 NSTimeInterval() 初始值设定项无法解析?
NSTimeInterval
只是 Double
的别名。您可以通过定义自己的结构轻松实现您想要的效果,如下所示:
struct MyTimeInterval {
var totalSecs: NSTimeInterval
var totalHours: NSTimeInterval {
get {
return self.totalSecs / 3600.0
}
set {
self.totalSecs = newValue * 3600.0
}
}
init(_ secs: NSTimeInterval) {
self.totalSecs = secs
}
func totalHourString() -> String {
return String(format: "%.2f hours", arguments: [self.totalHours])
}
}
var t = MyTimeInterval(5400)
print(t.totalHourString())
extension Int {
var seconds: Int {
return self
}
var minutes: Int {
get {
return self * 60
}
}
var hours: Int {
get {
return minutes * 60
}
}
var timeString: String {
get {
let seconds = abs(self) % 60
let minutes = ((abs(self) - seconds) / 60) % 60
let hours = ((abs(self) - seconds) / 3660)
let sign = self < 0 ? "-" :" "
let str = String(format: "\(sign)%2d:%02d:%02d", arguments: [Int(hours),Int(minutes),Int(seconds)])
return str
}
}
}
6.minutes == 360.seconds // true
let t1 = 1.hours + 22.minutes + 12.seconds
let t2 = 12.hours - 15.minutes
let time = t1 - t2
print("t1 =",t1.timeString,t1) // t1 = 1:22:12 4932
print("t2 =",t2.timeString,t2) // t2 = 11:45:00 42300
print("t1-t2 =",time.timeString,time) // t1-t2 = -10:22:48 -37368
(12.hours / 30.minutes) == 24 // true
I could redundantly extend Uint, and then UInt16, and then Int16, etc....
正确。这就是今天 Swift 的做法。您不能在扩展中声明一个协议符合另一个协议。 ("Why?" "Because the compiler doesn't allow it.")
但这并不意味着您必须重写所有实现。你目前必须执行它三次(如果你想要执行四次 Float80
,但这在这里似乎没有用)。首先,你声明你的协议。
import Foundation
// Declare the protocol
protocol TimeIntervalConvertible {
func toTimeInterval() -> NSTimeInterval
}
// Add all the helpers you wanted
extension TimeIntervalConvertible {
var seconds:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval())
}
var minutes:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 3600 * 24)
}
}
// Provide the implementations. FloatingPointType doesn't have an equivalent to
// toIntMax(). There's no toDouble() or toFloatMax(). Converting a Float to
// a Double injects data noise in a way that converting Int8 to IntMax does not.
extension Double {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self) }
}
extension Float {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self) }
}
extension IntegerType {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self.toIntMax()) }
}
// And then we tell it that all the int types can get his implementation
extension Int: TimeIntervalConvertible {}
extension Int8: TimeIntervalConvertible {}
extension Int16: TimeIntervalConvertible {}
extension Int32: TimeIntervalConvertible {}
extension Int64: TimeIntervalConvertible {}
extension UInt: TimeIntervalConvertible {}
extension UInt8: TimeIntervalConvertible {}
extension UInt16: TimeIntervalConvertible {}
extension UInt32: TimeIntervalConvertible {}
extension UInt64: TimeIntervalConvertible {}
这就是 Swift 目前数字类型的处理方式。查看标准库。你会看到很多东西,比如:
extension Double {
public init(_ v: UInt8)
public init(_ v: Int8)
public init(_ v: UInt16)
public init(_ v: Int16)
public init(_ v: UInt32)
public init(_ v: Int32)
public init(_ v: UInt64)
public init(_ v: Int64)
public init(_ v: UInt)
public init(_ v: Int)
}
在某些情况下谈论 "number-like things?" 是不是很好。你不能在 Swift 今天。
"Why?"
因为编译器没有实现它。也许有一天。在那之前,为您想要的每种类型创建扩展。一年前,这需要更多代码。
请注意,虽然 一些 这是 "Swift doesn't have that feature yet," 一些也是故意的。 Swift 故意要求数字类型之间的显式转换。在数字类型之间进行转换通常会导致信息丢失或注入噪音,并且在历史上一直是棘手错误的来源。每次转换数字时都应该考虑这一点。例如,很明显,从 Int64 到 Int8 或从 UInt8 到 Int8 可能会丢失信息。但是从 Int64 到 Double 也会丢失信息。并非所有 64 位整数都可以表示为 Double。这是一个微妙的事实,在处理非常大的数字时经常会烧伤人们,Swift 鼓励您处理它。即使将 Float 转换为 Double 也会向您的数据中注入噪音。表示为 Float 的 1/10 与表示为 Double 的 1/10 是不同的值。当您将 Float 转换为 Double 时,您是否打算扩展重复数字?根据您选择的不同,您将引入不同类型的错误,因此您需要选择。
另请注意,您的 .days
可能会根据具体的问题域引入细微的错误。一天并不总是 24 小时。根据夏令时的变化,它可能是 23 小时或 25 小时。有时这很重要。有时它没有。但这是在将 "days" 视为特定秒数时要非常小心的原因。通常如果你想在几天内工作,你应该使用 NSDate
,而不是 NSTimeInterval
。我会非常怀疑那个特定的。
顺便说一句,您可能对 my old implementation of this idea 感兴趣。而不是使用语法:
1.seconds
我使用了语法:
1 * Second
然后将乘法重载到 return 结构而不是 Double
。以这种方式返回结构可以提供更好的类型安全性。例如,我可以键入检查 "time * frequency == cycles" 和 "cycles / time == frequency",这是 Double 无法做到的。不幸的是 NSTimeInterval 不是一个单独的类型;它只是 Double 的另一个名称。所以你放在 NSTimeInterval 上的任何方法都会应用于每个 Double(有时很奇怪)。
就我个人而言,我可能会这样解决整个问题:
let Second: NSTimeInterval = 1
let Seconds = Second
let Minute = 60 * Seconds
let Minutes = Minute
let Hour = 60 * Minutes
let Hours = Hour
let x = 100*Seconds
您甚至不需要重载运算符。已经为您完成了。
(这可能需要一个更好的标题...)
我想要一组可以在代码中使用的访问器来快速表达持续时间。例如:
42.seconds
3.14.minutes
0.5.hours
13.days
IntegerType
和 FloatingPointType
采纳它。所以我想我会走更冗余的路线,直接扩展 IntegerType
(然后重复 FloatingPointType
的代码)。
extension IntegerType {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
}
生成的错误令人困惑:
Playground execution failed: /var/folders/2k/6y8rslzn1m95gjpg534j7v8jzr03tz/T/./lldb/9325/playground131.swift:31:10: error: cannot invoke initializer for type 'NSTimeInterval' with an argument list of type '(Self)'
return NSTimeInterval(self)
^
/var/folders/2k/6y8rslzn1m95gjpg534j7v8jzr03tz/T/./lldb/9325/playground131.swift:31:10: note: overloads for 'NSTimeInterval' exist with these partially matching parameter lists: (Double), (UInt8), (Int8), (UInt16), (Int16), (UInt32), (Int32), (UInt64), (Int64), (UInt), (Int), (Float), (Float80), (String), (CGFloat), (NSNumber)
return NSTimeInterval(self)
让我感到困惑的是,它似乎说我不能用 (Self) 做一个 NSTimeInterval() 初始值设定项,但是 Self 代表的所有内容都列在下一行,它显示了 NSTimeInterval 的所有可能初始值设定项.我在这里错过了什么?
旁白:如果有关于 Swift 的类型系统和做这些事情的写得很好的教程,我会很高兴。 intermediate/advanced Apple 稀疏的 Swift 文档
中没有很好地涵盖这些内容Update/Clarification:
我想要的是能够计算以上任何一个表达式:
42.seconds --> 42
3.14.minutes --> 188.4
0.5.hours --> 1800
13.days --> 1123200
此外,我希望它们的 return 类型为 NSTimeInterval(Double 的类型别名),这样:
42.seconds is NSTimeInterval --> true
3.14.minutes is NSTimeInterval --> true
0.5.hours is NSTimeInterval --> true
13.days is NSTimeInterval --> true
我知道我可以通过简单地扩展 Double
和 Int
来实现这一点:
extension Int {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
var minutes:NSTimeInterval {
return NSTimeInterval(self * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self * 3600 * 24)
}
}
extension Double {
var seconds:NSTimeInterval {
return NSTimeInterval(self)
}
var minutes:NSTimeInterval {
return NSTimeInterval(self * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self * 3600 * 24)
}
}
但我还希望以下表达式起作用:
let foo:Uint = 4242
foo.minutes --> 254520
foo.minutes is NSTimeInterval --> true
但这行不通,因为我只扩展了 Int
,而不是 UInt
。我可以冗余扩展Uint
,然后UInt16
,然后Int16
,等等....
我想将 Int
的扩展概括为 IntegerType
,如原始列表中所示,这样我就可以获得所有整数类型的一般转换。然后对 FloatingPointType
而不是具体 Double
执行相同的操作。但是,这会产生原始错误。我想知道为什么我不能像通常显示的那样扩展 IntegerType。除了列表中显示的那些之外,是否还有其他 IntegerType
采纳者导致 NSTimeInterval() 初始值设定项无法解析?
NSTimeInterval
只是 Double
的别名。您可以通过定义自己的结构轻松实现您想要的效果,如下所示:
struct MyTimeInterval {
var totalSecs: NSTimeInterval
var totalHours: NSTimeInterval {
get {
return self.totalSecs / 3600.0
}
set {
self.totalSecs = newValue * 3600.0
}
}
init(_ secs: NSTimeInterval) {
self.totalSecs = secs
}
func totalHourString() -> String {
return String(format: "%.2f hours", arguments: [self.totalHours])
}
}
var t = MyTimeInterval(5400)
print(t.totalHourString())
extension Int {
var seconds: Int {
return self
}
var minutes: Int {
get {
return self * 60
}
}
var hours: Int {
get {
return minutes * 60
}
}
var timeString: String {
get {
let seconds = abs(self) % 60
let minutes = ((abs(self) - seconds) / 60) % 60
let hours = ((abs(self) - seconds) / 3660)
let sign = self < 0 ? "-" :" "
let str = String(format: "\(sign)%2d:%02d:%02d", arguments: [Int(hours),Int(minutes),Int(seconds)])
return str
}
}
}
6.minutes == 360.seconds // true
let t1 = 1.hours + 22.minutes + 12.seconds
let t2 = 12.hours - 15.minutes
let time = t1 - t2
print("t1 =",t1.timeString,t1) // t1 = 1:22:12 4932
print("t2 =",t2.timeString,t2) // t2 = 11:45:00 42300
print("t1-t2 =",time.timeString,time) // t1-t2 = -10:22:48 -37368
(12.hours / 30.minutes) == 24 // true
I could redundantly extend Uint, and then UInt16, and then Int16, etc....
正确。这就是今天 Swift 的做法。您不能在扩展中声明一个协议符合另一个协议。 ("Why?" "Because the compiler doesn't allow it.")
但这并不意味着您必须重写所有实现。你目前必须执行它三次(如果你想要执行四次 Float80
,但这在这里似乎没有用)。首先,你声明你的协议。
import Foundation
// Declare the protocol
protocol TimeIntervalConvertible {
func toTimeInterval() -> NSTimeInterval
}
// Add all the helpers you wanted
extension TimeIntervalConvertible {
var seconds:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval())
}
var minutes:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 60)
}
var hours:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 3600)
}
var days:NSTimeInterval {
return NSTimeInterval(self.toTimeInterval() * 3600 * 24)
}
}
// Provide the implementations. FloatingPointType doesn't have an equivalent to
// toIntMax(). There's no toDouble() or toFloatMax(). Converting a Float to
// a Double injects data noise in a way that converting Int8 to IntMax does not.
extension Double {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self) }
}
extension Float {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self) }
}
extension IntegerType {
func toTimeInterval() -> NSTimeInterval { return NSTimeInterval(self.toIntMax()) }
}
// And then we tell it that all the int types can get his implementation
extension Int: TimeIntervalConvertible {}
extension Int8: TimeIntervalConvertible {}
extension Int16: TimeIntervalConvertible {}
extension Int32: TimeIntervalConvertible {}
extension Int64: TimeIntervalConvertible {}
extension UInt: TimeIntervalConvertible {}
extension UInt8: TimeIntervalConvertible {}
extension UInt16: TimeIntervalConvertible {}
extension UInt32: TimeIntervalConvertible {}
extension UInt64: TimeIntervalConvertible {}
这就是 Swift 目前数字类型的处理方式。查看标准库。你会看到很多东西,比如:
extension Double {
public init(_ v: UInt8)
public init(_ v: Int8)
public init(_ v: UInt16)
public init(_ v: Int16)
public init(_ v: UInt32)
public init(_ v: Int32)
public init(_ v: UInt64)
public init(_ v: Int64)
public init(_ v: UInt)
public init(_ v: Int)
}
在某些情况下谈论 "number-like things?" 是不是很好。你不能在 Swift 今天。
"Why?"
因为编译器没有实现它。也许有一天。在那之前,为您想要的每种类型创建扩展。一年前,这需要更多代码。
请注意,虽然 一些 这是 "Swift doesn't have that feature yet," 一些也是故意的。 Swift 故意要求数字类型之间的显式转换。在数字类型之间进行转换通常会导致信息丢失或注入噪音,并且在历史上一直是棘手错误的来源。每次转换数字时都应该考虑这一点。例如,很明显,从 Int64 到 Int8 或从 UInt8 到 Int8 可能会丢失信息。但是从 Int64 到 Double 也会丢失信息。并非所有 64 位整数都可以表示为 Double。这是一个微妙的事实,在处理非常大的数字时经常会烧伤人们,Swift 鼓励您处理它。即使将 Float 转换为 Double 也会向您的数据中注入噪音。表示为 Float 的 1/10 与表示为 Double 的 1/10 是不同的值。当您将 Float 转换为 Double 时,您是否打算扩展重复数字?根据您选择的不同,您将引入不同类型的错误,因此您需要选择。
另请注意,您的 .days
可能会根据具体的问题域引入细微的错误。一天并不总是 24 小时。根据夏令时的变化,它可能是 23 小时或 25 小时。有时这很重要。有时它没有。但这是在将 "days" 视为特定秒数时要非常小心的原因。通常如果你想在几天内工作,你应该使用 NSDate
,而不是 NSTimeInterval
。我会非常怀疑那个特定的。
顺便说一句,您可能对 my old implementation of this idea 感兴趣。而不是使用语法:
1.seconds
我使用了语法:
1 * Second
然后将乘法重载到 return 结构而不是 Double
。以这种方式返回结构可以提供更好的类型安全性。例如,我可以键入检查 "time * frequency == cycles" 和 "cycles / time == frequency",这是 Double 无法做到的。不幸的是 NSTimeInterval 不是一个单独的类型;它只是 Double 的另一个名称。所以你放在 NSTimeInterval 上的任何方法都会应用于每个 Double(有时很奇怪)。
就我个人而言,我可能会这样解决整个问题:
let Second: NSTimeInterval = 1
let Seconds = Second
let Minute = 60 * Seconds
let Minutes = Minute
let Hour = 60 * Minutes
let Hours = Hour
let x = 100*Seconds
您甚至不需要重载运算符。已经为您完成了。