简体   繁体   English

基本类型的通用协议

[英]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.

相关问题 具有泛型类型的 Swift 协议 - Swift protocol with generic types 协议类型的异构混合,包括通用协议 - Heterogeneous mixture of protocol types, including a generic protocol 如何将协议类型传递给泛型函数? - How can I pass protocol types to generic functions? 如何为不符合特定协议的所有类型扩展通用类? - How to extend a generic class for all types that DOES NOT conform to a specific protocol? 在Swift中泛型类型的where子句中访问自定义协议的关联类型 - Access associated type of a custom protocol in a where clause on generic types in Swift 如何快速实例化具有不同类型的同一通用协议的多个实例? - How to instantiate multiple instances of the same generic protocol with different types in swift? 具有基本类型的Kotlin扩展泛型函数,没有反射API - Kotlin extension generic function with basic types without reflection API Swift 泛型/不透明类型:尝试使用协议一致性在泛型函数中的类型之间进行转换 - Swift Generics/Opaque Types: trying to use protocol conformance to convert between types in a generic function 专门用于通用协议的Swift协议 - Swift protocol specializing generic protocol Swift 3-具有静态方法的协议,具有泛型类型的实例方法,可选的&#39;.Type&#39;参数 - Swift 3 - Protocol with static methods, instance method with generic types, optional '.Type' parameter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM