[英]Generic protocol for basic types
I've been pondering the possible implementations of string formatting for swift, most of which boil down to "use NSString(format:...)" That's all well and good, but I wanted a concise and readable format, so I decided to implement something like python's % formatting operator: 我一直在思考swift的字符串格式化的可能实现,其中大多数归结为“使用NSString(格式:...)”这一切都很好,但我想要一个简洁易读的格式,所以我决定实现类似python的%格式化运算符:
@infix func % (value:Double, format:String) -> String {
return NSString(format:format, value)
}
This works great for Double's as I can use: 这适用于Double的,因为我可以使用:
println("PI = " + M_PI % "%.3f")
which results in: 这导致:
PI = 3.142
While I can create 5 of these trivially, I'd like to turn it into a generic function: 虽然我可以创造其中的5个,但我想把它变成一个通用函数:
@infix func %<T> (value:T, format:String) -> String {
return NSString(format:format, value)
}
But that results in the message: 但这导致了这样的信息:
Could not find an overload for 'init' that accepts the supplied arguments
Reasonable enough, I could be passing in a tuple, or something equally non-objective-C. 合理的,我可以传入一个元组,或者同样非客观的东西-C。 (Note that to really do this Python-style, I want to pass in a tuple, but that's another matter and beyond the scope of this question)
(注意,要真正做到这种Python风格,我想传入一个元组,但这是另一个问题,超出了这个问题的范围)
I tried declaring my own empty protocol and implementing that on Double, but it didn't help at all. 我尝试声明我自己的空协议并在Double上实现它,但它根本没有帮助。
protocol NSStringFormattable {}
extension Double : NSStringFormattable {}
@infix func % <T:NSStringFormattable> (value:T, format:String) -> String {
return NSString(format:format, value)
}
I could obviously do something like add a format function to each class and then just define the operator in terms of the format function, but in many ways that's not any better than just defining 5 different operator overloads. 我显然可以做一些事情,比如为每个类添加一个格式函数,然后根据格式函数定义运算符,但在许多方面,这并不比仅定义5个不同的运算符重载更好。
protocol NSStringFormattable {
func format(format:String) -> String
}
extension Double : NSStringFormattable {
func format(format:String) -> String {
return NSString(format:format, self)
}
}
@infix func % <T:NSStringFormattable> (value:T, format:String) -> String {
return value.format(format)
}
How can I restrict T to only those types that can be passed to NSString(format:...)
? 如何将T限制为只能传递给
NSString(format:...)
那些类型NSString(format:...)
?
Found it! 找到了!
@infix func % (value:CVarArg, format:String) -> String {
return NSString(format:format, value)
}
This single function allows for: 这个单一功能允许:
5 % "%04x"
3.4 % "%.3f"
M_PI % "%.3f"
Int64(32) % "%04X"
Unfortunately, it also allows for: 不幸的是,它还允许:
"String" % "%3.3s"
and produces garbage, but welcome to printf without argument type checking 并产生垃圾,但欢迎使用printf而不进行参数类型检查
Even further, by defining a set of functions: 更进一步,通过定义一组函数:
@infix func % (values:(CVarArg, CVarArg), format:String) -> String {
return NSString(format:format, values.0, values.1)
}
@infix func % (values:(CVarArg, CVarArg, CVarArg), format:String) -> String {
return NSString(format:format, values.0, values.1, values.2)
}
We can achieve python-like affects: 我们可以实现类似python的影响:
(M_PI, 5) % "%.3f->%d"
Kind of ugly to have to define one per tuple-length, but I'll keep hacking on it :) 有点难看,每个元组长度必须定义一个,但我会继续黑客攻击:)
You're very close, but you don't need a fixed-length tuple. 你非常接近,但你不需要一个固定长度的元组。 That's what's causing your headaches.
这就是导致你头痛的原因。 Just use an array instead.
只需使用数组即可。
@infix func % (values:CVarArg[], format:String) -> String {
return NSString(format:format, arguments:getVaList(values))
}
[M_PI, 6] % "%.3f->%d"
==> "3.142->6"
[M_PI, M_PI_2] % "%.3f %.3f"
==> "3.142 1.571"
Of course this is highly type-unsafe because it's an unchecked printf as you say. 当然,这是高度类型不安全的,因为它是一个未经检查的printf,如你所说。
BTW, this even works with mixed-type stuff, and with non-literals: 顺便说一句,这甚至适用于混合类型的东西,以及非文字:
let x = 1
let y = 1.5
let z = "yes"
[x, y, z] % "%d, %.2f, %@"
==> "1, 1.50, yes"
I don't know if that part is going to be fragile, however. 但是,我不知道那部分是否会变得脆弱。 Mixed-type literals are promoted to
NSArray
, which seems a dangerous thing to do automatically, so they may change it. 混合型文字被提升为
NSArray
,这似乎是自动执行的危险事情,因此他们可能会改变它。 But NSArray
is acceptable as a CVarArg[]
. 但是
NSArray
可以接受为CVarArg[]
。
Note that not all types can be converted this way. 请注意,并非所有类型都可以通过这种方式转换。 Characters currently cannot for instance.
例如,字符不能。 You can overcome this by extending them to do so:
您可以通过扩展它们来解决这个问题:
extension Character : CVarArg {
func encode() -> Word[] {
var result = Word[]()
let s = String(self)
for c in s.unicodeScalars {
result.append(Word(c.value))
}
return result
}
}
let c:Character = "c"
["I", c, 2*3] % "%@, %lc, %d"
==> "I, c, 6"
I'm wondering if there's an easier way to write encode()
, but I'm not sure yet. 我想知道是否有更简单的方法来编写
encode()
,但我还不确定。 Hopefully a Character encoding will be provided by Swift in the future. 希望未来Swift将提供字符编码。 But the lesson here is that arbitrary types could be given an
encode
and be formatted. 但是这里的教训是任意类型都可以被赋予
encode
并被格式化。
class Car {
let make = "Ford"
}
extension Car : CVarArg {
func encode() -> Word[] {
return NSString(string:self.make).encode()
}
}
let car = Car()
[car] % "%@"
The lesson here is that you can turn arbitrary things into a CVarArg
or into any protocol, via extensions. 这里的教训是,您可以通过扩展将任意事物转换为
CVarArg
或任何协议。
As far as I have discovered (having gone through the same journey as you), generics are not the solution here, rather multiple overloading (which isn't pretty) - 据我所知(与你经历了相同的旅程),泛型不是这里的解决方案,而是多次重载(这不太漂亮) -
operator infix % { }
@infix func % (format: String, value: Double) -> String {
return NSString(format:format, value)
}
@infix func % (format: String, value: Float) -> String {
return NSString(format:format, value)
}
@infix func % (format: String, value: Int) -> String {
return NSString(format:format, value)
}
sorry, reverse parameter order from your example - allows 抱歉,从您的示例反向参数顺序 - 允许
println("PI = %.3f" % M_PI)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.