2011-12-30 14 views
9

¿Cuáles son algunas ideas para expresar esta función en Scala "idiomático". O más precisamente, ¿hay alguna manera de eliminar los vars locales sin sacrificar la legibilidad?Idiomatic Scala solución al código imperativo

def solve(threshold: Int)(f: Int => Int): Int = { 
    var sum = 0 
    var curr = 0 
    while(sum < threshold) { 
    sum += f(curr) 
    curr += 1 
    } 
    curr 
} 

Lo único que se me ocurrió fue esto, pero es más largo y menos legible en mi opinión.

def solve2(threshold: Int)(f: Int => Int): Int = { 
    val resultIterator = Iterator.iterate (0, 0) { case (curr, sum) => 
    (curr + 1, sum + f(curr)) 
    } 
    (resultIterator find (_._2 >= threshold)).get._1 
} 
+0

Fue difícil decidir cuál de ellos para hacer _correct_ ya que todos eran buenos, así que elegí el que parecía más intuitiva a yo –

+0

@ La solución de Dan Burton me dio los trucos más nuevos para la caja de herramientas. –

Respuesta

10

El enfoque más directo es convertir el bucle while en una función recursiva de cola anidada.

def solve(threshold: Int)(f: Int => Int): Int = { 
    def solveLoop(sum: Int, curr: Int): Int = if (sum < threshold) { 
     solveLoop(sum + f(curr), curr + 1) 
    } else { 
     curr 
    } 
    solveLoop(0,0) 
} 

Esta es la forma estándar de "funcionamiento" de bucle.

+0

Después de reflexionar, esta es simplemente la versión no resumida de mi solución "hasta". :) –

13
def solve(threshold: Int)(f: Int => Int): Int = { 
    Iterator.from(0).map(f).scanLeft(0)(_ + _).indexWhere(threshold <=) 
} 

En mi opinión, la versión de bucle es mucho más clara.

7

Usted podría

def solve(threshold: Int, i: Int = 0)(f: Int => Int) = { 
    if (threshold <= 0) i else solve(threshold - f(i), i+1)(f) 
} 

pero no estoy seguro de que en realidad es más clara. Tenga en cuenta que en realidad es más caracteres que una versión compacta del bucle while:

def solve(threshold: Int)(f: Int => Int) = { 
    var s,i = 0; while (s < threshold) { s += f(i); i += 1 }; i 
} 

Loops con variables mutables no siempre son malos, "idiomática" o no. Simplemente mantenga el estado mutable contenido de forma segura dentro de la función, y todo lo que cualquier otra persona ve es una función sin estado para llamar.

Por cierto, aunque sum es un nombre de variable sensible, curr es cuestionable. ¿Qué pasa con i? Es ampliamente utilizado como una variable de índice, y de todos modos, tener una variable en absoluto es una molestia; el punto es que tome algo e incremente, sea lo que sea, un paso cada vez, y luego devuélvalo. Es este flujo de lógica, no el nombre, lo que le dice a usted (y a otros) para qué sirve.

+0

Encontré 'curr' la distracción también. – huynhjl

+0

Tiene razón sobre la nomenclatura variable. Solo puedo decir en el contexto original que el nombre tiene más sentido. –

1

Siempre me pregunto cuando la gente habla de scala 'idiomático'. Porque, en mi opinión, todos tienen su propia percepción de idiomática. Si está buscando una solución funcional, me gustaría sugerirle que eche un vistazo a la "esencia del patrón del iterador". En realidad, hay una muy buena entrada de blog en la Scala de este verificarlo aquí: http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

+0

¿Puedes mostrar con algún código cómo es 'traverse' relevante aquí? – missingfaktor

+0

Bueno, si nos fijamos en: umbral, suma y cur. ¿Qué hace con esos valores? Él los utiliza para generar una "corriente" finita de valores a la que él aplica su función. ¿Convencido?:) – AndreasScheinert

+0

Bueno, si nos fijamos en: umbral, suma y cur. ¿Qué hace con esos valores? Él los utiliza para generar una "corriente" finita de valores a los que él aplica su función. ¿Convencido? En realidad, me gusta más la implementación de scanLeft. Idiomatic es muy subjetivo, algunas personas dicen lacónico o lo que sea. Generando una secuencia y aplicando una función mientras se enhebra el estado. Depende de cómo quieras expresar eso. – AndreasScheinert

4

Así es como lo haría en Haskell:

solve threshold f = snd $ until test step (0, 0) 
    where test (sum, i) = sum >= threshold 
     step (sum, i) = (sum + f i, succ i) 

Esto marca claramente la test, la step, y la primera valores, al igual que la versión imperativa. No estoy seguro de si Scala tiene until en las librerías en alguna parte, pero es trivial para definir:

def until[A](test: A => Boolean)(f: A => A)(v: A): A = { 
    if (test(v)) { 
    v 
    } else { 
    until(test)(f)(f(v)) 
    } 
} 

def solve(threshold: Int)(f: Int => Int): Int = { 
    def test = (sum: Int, i: Int) => sum >= threshold 
    def step = (sum: Int, i: Int) => (sum + f(i), i + 1) 
    until(test.tupled)(step.tupled)((0, 0))._2 
} 
+2

+1, que parece una función muy útil. Debe agregar un Scala equivalente a su respuesta IMO. – missingfaktor

+0

@missingfaktor done :) Escribí el original anoche en mi teléfono y era demasiado flojo para probarlo en Scala. No estoy seguro si mi uso liberal de currying aquí es compatible con la optimización de llamadas de cola de Scala, pero qué puedo decir, me gusta el currículum de Haskell. –

Cuestiones relacionadas