2015-08-16 3 views
1

Может кто-нибудь объяснить мне, почему следующий код, похоже, не может различать числовые типы?Странная фильтрация поведения Swift Array по типу

extension Array { 
    func filterByType<T>(type: T.Type) -> [T] { 
     var r = [T]() 
     for case let m as T in self { 
      r += [m] 
     } 
     return r 
    } 
    func filterByType2<T>(type: T.Type) -> [T] { 
     var r = [T]() 
     for m in self { 
      if m is T { 
       r += [m as! T] 
      } 
     } 
     return r 
    } 
} 

let objects = [1, "2", 3, "4", 5.1, [1, 2]] 

typealias IntArray = [Int] 

objects.filterByType(String.self) // ["2", "4"] - as expected 
objects.filterByType(IntArray.self) // [[1, 2]] - as expected 
objects.filterByType(Double.self) // [1, 3, 5.1] - ok, but surprised 1 & 3 aren't Ints 
objects.filterByType(Int.self) // [1, 3, 5] - why? 

objects.filterByType2(String.self) // ["2", "4"] - as expected 
objects.filterByType2(IntArray.self) // [[1, 2]] - as expected 
objects.filterByType2(Double.self) // [1, 3, 5.1] - ok, but surprised 1 & 3 aren't Ints 
objects.filterByType2(Int.self) // [1, 3, 5] - why? 
+1

Не могли бы вы попробовать это с 'let objects: [Any] = ...'? Я получил результаты, которые вы ожидаете именно так, но у меня установлен более ранний Xcode. – dasblinkenlight

+0

@dasblinkenlight: Вы правы, что дает «ожидаемые» результаты также с Xcode 7 beta 5. –

ответ

3

Без какой-либо типа аннотации, тип гетерогенного массива

let objects = [1, "2", 3, "4", 5.1, [1, 2]] 

выводятся в [NSObject]. В частности, цифры преобразуются в NSNumber объектов. A (условный) литой от NSNumber до Double или Int всегда преуспевает и использует метод doubleValue или integerValue .

Это можно увидеть на следующем простом примере:

let n1 = NSNumber(integer: 123) 
if let x = n1 as? Double { // warning: conditional cast from 'NSNumber' to 'Double' always succeeds 
    print(x) // 123.0 
} 

let n2 = NSNumber(double: 12.8) 
if let x = n2 as? Int { // warning: conditional cast from 'NSNumber' to 'Double' always succeeds 
    print(x) // 12 
} 

Смотрите также

+0

Отличный ответ. Большое спасибо. – Grimxn

0

Я считаю, что следующие две строки в filterByType и filterByType2 литья значения Int и Double. Типы String и [Int] дают ожидаемые результаты, потому что их нельзя отличить.

for case let m as T in self { 
// ... 
r += [m as! T] 

1, 3, 5.1 может быть приведен в качестве Double или Int.

1

Массив объектов выводится как типа NSObject и числа имеют тип NSNumber который может быть преобразован в Int, Double и даже Bool (возможно неожиданное).

Так что для того, чтобы получить желаемое поведение, вы должны явно объявить тип вашего массива, как [Any]

Как улучшения для вашего алгоритма я бы предложил использовать flatMap:

extension Array { 
    func filterByType<T>(type: T.Type) -> [T] { 
     return self.flatMap{ $0 as? T } 
    } 
}