Como posso estender o Swift Array<T>
ou T[]
digitar com utilitários funcionais personalizados?
Navegar nos documentos da API do Swift mostra que os métodos Array são uma extensão do T[]
, por exemplo:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
Ao copiar e colar a mesma fonte e tentar variações como:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
Falha ao construir com o erro:
O tipo nominal
T[]
não pode ser estendido
O uso da definição de tipo completo falha com Use of undefined type 'T'
, ou seja:
extension Array<T> {
func foo(){}
}
E também falha com Array<T : Any>
e Array<String>
.
Curiosamente Swift me permite estender uma matriz não tipada com:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Com o qual eu posso ligar:
[1,2,3].each(println)
Mas não consigo criar uma extensão de tipo genérica adequada, pois o tipo parece estar perdido quando flui pelo método, por exemplo, tentando substituir o filtro interno do Swift por :
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
}
}
Mas o compilador o trata como não digitado, onde ainda permite chamar o ramal com:
["A","B","C"].find { $0 > "A" }
E quando percorrido com um depurador indica que o tipo é, Swift.String
mas é um erro de compilação tentar acessá-lo como uma String sem convertê-lo para o String
primeiro, ou seja:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Alguém sabe qual é a maneira correta de criar um método de extensão digitado que funciona como as extensões internas?
extension T[]
bit ao clicar em Command no tipo Array no XCode, mas não vendo nenhuma maneira de implementá-lo sem obter um erro.<T>
da assinatura do método.Respostas:
Para estender matrizes digitadas com classes , o abaixo funciona para mim (Swift 2.2 ). Por exemplo, classificando uma matriz digitada:
Tentar fazer isso com uma struct ou typealias dará um erro:
Atualização :
Para estender matrizes digitadas com não classes, use a seguinte abordagem:
No Swift 3, alguns tipos foram renomeados:
fonte
[Iterator.Element]
?Depois de um tempo tentando coisas diferentes, a solução parece remover o
<T>
da assinatura como:Que agora funciona conforme o esperado, sem erros de compilação:
fonte
filter
função existente :let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
é funcionalmente equivalente ao seufind
, ou seja, o resultado da função é o mesmo. Se o fechamento do filtro tiver efeitos colaterais, você poderá não gostar dos resultados, com certeza.filter
.filter
,map
e sereduce
originam, são executadas para seus valores de retorno. Por outro lado, aeach
função que você define acima é um exemplo de função executada por seu efeito colateral, porque não retorna nada. Acho que podemos concordar que a implementação atual do Swift não é ideal e a documentação não afirma nada sobre suas características de tempo de execução.Estenda todos os tipos:
Estenda alguns tipos:
Estenda um tipo específico :
fonte
Eu tive um problema semelhante - queria estender a matriz geral com um método swap (), que deveria receber um argumento do mesmo tipo que a matriz. Mas como você especifica o tipo genérico? Descobri por tentativa e erro que o abaixo funcionou:
A chave para isso era a palavra "elemento". Observe que eu não defini esse tipo em nenhum lugar, ele parece existir automaticamente dentro do contexto da extensão da matriz e se refere a qualquer tipo de elemento da matriz.
Não tenho 100% de certeza do que está acontecendo lá, mas acho que provavelmente é porque 'Element' é um tipo associado da matriz (consulte 'Tipos associados' aqui https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )
No entanto, não consigo ver nenhuma referência disso na referência da estrutura Array ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) ... então ainda estou um pouco inseguro.
fonte
Array
é um tipo genérico:Array<Element>
(consulte swiftdoc.org/v2.1/type/Array ),Element
é um espaço reservado para o tipo contido. Por exemplo:var myArray = [Foo]()
significa quemyArray
conterá apenas o tipoFoo
.Foo
nesse caso, é "mapeado" para o espaço reservado genéricoElement
. Se você deseja alterar o comportamento geral da matriz (via extensão), use o espaço reservado genéricoElement
e não qualquer tipo concreto (como Foo).Usando o Swift 2.2 : Eu tive um problema semelhante ao tentar remover duplicatas de uma matriz de seqüências de caracteres. Consegui adicionar uma extensão na classe Array que faz exatamente o que eu estava procurando fazer.
A adição desses dois métodos à classe Array permite chamar um dos dois métodos em uma matriz e remover duplicatas com êxito. Observe que os elementos na matriz devem estar em conformidade com o protocolo Hashable. Agora eu posso fazer isso:
fonte
let deDuped = Set(dupes)
, o que você poderia voltar em um método não destrutivo chamadotoSet
enquanto você está ok com a mudança tipoSe você quiser aprender sobre como estender matrizes e outros tipos de código de checkout de classes em classes neste repositório do github https://github.com/ankurp/Cent
A partir do Xcode 6.1, a sintaxe para estender matrizes é a seguinte
fonte
Dei uma olhada nos cabeçalhos da biblioteca padrão do Swift 2 e aqui está o protótipo da função de filtro, o que torna bastante óbvio como usar o seu.
Não é uma extensão para Array, mas para CollectionType, portanto, o mesmo método se aplica a outros tipos de coleção. @noescape significa que o bloco passado não deixará o escopo da função de filtro, o que permite algumas otimizações. O eu com S maiúsculo é a classe que estamos estendendo. Self.Generator é um iterador que itera através dos objetos na coleção e Self.Generator.Element é o tipo dos objetos, por exemplo, para uma matriz [Int?] Self.Generator.Element seria Int ?.
No geral, esse método de filtro pode ser aplicado a qualquer CollectionType, ele precisa de um bloco de filtro que pegue um elemento da coleção e retorne um Bool e retorne uma matriz do tipo original. Então, juntando isso, aqui está um método que eu acho útil: ele combina mapa e filtro, pegando um bloco que mapeia um elemento de coleção para um valor opcional e retorna uma matriz desses valores opcionais que não são nulos.
fonte
fonte
( Swift 2.x )
Você também pode estender a matriz para estar em conformidade com um protocolo contendo blue-rpints para métodos de tipo genérico, por exemplo, um protocolo contendo seus utilitários funcionais personalizados para todos os elementos genéricos da matriz em conformidade com alguma restrição de tipo, digamos protocolo
MyTypes
. O bônus usando essa abordagem é que você pode escrever funções usando argumentos genéricos de matriz, com uma restrição de que esses argumentos de matriz devem estar em conformidade com o protocolo de utilitários de funções personalizadas, por exemplo, protocoloMyFunctionalUtils
.Você pode obter esse comportamento implicitamente, digitando os elementos da matriz
MyTypes
como ---, como mostrarei no método descrito abaixo ---, de maneira bem explícita, deixando seu cabeçalho genérico de funções da matriz mostrar diretamente as matrizes de entrada está em conformidade comMyFunctionalUtils
.Começamos com Protocolos
MyTypes
para uso como restrição de tipo; estenda os tipos que você deseja ajustar em seus genéricos por este protocolo (o exemplo abaixo estende os tipos fundamentaisInt
eDouble
também um tipo personalizadoMyCustomType
)Protocolo
MyFunctionalUtils
(mantendo blueprints de nossos utilitários genéricos adicionais de funções de matriz) e, posteriormente, a extensão de Matriz porMyFunctionalUtils
; implementação de métodos impressos em azul:Por fim, testes e dois exemplos mostrando uma função que recebe matrizes genéricas, com os seguintes casos, respectivamente
Mostrando afirmação implícita de que os parâmetros da matriz estão de acordo com o protocolo 'MyFunctionalUtils', via tipo restringindo os elementos das matrizes a 'MyTypes' (função
bar1
).Mostrando explicitamente que os parâmetros da matriz estão em conformidade com o protocolo 'MyFunctionalUtils' (função
bar2
).O teste e os exemplos a seguir:
fonte
fonte
$0 as! Double
) estão lutando contra o sistema de tipos de Swift e também derrotam o objetivo da pergunta do OP, na minha opinião. Ao fazer isso, você perde todo o potencial de otimizações do compilador para os cálculos que realmente deseja fazer e também polui o espaço de nomes do Array com funções sem sentido (por que você deseja ver .calculateMedian () em uma matriz de UIViews ou de qualquer coisa, exceto o dobro para esse assunto?). Há um caminho melhor.extension CollectionType where Generator.Element == Double {}