O que é uma fatia no Swift?

85

O que é uma fatia em Swift e como ela difere de uma matriz?

A partir da documentação, a assinatura de tipo de subscrito (intervalo) é:

subscript(Range<Int>) -> Slice<T>

Por que não devolver outro em Array<T>vez de um Slice<T>?

Parece que posso concatenar uma fatia com uma matriz:

var list = ["hello", "world"]
var slice: Array<String> = [] + list[0..list.count]

Mas isso produz o erro:

não foi possível encontrar uma sobrecarga para 'subscrito' que aceita os argumentos fornecidos

var list = ["hello", "world"]
var slice: Array<String> = list[0..list.count]

O que é uma fatia?

hjing
fonte

Respostas:

97

A fatia aponta para a matriz. Não adianta fazer outro array quando o array já existe e o slice pode apenas descrever a parte desejada dele.

A adição causa coerção implícita, então funciona. Para fazer sua tarefa funcionar, você precisa coagir:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
mate
fonte
Isso faz sentido. Isso está descrito em algum lugar da documentação?
hjing
2
É um detalhe de implementação, na verdade. Você habilmente investigou o caso extremo que revela o funcionamento da caixa preta ...!
matt de
2
na verdade, Slice é uma cópia do array. Depois de atualizar a matriz original, a fatia não mudará. Isso ocorre devido à natureza da estrutura. E para a observação, o protocolo Sliceable não implica o uso do tipo Slice
Marcin
4
A linguagem rápida mudou e uma fatia na verdade usa três reticências e não duas developer.apple.com/library/ios/documentation/General/Reference/…
Daniel Galasko
7
Não iria destruir a correspondência se você adicionasse uma edição simples informando aos novos leitores que os operadores de alcance mudaram?
Daniel Galasko
22

Nota: Felizmente, esta resposta é inválida no Swift beta 3, uma vez que os arrays agora são tipos de valor verdadeiros.


@matt está correto, acima - os Slice<T>pontos na matriz. Isso parece contrário à maneira como o Swift lida com todos os outros tipos de dados com os quais estamos trabalhando, pois significa que o valor da fatia pode mudar mesmo se for declarado como uma constante:

var arr = ["hello", "world", "goodbye"]    // ["hello", "world", "goodbye"]
let slice = arr[0..2]                      // ["hello", "world"]
arr[0] = "bonjour"
println(slice)                             // ["bonjour", "world"]

A pior parte é que a fatia age exatamente como um array. Dado que em Swift temos uma expectativa de imutabilidade, parece perigoso que os valores subscritos da fatia possam mudar sem aviso:

println(slice[1])                          // "world"
arr[1] = "le monde"
println(slice[1])                          // "le monde"

Mas se a matriz subjacente muda muito drasticamente, eles são desengatados:

arr.removeAtIndex(0)                       // this detaches slice from arr
println(slice)                             // ["bonjour", "le monde"]
arr[0] = "hola"
println(slice)                             // ["bonjour", "le monde"]
Nate Cook
fonte
6
Na verdade, fatias funciona apenas como matrizes, como você diz. Matrizes Swift têm elementos mutáveis, mesmo se forem declarados como imutáveis . Por exemplo, tente let arr = ["hello", "world", "goodbye"]; arr[0] = "bonjour". Você verá que funciona. Estranhamente, com arrays imutáveis, é apenas o tamanho que é imutável, não o conteúdo. (Veja "Mutabilidade de coleções" na linguagem de programação Swift )
Matt Gibson
1
"Elementos mutáveis" - Isso não é mais verdade
Alex Brown
14

Resumo:

As respostas acima eram verdadeiras até o Beta 3 (e podem mudar novamente em versões futuras)

O Slice agora atua como um array, mas como @matt disse acima, é efetivamente uma cópia superficial para um array por baixo do capô, até que uma alteração seja feita. Slices (agora) veja um instantâneo dos valores originais,

Observe também que a sintaxe da fatia mudou:

[from..upToButNotIncluding] -> [from..<upToButNotIncluding]

Exemplo:

var arr = ["hello", "world", "goodbye"] // ["hello", "world", "goodbye"]
var arrCopy = arr
let slice = arr[0..<2]                  // ["hello", "world"]
arr[0] = "bonjour"
arr                                     // ["bonjour", "world", "goodbye"]
arrCopy                                 // ["hello", "world", "goodbye"]
slice                                   // ["hello", "world"]

Isso permite um processamento muito mais uniforme, pois é mais simples (IMHO) fazer o processamento de listas no estilo python - filtrando uma lista para fazer outra. de acordo com a resposta de Matt antes do Beta 3, você tinha que criar uma matriz temporária para mapear uma fatia. O novo código agora é mais simples:

class NameNumber {
    var name:String = ""
    var number:Int = 0

    init (name:String, number:Int) {
        self.name = name
        self.number = number
    }
}

var number = 1
let names = ["Alan", "Bob", "Cory", "David"]
let foo = names[0..<2].map { n in NameNumber(name:n, number:number++) }
foo     // [{name "Alan" number 1}, {name "Bob" number 2}]

(embora para ser justo, foo ainda é uma fatia)

Referência:

http://adcdownload.apple.com//Developer_Tools/xcode_6_beta_3_lpw27r/xcode_6_beta_3_release_notes__.pdf

Alterações importantes, problemas resolvidos, - linguagem rápida, parágrafo 1

"Array em Swift foi completamente redesenhado para ter uma semântica de valor total, como Dicionário e String ... m"

Chris Conover
fonte