2012-01-04 20 views
6

en JavaScript, podemos hacer algo como:equivalente al operador javascript || en Scala

value = f1() || f2() || f3(); 

Para ello, será f1, y asignarlo a valorar si el resultado no es nulo. solo si el resultado es nulo, llamará a f2 y lo asignará al valor si eso no es nulo. ...

Una forma de lograr esto en Scala se da aquí: How to make this first-not-null-result function more elegant/concise? crear una función getFirstNNWithOption que llama a cada función hasta que no nulo:

value = getFirstNNWithOption(List(f1 _, f2 _, f3 _)) 

Sin embargo, esto no es tan bonito como el Javascript || operador, que es más flexible. por ejemplo:

value = f1() || f2(3) || f3(44, 'ddd') || value4; 

¿hay alguna manera de lograr esto en scala?

Respuesta

9

Estoy usando el recomendado Option en lugar de null.
Por ejemplo:

class OptionPimp[T](o: Option[T]) { 
    def ||(opt: => Option[T]) = o orElse opt 
    def ||(t: => T) = o getOrElse t 
} 
implicit def optionPimp[T](o: Option[T]) = new OptionPimp(o) 

def f1(): Option[Int] = None 
def f2(): Option[Int] = None 
def f3(): Option[Int] = Some(3) 

val value1 = f1() || f2() || f3() || 4 // yields 3 
val value2 = f1() || f2()   || 4 // yields 4 
+0

favor, lea esto, donde el encadenamiento está bien explicado. http://daily-scala.blogspot.com/2010/02/chaining-options-with-orelse.html –

+0

@dave ¡buen punto, gracias! –

+0

Puede valer la pena incluir una línea en esto, de lo contrario, todos los parámetros de llamada por nombre ... –

11

Debido a que su está utilizando la función de regresar Option.

La mejor solución sería utilizar la opción de encadenamiento como se explica aquí Option's chaining

Así que haría

f1().orElse(f2()).orElse(f3()).orElse(Some(4)) 
+0

Es la misma solución que @Peter Schmitz en realidad. Sin el patrón chimp my lib, eso ayuda a evitar el repetitivo Some() para literal y tiene el aspecto que deseó. Así que voy a agregar como comentario un enlace a lo que sugerí. Esa sigue siendo una lectura obligada –

4

estoy más o menos de la misma opinión que Andy, excepto que había utilizar notación de operadores, y utilizar el método adecuado para defecto:

value = f1 orElse f2(3) orElse f3(44, 'ddd') getOrElse value4 
5

Si está usando esto con las funciones que pueden devolver nULL, también puede d efine un operador nulo-coalescente:

class NullOption[A <: AnyRef](a: A) { 
    def |?|[B >: A](b: => B) = if (a ne null) a else b 
} 
implicit def null_handling[A <: AnyRef](a: A) = new NullOption(a) 

que funciona igual que Javascript. (He usado |?| para evitar confundirlo con boolean logical o, pero puedes usar || si prefieres). Esto funciona incluso mejor - casi como Option - si agregas un mapa condicional (aquí se llama ? --pick tu palabra favorita o símbolo):

class NullOption[A <: AnyRef](a: A) { 
    def |?|[B >: A](b: => B) = if (a ne null) a else b 
    def ?[C >: Null](f: A => C): C = if (a ne null) f(a) else null 
} 
implicit def null_handling[A <: AnyRef](a: A) = new NullOption(a) 

a continuación, puede

scala> val s: String = null 
s: String = null 

scala> val t: String = "foo" 
t: String = foo 

scala> s?(_.toUpperCase) |?| t |?| { println("I am not executed"); "bar" } 
res4: java.lang.String = "foo" 

Por supuesto, el sistema de tipo no va a ayudarle a recordar que usted necesita para manejar no-es-no-datos de los casos, pero puede hacer que la manipulación nula sea ligeramente más placentera (al menos mientras no haya primitivos, con primitivas, retornando n ull-or-primitive realmente no tiene sentido).

1

Con la nueva scala 2.10, puede utilizar clases implícitas para actualizar la respuesta de @PeterSchmitz, por lo que no necesita definir una función de conversión implícita.

implicit class OptionPimp[T](o: Option[T]) { 
    def ||(opt: => Option[T]) = o orElse opt 
    def ||(t: => T) = o getOrElse t 
} 

Lo mismo para la respuesta Rex Kerr:

implicit class NullOption[A <: AnyRef](a: A) { 
    def |?|[B >: A](b: => B) = if (a ne null) a else b 
    def ?[C >: Null](f: A => C): C = if (a ne null) f(a) else null 
}