software - ¿Cómo ceder un solo elemento de for loop en scala?
scala wikipedia (9)
¿Por qué no hacer exactamente lo que dibujó anteriormente, es decir, return
del bucle antes? Si está interesado en lo que Scala realmente hace bajo el capó, ejecute su código con -print
. Scala desgasta el ciclo en un foreach
y luego usa una excepción para dejar el foreach
prematuramente.
Muy parecido a esta pregunta:
Código funcional para bucles con salida anticipada.
Di que el código es
def findFirst[T](objects: List[T]):T = {
for (obj <- objects) {
if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
}
None
}
¿Cómo producir un elemento único de un bucle for como este en scala?
No quiero usar find, como se propuso en la pregunta original, tengo curiosidad acerca de si y cómo podría implementarse usando el bucle for.
* ACTUALIZACIÓN *
Primero, gracias por todos los comentarios, pero supongo que no estaba claro en la pregunta. Estoy disparando por algo como esto:
val seven = for {
x <- 1 to 10
if x == 7
} return x
Y eso no se compila. Los dos errores son: - retorno fuera de la definición del método - método main tiene una declaración de retorno; necesita tipo de resultado
Sé que encontrar () sería mejor en este caso, solo estoy aprendiendo y explorando el idioma. Y en un caso más complejo con varios iteradores, creo que encontrar con for puede ser útil.
Gracias a los comentaristas, comenzaré una recompensa para compensar el mal planteamiento de la pregunta :)
Entonces, lo que está tratando de hacer es romper un ciclo después de que su condición esté satisfecha. La respuesta aquí puede ser lo que estás buscando. ¿Cómo puedo salir de un bucle en Scala? .
En general, para la comprensión en Scala se traduce en mapa, plano y operaciones de filtro. Por lo tanto, no será posible romper estas funciones a menos que lance una excepción.
Espero poder ayudarte.
Creo que ... no ''retorno'' implícita.
object TakeWhileLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else
seq(seq.takeWhile(_ == null).size)
}
object OptionLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T], index: Int = 0): T = if (seq.isEmpty) null.asInstanceOf[T] else
Option(seq(index)) getOrElse func(seq, index + 1)
}
object WhileLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else {
var i = 0
def obj = seq(i)
while (obj == null)
i += 1
obj
}
}
Este es un hack horrible. Pero te daría el resultado que deseabas.
De forma idiomática, usaría un Stream o View y solo computaría las partes que necesita.
def findFirst[T](objects: List[T]): T = {
def expensiveFunc(o : T) = // unclear what should be returned here
case class MissusedException(val data: T) extends Exception
try {
(for (obj <- objects) {
if (expensiveFunc(obj) != null) throw new MissusedException(obj)
})
objects.head // T must be returned from loop, dummy
} catch {
case MissusedException(obj) => obj
}
}
Por que no algo como
object Main {
def main(args: Array[String]): Unit = {
val seven = (for (
x <- 1 to 10
if x == 7
) yield x).headOption
}
}
La variable seven
será una Opción que contiene Some(value)
si el valor cumple la condición
Puede convertir su lista en una secuencia, de modo que los filtros que contiene el bucle for solo se evalúen bajo demanda. Sin embargo, el rendimiento de la secuencia siempre devolverá una secuencia, y lo que desea es una opción, supongo, por lo que, como paso final, puede comprobar si la secuencia resultante tiene al menos un elemento y devolver su encabezado como opción. La función headOption hace exactamente eso.
def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
(for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption
Si desea utilizar un bucle for
, que utiliza una sintaxis más agradable que las invocaciones encadenadas de .find
, .filter
, etc., hay un truco .filter
. En lugar de iterar sobre colecciones estrictas como la lista, iterar sobre las perezosas como iteradores o flujos. Si está comenzando con una colección estricta, haga que sea perezoso con, por ejemplo, .toIterator
.
Veamos un ejemplo.
Primero definamos un int "ruidoso", que nos mostrará cuándo se invoca
def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }
Ahora vamos a llenar una lista con algunos de estos:
val l = List(1, 2, 3, 4).map(noisyInt)
Queremos buscar el primer elemento que sea parejo.
val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v
Los resultados de la línea anterior en:
Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)
... lo que significa que todos los elementos fueron accedidos. Eso tiene sentido, dado que la lista resultante contiene todos los números pares. Vamos a iterar sobre un iterador esta vez:
val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)
Esto resulta en:
Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator
Observe que el bucle se ejecutó solo hasta el punto en el que pudo determinar si el resultado fue un iterador vacío o no vacío.
Para obtener el primer resultado, ahora puede simplemente llamar r2.next
.
Si desea un resultado de un tipo de Option
, use:
if(r2.hasNext) Some(r2.next) else None
Edita Tu segundo ejemplo en esta codificación es solo:
val seven = (for {
x <- (1 to 10).toIterator
if x == 7
} yield x).next
... por supuesto, debe asegurarse de que siempre haya al menos una solución si va a utilizar .next
. Alternativamente, use headOption
, definido para todos los Traversable
s, para obtener una Option[Int]
.
Si se está preguntando, así es como se implementa la LineerSeqOptimized.scala en LineerSeqOptimized.scala ; cual lista hereda
override /*IterableLike*/
def find(p: A => Boolean): Option[A] = {
var these = this
while (!these.isEmpty) {
if (p(these.head)) return Some(these.head)
these = these.tail
}
None
}
objects iterator filter { obj => (expensiveFunc(obj) != null } next
El truco es obtener una vista evaluada perezosa en la colección, ya sea un iterador o una secuencia, u objects.view. El filtro solo se ejecutará tan lejos como sea necesario.