2009-07-05 5 views
8

Non capisco perché gli autori dicano che il Codice 9.1 da "Programmazione in Scala" utilizza la chiusura. Nel capitolo 9, che mostrano come il refactoring del codice in più forma meno duplicato, da questo codice originale:Domanda su Scala Closure (da "Programmazione in 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 
} 

Per la seconda versione:

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(_)) 
} 

che hanno detto che non c'è nessun uso di chiusura Qui. Ora capisco fino a questo punto. Tuttavia hanno introdotto l'uso di chiusura di refactoring anche un po 'di più, mostrato nel Listato 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)) 
} 

Ora hanno detto che interrogazione è una variabile libera, ma io non capisco il motivo per cui hanno detto così? Poiché "" query "" sembra essere passato esplicitamente dal metodo top down alla funzione di corrispondenza delle stringhe.

risposta

17

Diamo un'occhiata alla classica chiusura add-n da What is a closure.

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

(define add3 (add 3)) 

(add3 4) returns 7 

Nell'espressione lambda sopra, a è il free variable, che è definito nel collegamento Wikipedia essere:

una variabile di cui una funzione che non è una variabile locale o un argomento di quella funzione. Un valore massimo è una variabile libera che è stata associata a (chiusa) con una chiusura.

Tornando

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

La funzione implicita x => x.endsWith(query) è la funzione di prima classe, che è assegnato al valore di prima classe matcher e _.endsWith() è chiuso sopra query, simile al modo 3 chiude su a in (add 3). (add3 4) equivalente è fatto da matcher(file.getName).

Modifica: La parte difficile è la funzione di Scala chiamata funzioni anonimo sintassi segnaposto. Usando _ al posto del mittente o del parametro, Scala crea automaticamente una funzione anonima, che possiamo considerare come espressione lambda.

Ad esempio,

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

All'interno della funzione x => x.endsWith(query), query soddisfa i due requisiti di essere una variabile libera:

  1. query non è una variabile locale definita all'interno della funzione (non c'è variabile locale).
  2. query non è un argomento della funzione (l'unico argomento è x).
+1

Sono a conoscenza del fatto che, poiché il metodo "matcher" acquisisce la variabile "query", quindi utilizza la chiusura. – Ekkmanz

+2

Sì, in questo codice "def filesEnding (query: String) = filesMatching (_. EndsWith (query))" c'è un lambda "_.endsWith (query)" che quando desugared assomiglia a "{x => x .endsWith (query)}". Nella notazione schematica sembrerebbe "(lambda (x) (endwith x query))". Come puoi vedere, nella "query" lambda è una variabile libera. Non è vincolato come argomento né con una let in lambda, quindi quando la chiusura è formata la query viene catturata dall'ambiente di contenimento, ad es. invocazioni di metodi come "filesEnding". –