I have this function that returns an integer within bounds:
func randomNumber<T: IntegerType>(min: T, max: T) -> T {
let n = max - min + 1
let u = UInt32(n) // Error: Cannot invoke 'init' with an argument of type 'T'
let r = arc4random_uniform(u)
return r + min
}
I dont understand why this is not working because UInt32
top most protocol is UnsignedIntegerType
, which conforms to IntegerType
.
I have to make n
into UInt32
because arc4random_uniform()
takes a UInt32
as parameter
Why wont this work?
The trouble is that UInt32
has no init
that takes an arbitrary IntegerType
. Sure, it takes every defined one in the standard library, but what if someone implemented a UInt128
that conformed to IntegerType
? Even if you substituted in let u = UInt32(n.toIntMax())
, you'll be stuck when trying to add r
to min
since, again, there isn't an implementation of +
that will add a UInt32
to any arbitrary IntegerType
. This makes sense given the overflow possibilities – you know arc4random_uniform(u)
will never return UInt32
greater than, say, an Int8.max
, but Swift can't. You'd need much richer functionality than IntegerType
provides to write a truly generic version of this function that had correct pre- and post-conditions.
You can create a new Integer Protocol to accept any UInt and also limit UInt64 to UInt32.max to conform to arc4random_uniform limit as follow:
protocol Integer {
init(_ value:Int)
var integerValue: Int { get }
}
extension Int : Integer { var integerValue : Int { return self } }
extension UInt64 : Integer { var integerValue : Int { return Int(self) } }
extension UInt32 : Integer { var integerValue : Int { return Int(self) } }
extension UInt16 : Integer { var integerValue : Int { return Int(self) } }
extension UInt8 : Integer { var integerValue : Int { return Int(self) } }
extension UInt : Integer { var integerValue : Int { return Int(self) } }
func randomNumber(min: Integer, max: Integer) -> Int {
if min.integerValue >= max.integerValue { return 0 }
if max.integerValue-min.integerValue+1 > UInt32.max { return 0 }
return (min.integerValue + arc4random_uniform(UInt32(max.integerValue - min.integerValue + 1))).integerValue
}
randomNumber(UInt(10), UInt64(13))
randomNumber(UInt8(10), UInt32(13))
randomNumber(UInt16(10), UInt16(13))
randomNumber(UInt32(10), UInt8(13))
randomNumber(UInt64(10), UInt(13))
You need to make at least 2 functions: one for SignedIntegerType
, one for UnsignedIntegerType
.
SignedIntegerType
has type coercing functions: toIntMax()
and init(_: IntMax)
protocol _SignedIntegerType : _IntegerType, SignedNumberType {
/// Represent this number using Swift's widest native signed integer
/// type.
func toIntMax() -> IntMax
/// Convert from Swift's widest signed integer type, trapping on
/// overflow.
init(_: IntMax)
}
UnsignedIntegerType
also has type coercing functions: toUIntMax()
and init(_: UIntMax)
protocol _UnsignedIntegerType : _IntegerType {
/// Represent this number using Swift's widest native unsigned
/// integer type.
func toUIntMax() -> UIntMax
/// Convert from Swift's widest unsigned integer type, trapping on
/// overflow.
init(_: UIntMax)
}
Using these functions, you can:
func randomNumber<T: UnsignedIntegerType>(min: T, max: T) -> T {
let n = max - min + 1
let u = UInt32(n.toUIntMax())
let r = arc4random_uniform(u)
return T(r.toUIntMax()) + min
}
func randomNumber<T: SignedIntegerType>(min: T, max: T) -> T {
let n = max - min + 1
let u = UInt32(n.toIntMax())
let r = arc4random_uniform(u)
return T(r.toIntMax()) + min
}
But, we already have handy numericCast
builtin functions:
func numericCast<T : _UnsignedIntegerType, U : _SignedIntegerType>(x: T) -> U
func numericCast<T : _SignedIntegerType, U : _UnsignedIntegerType>(x: T) -> U
func numericCast<T : _UnsignedIntegerType, U : _UnsignedIntegerType>(x: T) -> U
func numericCast<T : _SignedIntegerType, U : _SignedIntegerType>(x: T) -> U
numericCast
can simplify your implementations:
func randomNumber<T: UnsignedIntegerType>(min: T, max: T) -> T {
return min + numericCast(arc4random_uniform(numericCast(max - min + 1)))
}
func randomNumber<T: SignedIntegerType>(min: T, max: T) -> T {
return min + numericCast(arc4random_uniform(numericCast(max - min + 1)))
}
Inner numericCast
converts T
to UInt32
, outer one converts UInt32
to T
.
Now, these functions have exact the same implementation codes :) But I think you cannot unify them into one function.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.