Функция Swift pow () не принимает двойников в качестве аргументов


Я создал этот infix operator ^^ в качестве замены использования функции pow:

infix operator ^^ { associativity left precedence 155 }
func ^^ <T: IntegerLiteralConvertible>(left: T, right: T) -> T {
    return pow(left as Double, right as Double)
}
Я использовал протокол IntegerLiteralConvertible в качестве ограничения типа для генераторов left и right, потому что из моего понимания эта диаграмма показывает, что она в основном включает все типы чисел.

Чтобы использовать функцию pow, я должен понизить left и right до Double, что я и сделал, используя оператор as. Это не самый безопасный подход, но это к тому же точка.

При реализации функции таким образом swift говорит мне:

<stdin>:4:12: error: cannot invoke 'pow' with an argument list of type '(Double, Double)'
return pow(left as Double, right as Double)
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Теперь, насколько я знаю, pow принимает два Doubles в качестве параметров, так почему же он жалуется на это?

2 2

2 ответа:

Почему он жалуется на это?

Потому что pow возвращается Double. И Double не тождественно T. Сообщение об ошибке вводит в заблуждение, но оно означает "не удается найти pow, который принимает (Double, Double) и возвращает T тип"


Я думаю, что вы неправильно поняли "бросок" в Swift. В Swift as не преобразует числовые типы.

let intVal:Int = 12
let doubleVal:Double = intVal as Double
//                            ^ [!] error: 'Int' is not convertible to 'Double'

И если тип операнда не является предсказуемым во время компиляции, недопустимое приведение вызывает выполнение ошибка:

func foo<T: IntegerLiteralConvertible>(x: T)  {
    x as Double // <-- Execution was interrupted
}
foo(12 as Int)
Вместо этого мы должны явно "преобразовать" их. смотрите документ: преобразование числовых типов
let intVal:Int = 12
let doubleVal:Double = Double(intVal)

Это работает только потому, что Double имеет инициализатор init(_ v: Int). Следующий код не компилируется:

func foo<T: IntegerLiteralConvertible>(x: T)  {
    Double(x)
//  ^~~~~~~~~ [!] error: cannot invoke 'init' with an argument of type 'T'
}

Потому что Double не имеет инициализатора init<T:IntegerLiteralConvertible>(_ val:T).

Таким образом, если вы хотите использовать pow(), вы должны преобразовать T в Double для Аргументов и преобразовать Double в T для возвращаемого значения. И для этого нет простого решения.

Спасибо @ Martin R. это исходит из единственного вопроса, который я опубликовал до сих пор в S. O.

import Cocoa

Протоколы

protocol Fraction { init(_ value:Double) ; var asDouble  : Double { get } }
protocol Text     { init(_ value:String) ; var asString  : String { get } }

Расширения

extension String  : Text     { var asString : String { return self } }

extension Double  : Fraction { var asDouble : Double { return self         } }
extension Float   : Fraction { var asDouble : Double { return Double(self) } }
extension CGFloat : Fraction { var asDouble : Double { return Double(self) } }

Оператор Инфикса ^ ^

infix operator ^^ { associativity left precedence 170 }
func ^^<T:IntegerType, U:IntegerType> (var base:T, var power:U) -> T {
    if power < 0 { return 0 }
    var result: T = 1
    if power > 0 {
        if power % 2 == 1 { result *= base }
        power /= 2
    }
    while power > 0 {
        base *= base
        if power % 2 == 1 { result *= base }
        power /= 2
    }
    return result
}
func ^^<T:Fraction, U:Fraction> (base: T, power: U) -> T {
    return T(pow(base.asDouble, power.asDouble))
}
func ^^<T:Text, U:IntegerType> (base:T, var power:U) -> T {
    if power  <= 0 { return "" as T }
    if power  == 1 { return base as T }
    return power > 1 ? {var result = ""; for x in 1...power  { result+=base.asString };return result as T}() : "" as T
}
func ^^<T:Text, U:Text> (base:T, var power:U) -> T {
    return "" as T
}

Тестирование

println(1 ^^ -1)               // "0"      Int
println(1 ^^  0)               // "1"      Int
println(1 ^^  1)               // "1"      Int
println(1 ^^  2)               // "1"      Int

println(2 ^^ -1)               // "0"           Int
println(2 ^^  0)               // "1"           Int
println(2 ^^  1)               // "2"           Int
println(2 ^^  2)               // "4"           Int
println(2 ^^  8)               // "256"         Int
println(2 ^^  16)              // "65536"       Int
println(2 ^^  32)              // "4294967296"  Int

println(2 ^^  62)               // "4611686018427387904"

println(UInt(2) ^^ 8)          // "256"      UInt
println(UInt64(2) ^^ 8)        // "256"      UInt64