Быстрая карта (_:) для 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
.