2009-07-05 20 views
8

No entiendo por qué los autores dijeron que el Listado de Código 9.1 de "Programación en Scala" usa el cierre. En el capítulo 9, que muestran cómo refactorizar código en más forma menos duplicada, de este código original:Pregunta sobre el cierre de Scala (de "Programación en Scala")

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    def filesEnding(query: String) = 
    for (file <- filesHere; if file.getName.endsWith(query)) 
     yield file 
    def filesContaining(query: String) = 
    for (file <- filesHere; if file.getName.contains(query)) 
     yield file 
    def filesRegex(query: String) = 
    for (file <- filesHere; if file.getName.matches(query)) 
     yield file 
} 

Para la segunda versión:

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    def filesMatching(query: String, 
    matcher: (String, String) => Boolean) = { 
     for (file <- filesHere; if matcher(file.getName, query)) 
     yield file 
    }  
    def filesEnding(query: String) = 
    filesMatching(query, _.endsWith(_)) 
    def filesContaining(query: String) = 
    filesMatching(query, _.contains(_)) 
    def filesRegex(query: String) = 
    filesMatching(query, _.matches(_)) 
} 

que dijeron que no hay uso de cierre aquí. Ahora entiendo hasta este punto. Sin embargo introdujeron el uso de cierre refactorizar incluso un poco más, se muestra en el Listado 9.1:

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    private def filesMatching(matcher: String => Boolean) = 
    for (file <- filesHere; if matcher(file.getName)) 
     yield file 
    def filesEnding(query: String) = 
    filesMatching(_.endsWith(query)) 
    def filesContaining(query: String) = 
    filesMatching(_.contains(query)) 
    def filesRegex(query: String) = 
    filesMatching(_.matches(query)) 
} 

Ahora se dice que consulta es una variable libre, pero yo realmente no entiendo por qué dijeron eso? Debido a que "" query "" parece pasar del método superior a la función de coincidencia de cadenas explícitamente.

Respuesta

17

Veamos el clásico complemento add-n de What is a closure.

(define (add a) 
    (lambda (b) 
    (+ a b))) 

(define add3 (add 3)) 

(add3 4) returns 7 

En la expresión lambda anteriormente, a es el free variable, que se define en el enlace de Wikipedia a ser:

una variable se hace referencia en una función que no es una variable local o una argumento de esa función. Un upvalue es una variable gratuita que se ha vinculado (cerrado) con un cierre.

Volviendo a

def filesEnding(query: String) = 
    filesMatching(_.endsWith(query)) 

La función implícita x => x.endsWith(query) es la función de primera clase, que se asigna al valor de primera clase matcher, y _.endsWith() se cierra sobre query, similar a la forma en 3 cierra hasta a en (add 3). (add3 4) equivalente se realiza por matcher(file.getName).

Editar: Parte complicada es la característica en Scala llamada sintaxis de marcador de posición funciones anónimas. Al usar _ en lugar del remitente o el parámetro, Scala crea automáticamente una función anónima, que podemos considerar como expresión lambda.

Por ejemplo,

_ + 1    creates  x => x + 1 
_ * _    creates  (x1, x2) => x1 * x2 
_.endsWith(query) creates  x => x.endsWith(query) 

Dentro de la función x => x.endsWith(query), query cumple los dos requisitos de ser una variable libre:

  1. query no es una variable local definida dentro de la función (no hay variable local).
  2. query no es un argumento de la función (el único argumento es x).
+1

Am he entendido bien que debido a que el método "coincidencias de" variable de captura de "consulta" por lo tanto, el uso de cierre. – Ekkmanz

+2

Sí, en este código "def filesEnding (consulta: String) = filesMatching (_ endsWith (consulta).)" Hay una lambda "_.endsWith (consulta)", que cuando desazucaradas un poco se parece a "{x => x .endsWith (query)} ". En la notación esquemática se vería como "(lambda (x) (endwith x query))". Como puede ver, en la "consulta" lambda hay una variable gratuita. No está vinculado como un argumento ni con un let en el lambda, por lo que cuando se forma el cierre, la consulta se captura del entorno que lo contiene, p. invocaciones de métodos como "filesEnding". –