2016-11-02 2 views
0

Я использовал этот метод расширения для генерации случайных чисел:Swift3 Random Extension

func Rand(_ range: Range<UInt32>) -> Int { 
     return Int(range.lowerBound + arc4random_uniform(range.upperBound - range.lowerBound + 1)) 
    } 

мне понравилось б/с, что не было никакой глупости, ты только что назвал это так:

let test = Rand(1...5) //generates a random number between 1 and 5 

честно, я не знаю, почему вещи должны быть настолько сложными в Swift, но я отвлекся ..

так что я получаю ошибку сейчас в Swift3

No '...' candidates produce the expected contextual result type 'Range<UInt32>' 

Может кто-нибудь знать, что это значит, или как я могу снова запустить свою замечательную функцию Rand? Я думаю, x ... y больше не создает диапазоны или x..y должно быть явно определено как UInt32? Любые советы для меня, чтобы сделать вещи немного легче?

Большое спасибо, оцените ваше время!

ответ

3

В Swift 3 есть четыре уровня структуры:

  • "x" ..< "y"Range<T>
  • "x" ... "y"ClosedRange<T>
  • 1 ..< 5CountableRange<T>
  • 1 ... 5CountableClosedRange<T>

(Операторы ..< и ... перегружены, так что если элементы являются stridable (итераторы произвольного доступа, например, номера и указатели), счетный диапазон будет возвращен. Но эти операторы все равно могут возвращать равные диапазоны, чтобы удовлетворить проверку типов.)

Поскольку Range и ClosedRange представляют собой разные структуры, вы не можете неявно преобразовывать их друг в друга и, следовательно, ошибку.

Если вы хотите Rand принять ClosedRange, а также Range, вы должны перегружать:

// accepts Rand(0 ..< 5) 
func Rand(_ range: Range<UInt32>) -> Int { 
    return Int(range.lowerBound + arc4random_uniform(range.upperBound - range.lowerBound)) 
} 

// accepts Rand(1 ... 5) 
func Rand(_ range: ClosedRange<UInt32>) -> Int { 
    return Int(range.lowerBound + arc4random_uniform(range.upperBound + 1 - range.lowerBound)) 
} 
0

Вы можете переписать Rand() использовать Int, если это ваш основной случай использования:

func Rand(_ range: Range<Int>) -> Int { 
    let distance = UInt32(range.upperBound - range.lowerBound) 
    return range.lowerBound + Int(arc4random_uniform(distance + 1)) 
} 

Или, как kennytm указывает, использовать Rand(1..<6)

4

Приятное решение представлены в Generic Range Algorithms (на основе How to be DRY on ranges and closed ranges? в стремительной списке рассылки).

Он использует тот факт, что оба CountableRange и CountableClosedRange являются коллекции, и в самом деле RandomAccessCollection.

Таким образом, вы можете определить один (общий) функцию, которая принимает открытые и закрытые целым числом в диапазоне:

func rand<C: RandomAccessCollection>(_ coll: C) -> C.Iterator.Element { 
    precondition(coll.count > 0, "Cannot select random element from empty collection") 
    let offset = arc4random_uniform(numericCast(coll.count)) 
    let idx = coll.index(coll.startIndex, offsetBy: numericCast(offset)) 
    return coll[idx] 
} 

rand(1...5) // random number between 1 and 5 
rand(2..<10) // random number between 2 and 9 

, но также:

rand(["a", "b", "c", "d"]) // random element from the array 

В качестве альтернативы в качестве способа расширения протокола:

extension RandomAccessCollection { 

    func rand() -> Iterator.Element { 
     precondition(count > 0, "Cannot select random element from empty collection") 
     let offset = arc4random_uniform(numericCast(count)) 
     let idx = index(startIndex, offsetBy: numericCast(offset)) 
     return self[idx] 
    } 
} 

(1...5).rand() 
(2..<10).rand() 
["a", "b", "c", "d"].rand() 
+0

Есть ли способ ограничить функцию 'rand', чтобы принимать только коллекцию n 'Int'? – rmaddy

+1

@rmaddy: 'func rand (_ coll: C) -> C.Iterator.Element где C.Iterator.Element == Int' для свободной функции или' extension RandomAccessCollection где Iterator.Element == Int' для метода расширения. –

Смежные вопросы