Общий протокол для базовых типов
Я размышлял о возможных реализациях форматирования строк для swift, большинство из которых сводилось к "использованию NSString (format:...)". Это хорошо и хорошо, но мне нужен сжатый и читаемый формат, поэтому Я решил реализовать что-то вроде оператора форматирования python%:
@infix func % (value:Double, format:String) -> String {
return NSString(format:format, value)
}
Это отлично работает для Double, поскольку я могу использовать:
println("PI = " + M_PI % "%.3f")
что приводит к:
PI = 3.142
Пока я могу создать 5 из них тривиально, я хотел бы превратить его в общую функцию:
@infix func %<T> (value:T, format:String) -> String {
return NSString(format:format, value)
}
Но это приводит к сообщению:
Could not find an overload for 'init' that accepts the supplied arguments
Достаточно разумный, я мог бы проходить в кортеже, или что-то в равной степени не objective-C. (Обратите внимание, что для действительно этого стиля Python я хочу передать в кортеж, но это другое дело и выходит за рамки этого вопроса)
Я попытался объявить свой собственный пустой протокол и реализовать его в Double, но это не помогло.
protocol NSStringFormattable {}
extension Double : NSStringFormattable {}
@infix func % <T:NSStringFormattable> (value:T, format:String) -> String {
return NSString(format:format, value)
}
Я мог бы сделать что-то вроде добавления функции формата для каждого класса, а затем просто определить оператор в терминах функции формата, но во многих отношениях это не лучше, чем просто определение 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)
}
Как ограничить T только теми типами, которые можно передать на NSString(format:...)
?
Ответы
Ответ 1
Вы очень близки, но вам не нужен кортеж с фиксированной длиной. Это то, что вызывает ваши головные боли. Просто используйте массив.
@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, yes"
Я не знаю, будет ли эта часть хрупкой. Литералы смешанного типа продвигаются до NSArray
, что кажется опасным для работы автоматически, поэтому они могут изменить его. Но NSArray
допустимо как CVarArg[]
.
Обратите внимание, что не все типы могут быть преобразованы таким образом. Например, персонажи не могут, например. Вы можете преодолеть это, расширив их, чтобы сделать это:
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"
Мне интересно, есть ли более простой способ написать encode()
, но я еще не уверен. Надеюсь, кодировка символов будет предоставлена Swift в будущем. Но здесь урок состоит в том, что произвольным типам может быть присвоен encode
и отформатирован.
class Car {
let make = "Ford"
}
extension Car : CVarArg {
func encode() -> Word[] {
return NSString(string:self.make).encode()
}
}
let car = Car()
[car] % "%@"
Урок состоит в том, что вы можете превращать произвольные вещи в CVarArg
или в любой протокол через расширения.
Ответ 2
Нашел его!
@infix func % (value:CVarArg, format:String) -> String {
return NSString(format:format, value)
}
Эта единственная функция позволяет:
5 % "%04x"
3.4 % "%.3f"
M_PI % "%.3f"
Int64(32) % "%04X"
К сожалению, он также позволяет:
"String" % "%3.3s"
и создает мусор, но добро пожаловать в printf без проверки типа аргументов
Кроме того, определяя набор функций:
@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)
}
Мы можем достичь эффектов, подобных питону:
(M_PI, 5) % "%.3f->%d"
Какой-то уродливый, чтобы определить один за кортеж, но я буду продолжать его взламывать:)
Ответ 3
Насколько я обнаружил (пройдя то же путешествие, что и вы), дженерики здесь не являются решением, а множественная перегрузка (что не очень) -
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)
}
Извините, обратный порядок параметров из вашего примера - позволяет
println("PI = %.3f" % M_PI)