2015-04-30 11 views
5

Ho il seguente codice Scala:Scala Function.tupled vs f.tupled

def f(x: Int, y: Int): Option[String] = x*y match { 
    case 0 => None 
    case n => Some(n.toString) 
} 

val data = List((0, 1), (1, 0), (2, 3), (4, -1)) 

data flatMap {case (x, y) => f(x, y)} 

Ma, l'ultima riga è troppo prolisso così ho provato tutti questi e nessuno di loro compila.

data flatMap f

data flatMap f.tupled

data flatMap Function.tupled(f)

data flatMap {f _}

data flatMap (f _).tupled

data flatMap f(_)

Cosa sto sbagliando? L'unica cosa che funziona è questo:

(data map Function.tupled(f)).flatten

ho pensato un map seguito da flatten può sempre essere sostituito da flatMap, ma anche se la linea di cui sopra compila, questo non lo fa:

data flatMap Function.tupled(f)

risposta

2

È possibile utilizzare solo flatMap quando si restituisce Options perché è presente una conversione implicita da Option a Iterable dall'implicito option2Iterable . Il metodo flatMap su List[(Int, Int)] prevede una funzione da (Int, Int) a GenTraversableOnce[Int]. Il compilatore sta riscontrando problemi nell'individuare quella conversione implicita come opzione valida qui. Si può aiutare il compilatore lungo specificando esplicitamente i parametri generici:

import Function._ 
data.flatMap[String, Iterable[String]](tupled(f)) 
//Or 
data flatMap tupled[Int, Int, Iterable[String]](f) 

Altre formulazioni della stessa idea potrebbe anche permettere al compilatore di scegliere i tipi e impliciti corretti, anche senza i farmaci generici esplicite:

data flatMap (tupled(f _)(_)) 
data.flatMap (f.tupled(f _)(_)) 

Infine, si potrebbe anche voler giocare con collect insieme unlift qui, che può essere un bel modo per esprimere questa logica così:

data collect unlift((f _).tupled) 
data collect unlift(tupled(f)) 

Function.unlift accetta un metodo che restituisce un Option e lo trasforma in PartialFunction che non corrisponde a dove la funzione originale restituirebbe None. collect prende una funzione parziale e raccoglie i valori di una funzione parziale se è definita in ogni elemento.

+0

'dati raccolti unlift (tupla (f))' ha più senso qui IMO. Grazie! – pathikrit

0

per promuovere la risposta molto utile sopra, se si utilizza collect, si può quindi fare un passo ulteriore e riscrivere la funzione f come una funzione parziale:

val f: PartialFunction[(Int, Int), String] = { 
    case (x, y) if x*y != 0 => (x*y).toString 
} 

È quindi possibile elaborare i dati così:

data collect f 

In generale, qualsiasi funzione che restituisce un'opzione può essere riscritta come funzione parziale.In alcuni casi questo risulta più nitido in quanto si hanno meno espressioni case e non è necessario includere i valori restituiti in Some().