Jak mogę rozszerzyć Swift Array<T>
lub T[]
pisać za pomocą niestandardowych narzędzi funkcjonalnych?
Przeglądanie dokumentów API Swift pokazuje, że metody Array są rozszerzeniem T[]
np .:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
Podczas kopiowania i wklejania tego samego źródła i wypróbowywania wszelkich odmian, takich jak:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
Nie można zbudować z błędem:
Typ nominalny
T[]
nie może zostać przedłużony
Używanie pełnej definicji typu kończy się niepowodzeniem Use of undefined type 'T'
, tzn .:
extension Array<T> {
func foo(){}
}
I to również nie działa z Array<T : Any>
i Array<String>
.
Co ciekawe, Swift pozwala mi rozszerzyć nietypową tablicę o:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Który pozwala mi dzwonić z:
[1,2,3].each(println)
Ale nie mogę utworzyć właściwego rozszerzenia typu ogólnego, ponieważ typ wydaje się być utracony, gdy przepływa przez metodę, np. Próbuje zastąpić wbudowany filtr Swift :
extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}
Ale kompilator traktuje go jako bez typu, w którym nadal pozwala wywoływać rozszerzenie za pomocą:
["A","B","C"].find { $0 > "A" }
A kiedy krok po kroku z debuggerem wskazuje, że typ jest, Swift.String
ale błędem kompilacji jest próba uzyskania dostępu jak ciąg bez rzutowania go na String
pierwsze, tj .:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Czy ktoś wie, jaki jest właściwy sposób na utworzenie typowej metody rozszerzenia, która działa jak wbudowane rozszerzenia?
extension T[]
bit po kliknięciu Command typu Array w XCode, ale nie widząc żadnego sposobu na jego wdrożenie bez otrzymania błędu.<T>
z podpisu metody.Odpowiedzi:
Aby rozszerzyć tablice typowe o klasy , poniżej działa dla mnie (Swift 2.2 ). Na przykład, sortowanie tablicy o typie:
Próba zrobienia tego za pomocą struct lub typali spowoduje błąd:
Aktualizacja :
Aby rozszerzyć tablice typowane o nieklasy, należy zastosować następujące podejście:
W Swift 3 zmieniono nazwy niektórych typów:
źródło
[Iterator.Element]
?Po chwili wypróbowania różnych rzeczy rozwiązanie wydaje się usuwać
<T>
z podpisu, takie jak:Który teraz działa zgodnie z przeznaczeniem bez błędów kompilacji:
źródło
filter
funkcją:let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
jest ono funkcjonalnie równoważne z twoimfind
, tzn. Wynik funkcji jest taki sam. Jeśli zamknięcie filtra ma skutki uboczne, z pewnością wyniki mogą Ci się nie podobać.filter
.filter
,map
ireduce
funkcje pochodzą z funkcje są realizowane na ich wartości zwracanych. Dla kontrastu,each
funkcja zdefiniowana powyżej jest przykładem funkcji wykonanej dla jej efektu ubocznego, ponieważ nic nie zwraca. Wydaje mi się, że możemy się zgodzić, że obecna implementacja Swift nie jest idealna, a dokumentacja nie zawiera żadnych informacji na temat jej właściwości.Rozszerz wszystkie typy:
Rozszerz niektóre typy:
Rozszerz określony typ:
źródło
Miałem podobny problem - chciałem rozszerzyć ogólny Array za pomocą metody swap (), która miała przyjmować argument tego samego typu co tablica. Ale jak określić typ ogólny? Odkryłem metodą prób i błędów, że poniższe działania:
Kluczem do tego było słowo „Element”. Zauważ, że nigdzie nie zdefiniowałem tego typu, wydaje się, że automatycznie istnieje w kontekście rozszerzenia tablicy i odnoszę się do dowolnego typu elementów tablicy.
Nie jestem w 100% pewien, co się tam dzieje, ale myślę, że prawdopodobnie dlatego, że „Element” jest powiązanym typem tablicy (patrz „Powiązane typy” tutaj https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )
Jednak nie widzę żadnego odniesienia do tego w odwołaniu do struktury tablicy ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) ... więc wciąż jestem trochę niepewny.
źródło
Array
jest typem ogólnym:Array<Element>
(patrz swiftdoc.org/v2.1/type/Array ),Element
jest symbolem zastępczym dla zawartego typu. Na przykład:var myArray = [Foo]()
oznacza, żemyArray
będzie zawierał tylko typFoo
.Foo
w tym przypadku jest „mapowany” na ogólny symbol zastępczyElement
. Jeśli chcesz zmienić ogólne zachowanie Array (poprzez rozszerzenie), użyjesz ogólnego symbolu zastępczego,Element
a nie żadnego konkretnego typu (takiego jak Foo).Używając Swift 2.2 : napotkałem podobny problem podczas próby usunięcia duplikatów z tablicy ciągów. Byłem w stanie dodać rozszerzenie do klasy Array, które robi dokładnie to, czego chciałem.
Dodanie tych dwóch metod do klasy Array pozwala mi wywołać jedną z dwóch metod w tablicy i pomyślnie usunąć duplikaty. Zauważ, że elementy w tablicy muszą być zgodne z protokołem Hashable. Teraz mogę to zrobić:
źródło
let deDuped = Set(dupes)
metody nieniszczącej o nazwietoSet
tak długo, jak długo możesz zmienić typJeśli chcesz dowiedzieć się o rozszerzaniu tablic i innych typach klas wbudowanych kod kasy w tym repozytorium github https://github.com/ankurp/Cent
Począwszy od Xcode 6.1, składnia rozszerzania tablic jest następująca
źródło
Rzuciłem okiem na standardowe nagłówki bibliotek Swift 2, a oto prototyp funkcji filtrującej, co sprawia, że dość oczywiste jest, jak tworzyć własne.
Nie jest rozszerzeniem Array, ale CollectionType, więc ta sama metoda ma zastosowanie do innych typów kolekcji. @noescape oznacza, że przekazany blok nie opuści zakresu funkcji filtrowania, co umożliwia pewne optymalizacje. Jaźń z dużą literą S to klasa, którą poszerzamy. Self.Generator to iterator, który iteruje po obiektach w kolekcji, a Self.Generator.Element jest typem obiektów, na przykład dla tablicy [Int?] Self.Generator.Element będzie Int ?.
Podsumowując, ta metoda filtrowania może być zastosowana do dowolnego typu CollectionType, potrzebuje bloku filtra, który pobiera element kolekcji i zwraca wartość Bool i zwraca tablicę typu oryginalnego. Podsumowując, oto metoda, która uważam za przydatną: Łączy mapę i filtr, biorąc blok, który odwzorowuje element kolekcji na opcjonalną wartość, i zwraca tablicę tych opcjonalnych wartości, które nie są równe zero.
źródło
źródło
( Swift 2.x )
Można także rozszerzyć tablicę, aby była zgodna z protokołem zawierającym niebieskie znaki dla metod typu ogólnego, np. Protokół zawierający niestandardowe narzędzia funkcjonalne dla wszystkich elementów tablicy ogólnej zgodnych z pewnym ograniczeniem typu, powiedzmy protokół
MyTypes
. Zaletą tego podejścia jest to, że możesz pisać funkcje, biorąc ogólne argumenty tablicowe, z ograniczeniem, że te argumenty tablicowe muszą być zgodne z protokołem narzędzi funkcji niestandardowych, powiedzmy protokółMyFunctionalUtils
.Możesz uzyskać to zachowanie albo pośrednio, wpisując typ ograniczający elementy tablicy
MyTypes
, albo --- jak pokażę w metodzie, którą opisuję poniżej ---, całkiem starannie, wyraźnie, pozwalając nagłówkowi ogólnych funkcji tablicy bezpośrednio pokazywać tablice wejściowe jest zgodny zMyFunctionalUtils
.Zaczynamy od protokołów
MyTypes
do stosowania jako ograniczenie typu; rozszerzyć typy, które chcesz dopasować do generycznych za pomocą tego protokołu (przykład poniżej rozszerza typy podstawowe,Int
aDouble
także niestandardowy typMyCustomType
)Protokół
MyFunctionalUtils
(przechowujący plany naszych dodatkowych ogólnych funkcji funkcji tablicy), a następnie rozszerzenie Array przezMyFunctionalUtils
; wdrożenie metody (metod) drukowanej na niebiesko:Wreszcie testy i dwa przykłady pokazujące funkcję przyjmującą tablice ogólne, odpowiednio w następujących przypadkach
Pokazywanie niejawnego twierdzenia, że parametry tablicy są zgodne z protokołem „MyFunctionalUtils”, poprzez typ ograniczający elementy tablic do „MyTypes” (funkcja
bar1
).Pokazano wyraźnie , że parametry macierzy zgodne z protokołem „MyFunctionalUtils” (funkcja
bar2
).Test i przykłady są następujące:
źródło
źródło
$0 as! Double
) walczą z systemem typu Swift, a także, moim zdaniem, pokonują cel pytania OP. W ten sposób tracisz potencjał optymalizacji kompilatora dla obliczeń, które faktycznie chcesz wykonać, a także zanieczyszczasz przestrzeń nazw Array bezsensownymi funkcjami (dlaczego chcesz widzieć .calculateMedian () w tablicy UIViews, lub w tym przypadku oprócz Double?). Jest lepszy sposób.extension CollectionType where Generator.Element == Double {}