Быстрая карта (_:) для Set()?

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

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

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

Казалось, это сработало. Поэтому я попытался расширить Set:

    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, {$0 / 2})) 
   println(smarDividSet)
   smartDividSet = "[5, 0, 2, 4, 1, 3]" 
  //Somehow elements is the Set are going missing.

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

Ответ 1

Обновление: довольно много изменилось с Swift 2 и 3. Общая заполнитель Set теперь Element вместо T, и все коллекции имеют метод 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 { $0 / 2 }
print(divideSet) // [5, 0, 2, 4, 1, 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 { $0 / 2 }
println(divideSet) // [5, 0, 2, 4, 1, 3]

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

let floatdivideSet = numberSet.map { Double($0) / 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. Преимущество может заключаться в том, что промежуточный массив не создается.

Ответ 2

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

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

Вот почему Swift.map возвращает Array, а не тот же тип SequenceType/CollectionType, переданный как аргумент source.

Чтобы немного отвлечься, Dictionary (как было сказано ранее) имеет ту же проблему с ключевыми значениями (что в основном является Set), поэтому любой map, который работает либо на Set<K>, либо на Set<(K, V)> не обеспечивает согласованное отображение, но значение, которое изменяет значения, будет только в порядке. Это не было бы истинным map, хотя, поскольку Dictionary являются наборами кортежей, а не только для значений. Таким образом, может быть что-то похожее на mapValues, которое было бы правильным, но все же не истинным map в коллекции.

TL; DR

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

Ответ 3

С Swift 2.0 протокол CollectionType (реализованный Set) имеет метод map.