2011-12-16 14 views
7

Provengo da Groovy e ha un metodo .with su ogni tipo che accetta una chiusura a singolo argomento; l'argomento è l'oggetto su cui viene chiamato il metodo .with. Ciò consente una tecnica molto interessante di estendere le funzionalità di concatenamento funzionale, che ti libera dall'obbligo di introdurre variabili temporanee, influisce sul tuo codice, rende più facile la lettura e altre sottigliezze..con alternativa in scala

Voglio essere in grado di fare qualcosa del genere:

Seq(1, 2, 3, 4, 5) 
    .filter(_ % 2 == 0) 
    .with(it => if (!it.isEmpty) println(it)) 

Invece di

val yetAnotherMeaninglessNameForTemporaryVariable = 
    Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) 
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
    println(yetAnotherMeaninglessNameForTemporaryVariable) 

In altre parole il primo esempio del .with è un pò simile a .foreach ma invece di iterazione attraverso gli oggetti dell'oggetto che viene chiamato una volta sull'oggetto stesso. Quindi it equivale a Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).

Fin da quando ero molto sorpreso di non trovare niente di simile a Scala, le mie domande sono:

  • mi sto perdendo qualcosa?
  • esistono tecniche alternative native per Scala?
  • in caso contrario, ci sono motivi validi per cui questa funzione non è implementata in Scala?

Aggiornamento: Una richiesta di funzionalità appropriata è stata pubblicata sulla questione inseguitore Scala: https://issues.scala-lang.org/browse/SI-5324. Si prega di votare e promuovere

+1

Solo una nota: 'with' è una parola riservata in Scala, quindi il metodo non può essere chiamato la stessa cosa. Dovrebbe ancora esistere sotto un altro nome; questa è la domanda Scala più comune e answer_ su StackOverflow per quanto posso dire ("non esiste, crea il tuo come questo")! –

+0

Penso che il nome 'convert' si adatterebbe meglio, suggerendo quindi che questo metodo non dovrebbe avere effetti collaterali e dal momento che prende il chiamante come parametro e restituisce qualcosa di nuovo, deve essere una sorta di conversione. In questo senso questa funzione sarebbe insostituibile nella libreria standard. Inoltre, come suggerito in http://stackoverflow.com/a/8538277/485115, dovrebbe esserci anche una variante a effetto collaterale chiamata 'tap', che restituisce l'oggetto chiamante. –

risposta

12

Non esiste alcun metodo nella libreria standard, ma non è difficile definirlo.

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def tap[U](f: A => U): A = { 
    f(a) 
    a 
    } 
} 

val seq = Seq(2, 3, 11). 
      map(_ * 3).tap(x => println("After mapping: " + x)). 
      filter(_ % 2 != 0).tap(x => println("After filtering: " + x)) 

EDIT: (in risposta al commento)

Oh, ho capito male. Ciò di cui hai bisogno è nella biblioteca di Scalaz. Viene indicato con il nome |> (indicato come operatore di condotte). Con questo, il vostro esempio sarà simile illustrato di seguito:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) } 

Se non è possibile utilizzare Scalaz, è possibile definire l'operatore da soli:

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
} 

E non è una cattiva pratica di magnaccia metodo utile (s) su tipi esistenti.Dovresti usare le conversioni implicite con parsimonia, ma penso che questi due combinatori siano abbastanza comuni da giustificare i loro protettori.

+0

Sì, lo so che posso estendere la libreria standard con impliciti, ma trovo semplicemente di allontanare gli standard da una cattiva pratica. Ecco perché la mia domanda era: perché i ragazzi di Scala non lo implementano, perché probabilmente questo dovrebbe essere pubblicato sul loro tracker di problemi. Oltre alla funzione 'tap' anche se molto utile è un po 'diversa da' with' nel senso che invece del risultato della chiusura restituisce l'oggetto stesso. Sarebbe molto bello avere sia '.with' che' .tap' nella libreria standard IMO. –

+0

Denominato lo stesso metodo 'effect'. Perché "tap"? Mi piace che sia più breve, ma mi manca il beneficio mnemonico. –

+3

@NikitaVolkov Odersky ha criticato l'abitudine di alcuni programmatori di Scala di evitare dichiarazioni intermedie di 'val' a tutti i costi, a cui' '>' contribuisce certamente, quando è stato chiesto all'operatore del tubo. Quindi, per ora, non credo che diventerà parte della libreria standard. –

1

Ricordare la chiamata per nome? Forse ti dà la capablity si desidera:

object Test { 
def main(args: Array[String]) { 
    delayed(time()); 
} 

def time() = { 
    println("Getting time in nano seconds") 
    System.nanoTime 
} 

def delayed(t: => Long) = { 
    println("In delayed method") 
    println("Param: " + t) 
    t 
} 
} 

come descritto nella http://www.tutorialspoint.com/scala/functions_call_by_name.htm

3

Prova sth come questo.

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty)) 

Genera un'eccezione di asserzione se la condizione non è soddisfatta.

+0

+1, sebbene funzioni solo per esecuzioni di istruzioni singole (ad esempio println come citato qui). se volessi fare più di una operazione, diventerebbe complicato. – aishwarya

+0

Bella tecnica per altri scopi. Per questo, lanciare un'eccezione è overkill –

7

V'è una certa sintassi di questo modello incluso nella Scala:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) } 

Tuttavia, questo non è un linguaggio accettato così si dovrebbe forse astenersi da (ab) utilizzarlo.

Se non ti piace inventare carichi e carichi di nomi per variabili dummy, ricordate che è possibile utilizzare ambito Bretelle:

val importantResult = { 
    val it = Seq(1,2,3).filter(_ % 2 == 0) 
    if (!it.isEmpty) println(it) 
    it 
} 

val otherImportantResultWithASpeakingVariableName = { 
    val it = // ... 
    /* ... */ 
    it 
} 
+0

Bel suggerimento sulla partita! Anche se un po 'approssimativo, risponde alle prime 2 delle mie domande. –

1

Anche se mi piace altre soluzioni migliori (in quanto sono più locali e quindi più facile da seguire) , non dimenticare che è possibile

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x } 

evitare collisioni di nome sulle variabili senza significato e mantenerle vincolate all'ambito appropriato.

1

Questo è solo funzione di applicazione f(x) rovesciato sul suo capo: x.with(f) ... Se siete alla ricerca di un modo di fare idiomatica with a Scala, un-flip:

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)) 

Allo stesso modo, se voglio x.with(f).with(g), usa solo g(f(x)) ...

+2

': 8: errore: tipo di parametro mancante' – david