问题描述
(这可能需要一个更好的标题...)
我希望有一组可以在代码中使用的访问器,以快速表示持续时间.例如:
I would like to have a set of accessors I can use in code to quickly express time durations. E.g:
42.seconds
3.14.minutes
0.5.hours
13.days
这篇文章说明了您不能只使用简单的新协议,扩展,并强制IntegerType
和FloatingPointType
采纳该协议.所以我想我应该走更多的冗余路线,直接扩展IntegerType
(然后重复FloatingPointType
的代码).
This post illustrates that you can't just do it with a simple new protocol, extension, and forcing IntegerType
and FloatingPointType
to adopt that. So I thought I'd just go the more redundant route and just extend IntegerType
directly (and then repeat the code for 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的所有可能的初始化方法.我在这里想念什么?
What confuses me is that it seems to say that I can't do an NSTimeInterval() initializer with (Self), but everything Self represents is listed in the next line where it shows all of the possible initializers of NSTimeInterval. What am I missing here?
此外:如果有关于Swift的类型系统以及做这些事情的书面教程,我会喜欢的.苹果的稀疏Swift文档中并没有很好地涵盖中级/高级内容
更新/澄清:
我想要的是能够评估以上任何表达式:
What I want is to be able to evaluate any of the above expressions:
42.seconds --> 42
3.14.minutes --> 188.4
0.5.hours --> 1800
13.days --> 1123200
此外,我希望这些的返回类型为NSTimeInterval(Double的类型别名),例如:
Furthermore, I want the return type of these to be NSTimeInterval (type alias for Double), such that:
42.seconds is NSTimeInterval --> true
3.14.minutes is NSTimeInterval --> true
0.5.hours is NSTimeInterval --> true
13.days is NSTimeInterval --> true
我知道我可以通过简单地扩展Double
和Int
这样来实现:
I know that I can achieve this by simply extending Double
and Int
as such:
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)
}
}
但是我也希望下面的表达式起作用:
But I would also like the following expression to work:
let foo:Uint = 4242
foo.minutes --> 254520
foo.minutes is NSTimeInterval --> true
这将不起作用,因为我仅扩展了Int
,而没有扩展UInt
.我可以冗余地扩展Uint
,然后扩展UInt16
,然后扩展Int16
,依此类推....
This won't work though because I have only extended Int
, not UInt
. I could redundantly extend Uint
, and then UInt16
, and then Int16
, etc....
我想要将Int
的扩展名概括为IntegerType
,如原始清单所示,这样我就可以大致获得所有整数类型的转换.然后对FloatingPointType
(而不是专门针对Double
)执行相同的操作.但是,这会产生原始错误.我想知道为什么我不能像通常显示的那样扩展IntegerType.除了列表中显示的采纳者以外,还有其他IntegerType
采纳者,因此NSTimeInterval()初始化程序无法解析吗?
I wanted to generalize the extension of Int
to IntegerType
as shown in the original listing, so that I could just gain the conversions generally for all integer types. And then do the same for FloatingPointType
rather than specifically Double
. However, that produces the original error. I want to know why I can't extend IntegerType as generally shown. Are there other IntegerType
adopters other than the ones shown in the list, that make it so the NSTimeInterval() initializer does not resolve?
推荐答案
正确.这就是今天在Swift中完成的方式.您不能声明某个协议符合扩展中的另一个协议. (为什么?"因为编译器不允许.")
Correct. That is how it's done today in Swift. You can't declare that a protocol conforms to another protocol in an extension. ("Why?" "Because the compiler doesn't allow it.")
但这并不意味着您必须重写所有实现.目前,您必须实施3次(如果需要Float80
,则需要实施4次,但这在这里似乎没有用).首先,您声明协议.
But that doesn't mean you have to rewrite all the implementation. You currently have to implement it three times (four times if you want Float80
, but that doesn't seem useful here). First, you declare your protocol.
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中当前数字类型的处理方式.查看stdlib.您会看到很多类似的东西:
This is how number types are currently done in Swift. Look through stdlib. You'll see lots of stuff like:
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)
}
在某些情况下谈论类似数字的东西"会很好吗?当然.您今天不能使用Swift.
Would it be nice in some cases to talk about "number-like things?" Sure. You can't in Swift today.
为什么?"
因为编译器没有实现它.可能有一天.在此之前,为您要使用的每种类型创建扩展名.一年前,这将花费更多的代码.
Because the compiler doesn't implement it. Some day it may. Until then, create the extension for every type you want it on. A year ago this would have taken even more code.
请注意,尽管其中的 some 是"Swift还没有该功能",但其中一些也是故意的. Swift有意要求在数字类型之间进行显式转换.在数字类型之间进行转换通常会导致信息丢失或产生噪音,并且在历史上一直是棘手的错误的来源.您每次转换数字时都应该考虑这一点.例如,很明显,从Int64到Int8或从UInt8到Int8可能会丢失信息.但是从Int64到Double也会丢失信息.并非所有的64位整数都可以表示为Double.这是一个微妙的事实,当处理大量数字时,它经常使人燃烧,Swift鼓励您处理它.即使将Float转换为Double,也会在数据中注入噪音.表示为Float的1/10与表示为Double的1/10不同.将Float转换为Double时,是否意味着要扩展重复数字?您将根据选择的错误而引入不同类型的错误,因此您需要选择.
Note that while some of this is "Swift doesn't have that feature yet," some is also on purpose. Swift intentionally requires explicit conversion between number types. Converting between number types can often lead to losing information or injecting noise, and has historically been a source of tricky bugs. You should be thinking about that every time you convert a number. For example, there's the obvious case that going from Int64 to Int8 or from UInt8 to Int8 can lose information. But going from Int64 to Double can also lose information. Not all 64-bit integers can be expressed as a Double. This is a subtle fact that burns people quite often when dealing with very large numbers, and Swift encourages you to deal with it. Even converting a Float to a Double injects noise into your data. 1/10 expressed as a Float is a different value than 1/10 expressed as a Double. When you convert the Float to a Double, did you mean to extend the repeating digits or not? You'll introduce different kinds of error depending on which you pick, so you need to pick.
还请注意,根据确切的问题域,您的.days
可能会引入细微的错误.一天并不总是24小时.根据DST的更改,它可能是23个小时或25个小时.有时那很重要.有时并非如此.但这是将天"视为特定秒数时要非常小心的原因.通常,如果您想在几天内工作,则应该使用NSDate
,而不是NSTimeInterval
.我会对那一个特别怀疑.
Note also that your .days
can introduce subtle bugs depending on the exact problem domain. A day is not always 24 hours. It can be 23 hours or 25 hours depending on DST changes. Sometimes that matters. Sometimes it doesn't. But it's a reason to be very careful about treating "days" as though it were a specific number of seconds. Usually if you want to work in days, you should be using NSDate
, not NSTimeInterval
. I'd be very suspicious of that particular one.
顺便说一句,您可能对我对此的旧实现感兴趣想法.而不是使用语法:
BTW, you may be interested in my old implementation of this idea. Rather than use the syntax:
1.seconds
我使用了语法:
1 * Second
,然后重载乘法以返回结构而不是Double
.以这种方式返回结构可提供更好的类型安全性.例如,我可以类型检查时间*频率==周期"和周期/时间==频率",这是Double不能做到的.不幸的是NSTimeInterval不是一个单独的类型.它只是Double的别称.因此,您在NSTimeInterval上放置的任何方法都将应用于每个Double(有时很奇怪).
And then overloaded multiplication to return a struct rather than a Double
. Returning a struct this way gives much better type safety. For example, I could type-check "time * frequency == cycles" and "cycles / time == frequency", which is something you can't do with Double. Unfortunately NSTimeInterval isn't a separate type; it's just another name for Double. So any method you put on NSTimeInterval is applied to every Double (which is sometimes weird).
我个人可能会这样解决整个问题:
Personally I'd probably solve this whole problem this way:
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
您甚至不需要使运算符超载.已经为您完成了.
You don't even need to overload the operators. It's already done for you.
这篇关于尝试扩展IntegerType(和FloatingPointType);为什么不能将所有Int类型都转换为NSTimeInterval的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!