2015-05-14 10 views
5

Esiste un modo elegante (si pensi one-liner) per ottenere i primi n elementi di un SequenceType in Swift?Modo elegante per ottenere i primi n elementi di un SequenceType

Potrei ovviamente scrivere un ciclo for che termina dopo n elementi ma che è un po 'ingombrante.

Si noti inoltre che la soluzione deve essere in grado di gestire sequenze infinite.

+0

Hai provato 'foo [0 .. wltrup

+0

Non è possibile iscriversi 'SequenceType'. –

+0

@RobNapier Ah, ok, bene, tanto per il mio suggerimento. :) – wltrup

risposta

2

In Swift 2, è possibile creare questa come estensione:

extension SequenceType { 
    func take(n: Int) -> [Generator.Element] { 
     var result: [Generator.Element] = [] 
     var g = self.generate() 
     for _ in 1...n { 
      if let next = g.next() { 
       result.append(next) 
      } else { 
       break 
      } 
     } 
     return result 
    } 
} 

In Swift 1, avrebbe dovuto scritto come una funzione:

func take<Seq: SequenceType>(n: Int, xs: Seq) -> [Seq.Generator.Element] { 
    var result: [Seq.Generator.Element] = [] 
    var g = xs.generate() 
    for _ in 1...n { 
     if let next = g.next() { 
      result.append(next) 
     } else { 
      break 
     } 
    } 
    return result 
} 

Si noti che in entrambi i casi, SequenceType non specifica cosa succede se chiami generate() più di una volta. Potrebbe restituire gli stessi valori (come in un Array). Potrebbe restituire valori diversi (come in un flusso di dati audio). Potrebbe non restituire nulla. Quindi il chiamante di take() potrebbe aver bisogno di alcune conoscenze speciali sul suo impatto sulla sequenza.

+0

Lo implementerei come estensione di classe per 'SequenceType'. Ciò darà l'uso "one liner" desiderato. –

+0

SequenceType è un protocollo. Non è possibile applicare estensioni ai protocolli. –

+0

Non mi rendevo conto che ... hmmm. Suppongo che potresti creare un nuovo protocollo con l'ereditarietà del protocollo ma che potrebbe vanificare il tuo caso d'uso previsto. Immagino che una funzione di utilità globale sarebbe la strada da percorrere. –

0

SequenceType supporta solo generate(). Forse un approccio più "rapido" potrebbe essere quello di definire un Generator che ha un indice di inizio e di fine e un generatore di "base" salterà sui primi elementi, inizierà a restituire alcuni, e quindi si fermerà dopo l'indice finale. In questo modo:

struct SubscriptGenerator<Base: GeneratorType> : GeneratorType { 
    var nowIndex : Int = 0 
    let endIndex : Int 
    let begIndex : Int 
    var generator : Base 

    init (generator: Base, startIndex: Int, endIndex: Int) { 
    precondition(startIndex < endIndex, "oops") 
    self.generator = generator 
    self.endIndex = endIndex 
    self.begIndex = startIndex 
    } 

    // MARK - GeneratorType 

    typealias Element = Base.Element 

    mutating func next() -> Element? { 
    while (nowIndex < begIndex) { nowIndex++; generator.next() } 
    return nowIndex++ < endIndex ? generator.next() : nil 
    } 
} 

Questo è solo un esempio. Si potrebbe definire un convenience init() che prende uno SequenceType e produce il generatore di base. In azione:

75> var gen = [10,20,30,40,50].generate() 
76> var sg = SubscriptGenerator(generator: gen, startIndex: 1, endIndex:3) 
sg: SubscriptGenerator<IndexingGenerator<[Int]>> = { ... } 
77> sg.next() 
$R2: Int? = 20 
78> sg.next() 
$R3: Int? = 30 
79> sg.next() 
$R4: Int? = nil 

See Swift della EnumerateGenerator per un esempio.

Nota: potrebbe essere il Stride nesso di funzionalità di Swift fa quello che vuoi già.

+1

Critica minore: i documenti API per lo stato del metodo 'next()' * "Richiede: ... e nessuna chiamata precedente a' self.next() 'ha restituito' nil'. "* L'implementazione potrebbe chiamare' next () 'ripetutamente anche se ha restituito' nil' una volta. Ciò accade se la sequenza data ha meno di elementi 'endIndex'. –

+0

Sì, è per questo che ho notato "solo un esempio". Nessun vero controllo degli errori; oraIndex incrementa per sempre su più chiamate. Grazie. – GoZoner

+0

Se sto leggendo questo correttamente, non funzionerebbe per sequenze infinite (che sono state richieste). –

-1

Perché non

var seq = NominalSequence().generate() 
var a = (0..<10).map({_ in seq.next()!}) 

?

Due linee, ma funtional-ish.

+0

"Why not" -> perché 'prefix' lo fa già – Alexander

11

Non è esattamente quello che fa mySequence.prefix(numberOfElements)?

-3

È possibile rimuovere gli ultimi m elementi di cui non si ha bisogno.

array.removeRange(n..<array.count) 
Problemi correlati