2011-09-07 25 views
5

Tengo un patrón de préstamo que aplica una función n veces donde 'i' es la variable de incremento. "De vez en cuando", quiero que la función transferida tenga acceso a 'i' ... pero no deseo que todas las funciones pasadas requieran que se defina un parámetro para aceptar 'i'. Ejemplo a continuación ...modelo de préstamo de la escala, parámetro de función opcional

def withLoaner = (n:Int) => (op:(Int) => String) => { 
    val result = for(i <- 1 to n) yield op(i) 
    result.mkString("\n") 
} 

def bob = (x:Int) => "bob" // don't need access to i. is there a way use() => "bob" instead? 
def nums = (x:Int) => x.toString // needs access to i, define i as an input param 

println(withLoaner(3)(bob)) 

println(withLoaner(3)(nums)) 
+0

no relacionado con la pregunta, pero si escribe def f: (args) => expr en lugar de def f (args) = expr que una def. –

Respuesta

11
def withLoaner(n: Int) = new { 
    def apply(op: Int => String) : String = (1 to n).map(op).mkString("\n") 
    def apply(op:() => String) : String = apply{i: Int => op()} 
} 

(no estoy seguro de cómo se relaciona con el patrón de préstamo)

Editar pequeña explicación conforme a lo solicitado en el comentario.

No estoy seguro de lo que usted sabe y lo que no sabe de Scala y de lo que no entiende en ese código. lo siento si lo que acabo de hablar es obvio.

En primer lugar, un programa scala consiste en rasgos/clases (también objeto singleton) y métodos. Todo lo que se hace se hace por métodos (dejando al constructor a un lado). Las funciones (a diferencia de los métodos) son instancias de (subtipos de) los diversos caracteres FunctionN (N el número de argumentos). Cada uno de ellos tiene como método de aplicación que es la implementación real. Si se escribe

val inc = {i: Int => i + 1} 

se Desazucarado a

val inc = new Function1[Int, Int] {def apply(i: Int) = i + 1} 

(define una clase anónima que se extiende Function1, con dada aplicar el método y la creación de una instancia)

Así que escribir una función tiene bastante más peso que un método simple. Además, no puede haber sobrecarga (varios métodos con el mismo nombre, que difieren en la firma, justo lo que hice arriba), ni usar argumentos con nombre, ni valor predeterminado para los argumentos.

Por otro lado, las funciones son valores de primeras clases (se pueden pasar como argumentos, se devuelven como resultado) mientras que los métodos no lo son. Se convierten automáticamente en funciones cuando es necesario, sin embargo, puede haber algunos casos de bordes al hacer eso. Si un método está destinado únicamente a utilizarse como un valor de función, en lugar de llamarlo como método, podría ser mejor escribir una función.

Una función f, con su método apply, se llama con f(x) en lugar de f.apply(x) (que funciona también), porque la notación llamada a la función desugars Scala en un valor (valor seguido de paréntesis y 0 o más argumentos) para una llamada a método apply. f(x) es azúcar sintáctico para f.apply(x). Esto funciona sea cual sea el tipo de f, no necesita ser uno de los FunctionN.

Lo que se hace en withLoaner es devolver un objeto (de tipo anónimo, pero uno podría haber definido una clase por separado y haber devuelto una instancia de la misma). El objeto tiene dos métodos apply, uno que acepta un Int => String, el otro un () => String. Cuando lo haces withLoaner(n)(f) significa withLoaner(n).apply(f). Se selecciona el método de aplicación apropiado, si f tiene el tipo adecuado para uno de ellos, de lo contrario, compila el error.

En caso de que se pregunta withLoaner(n) no significa withLoaner.apply(n) (o que nunca se detendría, que podría también significar withLoaner.apply.apply(n)), como withLoaner es un método, no un valor.

+0

Esto se ve genial. Muy agradable. ¿Te importaría agregar una pequeña explicación a lo que está haciendo? Además, ¿estás diciendo que def f (args) = expr es una práctica más común/mejor? No estoy seguro de por qué ... – eptx

+0

Excelente didierd. ¡Gracias! – eptx

Cuestiones relacionadas