本文介绍了基本类型的通用协议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在思考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'的重载

code>

合理的是,我可以传入一个元组,或者一些同样非客观的东西-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.

这篇关于基本类型的通用协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 04:01