Qual è il modo più idiomatico per iterare la raccolta con azioni diverse per il primo elemento?

3

A volte incontriamo una situazione in cui dovremmo iterare (o mappare) su una raccolta, applicando la stessa procedura (funzione) per tutti gli elementi tranne il primo. L'esempio più semplice è trovare l'elemento massimo della raccolta, ma non è buono: l'operazione qui è idempotente. Un esempio più realistico è il dumping della tabella per porzioni: alla prima richiesta creiamo un file con l'intestazione per le colonne, quindi lo popoliamo.

Qual è l'approccio comune (o il modo più idiomatico) per l'implementazione di questo modello? Sono interessato a paradigmi di programmazione sia imperativi che funzionali.

Alcune varianti di possibili implementazioni (pseudocodice c-like):

primo (imperativo)

funcFirst(collection[0]); //duplicate code if funcFirst is something1 + func() + something2
for (int i = 1; i < collection.size(); i++){
    func(collection[i]);
}

secondo (imperativo)

for (int i = 0; i < collection.size(); i++){
    if (i == 0) //too many extra checks
        funcFirst(collection[0]);
    else 
        func(collection[i]); 
}

terzo (imperativo)

for (int i = 0; i < collection.size(); i++){
    if (i == 0) //too many extra checks
        initSomenthing(collection[0]); //double read of collection[0]
    func(collection[i]); 
}

primo (funzionale)

funcFirst(collection.first()) //duplicate code if funcFirst is something1 + func() + something2
collection.drop(1).map( func )

secondo (funzionale)

collection.iterateWithIndex.map (if index == 0 funcFirst else func) 

terzo (funzionale)

collection.fold (init(collection.first), func) //double read of collection[0]
    
posta ov7a 05.07.2016 - 14:30
fonte

2 risposte

3

Vorrei provare qualcosa di simile in Scala per mappare in modo diverso il primo elemento:

def apply[T,X](f: T=>X, g: T=>X, l: Seq[T]): Seq[X] = l match {
   case Nil => Nil
   case head :: tail => f(head) +: apply(g,g,tail)
}

> apply((x: Int)=> x*x, (x: Int)=> -x, List(5,4,3,2,1))
res1: Seq[Int] = List(25, -4, -3, -2, -1)

L'idea è di non fare nulla se la lista è vuota. Altrimenti, si sostituisce la testa della lista con una funzione di questa testa: la testa diventa f (testa), e il resto della lista viene mappato usando un'altra funzione g, qualunque sia l'elemento sulla testa della coda del lista.

Come notava @jk, la chiamata ricorsiva da applicare potrebbe essere sostituita da una mappa. Tuttavia, il mio approccio è un po 'più flessibile (non era necessario per rispondere alla domanda originale), dal momento che si possono alternare le funzioni da applicare (f per elementi dispari, g per elementi pari, ad esempio), oppure si può decidere quale la funzione deve essere applicata sull'elemento successivo (se presente) in base a un predicato arbitrario sull'elemento corrente.

    
risposta data 05.07.2016 - 15:22
fonte
5

Basta dividere l'elenco in un valore scalare per elaborare separatamente, quindi lavorare sul resto.

val first = list.head
val rest = list.tail
val firstResult = firstFunc(first)
val restResult = rest.map(restFunc)
    
risposta data 05.07.2016 - 14:32
fonte