2011-08-23 12 views
43

Así que decir que tengo alguna lista comoScala: ¿un parámetro lambda puede coincidir con una tupla?

val l = List((1, "blue"), (5, "red"), (2, "green")) 

y luego quiero filtrar a uno de ellos, puedo hacer algo como

val m = l.filter(item => { 
    val (n, s) = item   // "unpack" the tuple here 
    n != 2 
} 

¿Hay alguna manera que puedo "descomprimir" la tupla como el parámetro para el lambda directamente, en lugar de tener este intermedio item variable?

algo como lo siguiente sería lo ideal, pero Eclipse me dice wrong number of parameters; expected=1

val m = l.filter((n, s) => n != 2) 

Cualquier ayuda sería apreciada - usando 2.9.0.1

Respuesta

70

Esto es lo más cercano que puede obtener:

val m = l.filter { case (n, s) => n != 2 } 

Es básicamente la sintaxis de coincidencia de patrones dentro de un PartialFunction anónimo. También están los métodos tupled en el objeto Function y los rasgos, pero son solo un envoltorio alrededor de esta expresión de coincidencia de patrón.

+7

Puede reemplazar '' S 'por _', ya que nunca se utiliza. – missingfaktor

+0

esto es genial, muchas gracias – dvmlls

+1

Incluso puede hacerlo más corto que eso. –

8

hay un montón de opciones:

for (x <- l; (n,s) = x if (n != 2)) yield x 
l.collect{ case x @ (n,s) if (n != 2) => x } 
l.filter{ case (n,s) => n != 2 } 
l.unzip.zipped.map((n,s) => n != 2).zip // Complains that zip is deprecated 
19

Hmm aunque Kipton tiene una buena respuesta. En realidad puedes hacer que esto sea más corto al hacerlo.

val l = List((1, "blue"), (5, "red"), (2, "green")) 
val m = l.filter(_._1 != 2) 
+0

¡eres el ganador! –

+4

Más corto no lo hace mejor. Esto tiene sentido para ejemplos muy triviales, pero para cosas más complejas, el ejemplo de Kipton dará como resultado un código más legible. – Chris

+0

Bajé este voto (5 años después) porque en realidad no responde mi pregunta. Mi pregunta era cómo desempaquetar una tupla, no cómo filtrar una lista. – dvmlls

-1

Me he planteado lo mismo, y hoy hemos llegado a su pregunta.

No me gustan mucho los enfoques de función parcial (cualquier cosa que tenga case) ya que implican que podría haber más puntos de entrada para el flujo lógico. Al menos para mí, tienden a difuminar la intención del código. Por otro lado, realmente quiero ir directamente a los campos de tuplas, como tú.

Aquí hay una solución que redacté hoy. Parece que funciona, pero aún no lo he probado en producción.

object unTuple { 
    def apply[A, B, X](f: (A, B) => X): (Tuple2[A, B] => X) = { 
    (t: Tuple2[A, B]) => f(t._1, t._2) 
    } 
    def apply[A, B, C, X](f: (A, B, C) => X): (Tuple3[A, B, C] => X) = { 
    (t: Tuple3[A, B, C]) => f(t._1, t._2, t._3) 
    } 
    //... 
} 

val list = List(("a",1), ("b",2)) 
val list2 = List(("a",1,true), ("b",2,false)) 

list foreach unTuple((k: String, v: Int) => 
    println(k, v) 
) 

list2 foreach unTuple((k: String, v: Int, b: Boolean) => 
    println(k, v, b) 
) 

Salida:

(a,1) 
(b,2) 
(a,1,true) 
(b,2,false) 

Tal vez esto resulta ser útil. El objeto unTuple debe dejarse de lado en algún espacio de nombre de herramienta.

Adición:

aplicado a su caso:

val m = l.filter(unTuple((n:Int,color:String) => 
    n != 2 
)) 
+1

Downvoters, explique por qué votó negativamente (buenos modales en StackOverflow). En lugar de 'unTuple', se puede usar el' Function.tupled' integrado al que se refiere @ kipton-barros. – akauppi

4
val m = l.filter((n, s) => n != 2) 

... es una coincidencia de tipos debido a que lambda define un

  • Function2[String,Int,Boolean] con dos parámetros vez de
  • Function1[(String,Int),Boolean] con un Tuple2[String,Int] como su parámetro.

Puede convertir entre ellos de esta manera:

val m = l.filter(((n, s) => n != 2).tupled) 
Cuestiones relacionadas