问题描述
我一直在思考swift的字符串格式化的可能实现,其中大部分归结为使用NSString(格式:...)这一切都很好,但我想要一个简洁易读的格式,所以我决定实现类似python的%格式化操作符:
@infix func%(value:Double,format:String) - > ; String {
return NSString(format:format,value)
}
对于Double's来说,我可以使用它:
println(PI =+ M_PI%%.3f)
结果如下:
PI = 3.142
虽然我可以创建其中的5个,但我想把它变成一个通用函数:
@infix func%< T> (value:T,format:String) - > String {
return NSString(format:format,value)
}
但是结果是消息:
pre $ 无法找到接受提供参数的'init'的重载
合理的是,我可以传入一个元组,或者一些同样非客观的东西-C。 (请注意,为了实现这种Python风格,我想传入一个元组,但这是另一个问题,超出了这个问题的范围)。
我尝试了声明自己的空协议并在Double上实现它,但它根本没有帮助。
协议NSStringFormattable {}
扩展名Double:NSStringFormattable {}
@infix func%< T:NSStringFormattable> (value:T,format:String) - > String {
return NSString(format:format,value)
}
I显然可以做一些事情,比如为每个类添加一个格式化函数,然后用format函数来定义运算符,但在许多方面,这并不比定义5种不同的运算符重载更好。
protocol NSStringFormattable {
func format(format:String) - >字符串
}
扩展名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)
}
将T限制为只能传递给 NSString(format:...)
?
你非常接近,但你不需要一个固定长度的元组。这就是导致你头痛的原因。
@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
当然这是非常不安全的类型,因为它是一个未经检查的printf。
顺便说一句,这甚至适用于混合类型的东西和非文字:
let x = 1
let y = 1.5
let z =yes
[x,y,z]%%d,%。 2f,%@
==> 1,1.50,是的
我不知道这部分是否会变得脆弱,然而。混合类型的文字被提升为 NSArray
,这似乎是一个自动执行的危险的事情,所以他们可能会改变它。但是 NSArray
可以接受为 CVarArg []
。
请注意,并非所有类型都可以这种方式进行转换。目前不能使用字符。你可以通过扩展它来解决这个问题:
扩展名字符:CVarArg {
func encode() - > ; Word [] {
var result = Word []()
let s = String(self)
for s in.unicodeScalars {
result.append(Word值))
}
返回结果
}
}
让c:Character =c
[ I,c,2 * 3]%%@,%lc,%d
==> I,c,6
我想知道是否有更简单的方法来编写 encode()
,但我还不确定。希望将来有一个字符编码将由Swift提供。但是,这里的教训是,可以给任意类型一个 encode
并进行格式化。
class Car {
let make =Ford
}
extension Car:CVarArg {
func encode() - > Word [] {
return NSString(string:self.make).encode()
}
}
let car = Car()
[汽车]%%@
这里的教训是你可以把任意的东西通过扩展名到 CVarArg
或任何协议。
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:
@infix func % (value:Double, format:String) -> String {
return NSString(format:format, value)
}
This works great for Double's as I can use:
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:
@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. (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)
I tried declaring my own empty protocol and implementing that on Double, but it didn't help at all.
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.
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:...)
?
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.
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. But NSArray
is acceptable as a 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. Hopefully a Character encoding will be provided by Swift in the future. But the lesson here is that arbitrary types could be given an encode
and be formatted.
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.
这篇关于基本类型的通用协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!