Расширение Swift map(:) для Set()?

  let numberSet = Set(1...11)
  let divideSet = numberSet.map({   / 10 }) 
  //Error: Set does not have a member named map   :(

Swift 1.2 поддерживает Set() для неупорядоченных коллекций, но map(_:) похоже, не работает на множествах, поэтому я решил поумнеть на своей игровой площадке и попытался:

 let stringSet = Set(map(numberSet, { String()}))
 println(stringSet)
 stringSet =  ["2", "11", "1", "8", "6", "4", "3", "9", "7", "10", "5]

похоже, это сработало. Поэтому я попытался расширить набор:

    extension Set {
        func map<U>(transform: (T) -> U) -> Set<U> {
        return Set(Swift.map(self, transform))  }
    }
   Error: "couldn't find initialiser for Set(T) that accepts argument of type U"

и я думаю, что есть хорошая причина, почему это не работает, как, например, здесь:

   let smarDividSet = Set(map(numberSet, { / 2})) 
   println(smarDividSet)
   smartDividSet = "[5, 0, 2, 4, 1, 3]” 
  //Somehow elements is the Set are going missing.

любые идеи о том, как расширить набор для использования map(_:) надежно ?. Спасибо ребята.

3 ответов


Вы были почти там. По какой-то причине общий тип возвращаемый набор должен быть указан явно:

extension Set {
    func map<U>(transform: (T) -> U) -> Set<U> {
        return Set<U>(Swift.map(self, transform))
    }
}

пример:

let numberSet = Set(1...11)

let divideSet = numberSet.map {  / 2 }
println(divideSet) // [5, 0, 2, 4, 1, 3]

результирующий набор имеет меньше элементов, потому что целочисленное деление / 2 усекает коэффициент, например, 4/2 и 5/2 сопоставляются с один и тот же элемент 2. Это не происходит с делением с плавающей запятой:

let floatdivideSet = numberSet.map { Double() / 2.0 }
println(floatdivideSet) // [4.0, 5.0, 4.5, 5.5, 2.0, 3.0, 3.5, 2.5, 1.5, 1.0, 0.5]

другой возможной реализацией является

extension Set {
    func map<U>(transform: (T) -> U) -> Set<U> {
        return Set<U>(lazy(self).map(transform))
    }
}

здесь lazy(self) возвращает LazyForwardCollection, который имеет а map() метод, который возвращает LazyForwardCollection снова. Преимущество может заключаться в том, что промежуточный массив не создается.


обновление: довольно много изменилось с Swift 2 и 3. Непатентованное средство буква Set теперь Element вместо T и все сборники have a map() метод, который возвращает массив.

были также хорошие аргументы, приведенные о проблемах Set -> Set сопоставление (например, сопоставление различных элементов с одним и тем же результатом). С другой стороны, для такого сопоставления может существовать прецедент, Итак, вот обновление для Swift 3 (теперь используется другое имя).

extension Set {
    func setmap<U>(transform: (Element) -> U) -> Set<U> {
        return Set<U>(self.lazy.map(transform))
    }
}

пример:

let numberSet = Set(1...11)
let divideSet = numberSet.setmap {  / 2 }
print(divideSet) // [5, 0, 2, 4, 1, 3]

вопрос с Set.map(_:) то же самое с Dictionary.map(_:), и они не реализовали его в Swift модуль (стандартная библиотека), потому что на самом деле не правильно для его реализации. Причина: mapping не просто перечисляет (что вы можете сделать с любым SequenceType на for-in), но он преобразует (с закрытием аргумента) каждое значение в другое. Таким образом, вы ожидаете, что результат будет иметь все transform(element) в нем, но думаю, что, если значения же они рушатся (ибо Dictionarys только ключи ведут себя таким образом).

например, (с предлагаемой реализации) Set([1, 2, 3, 4, 5]).map { 1 }.count == 1

вот почему Swift.map возвращает Array и не тот же тип SequenceType/CollectionType прошло как , хотя с DictionaryС коллекции кортежей, а не только значение. Таким образом, может быть что-то похожее на mapValues что было бы правильно, но не истинно map на сборе.

TL; DR

map действительно полезно, но будьте осторожны в том, что вы на самом деле делаете, потому что вы не можете действительно сделать map С Set для Set.

по состоянию на Swift 2.0 CollectionType протокола (осуществляется Set), имеет map метод.