2008-08-16 9 views
13

Quelqu'un at-il un exemple décent, de préférence pratique/utile, ils pourraient poster la démonstration du concept?F # Fermeture

Répondre

10

Les fermetures peuvent être utilisées pour un certain nombre de raisons, dont l'une est de réduire la portée des fonctions auxiliaires ou des valeurs. Ainsi, plutôt que de polluer le module/espace de noms avec des conneries aléatoires, vous pouvez les placer juste là où ils sont nécessaires.

open System 

let isStrongPassword password = 

    let isLongEnough (str : string) = str.Length > 10 
    let containsNumber str = 
     str |> Seq.tryfind (fun c -> Char.IsDigit(c)) |> Option.is_some 
    let containsUpper str = 
     str |> Seq.tryfind (fun c -> Char.IsUpper(c)) |> Option.is_some 


    if isLongEnough password && 
     containsNumber password && 
     containsUpper password then 
     true 
    else 
     false 

Edit: Voici un autre exemple qui capture les valeurs et les amène dans un champ intérieur sans être passé en tant que paramètres. Dans l'exemple, myPassword est utilisé dans toutes les fonctions internes, mais il n'a pas été transmis en tant que paramètre.

+0

Note de côté: la dernière instruction 'if-then-else' est équivalente à juste' mineIsLongerThan yourPassword && mineHasMoreNumsThan yourPassword' –

2

Le premier exemple de Chris Smith offre un aperçu utile de identifier scope in F#. Cependant, il ne tire pas parti des bound variables disponibles pour les fonctions imbriquées. Dans la variante suivante, minLength est disponible pour la fonction interne car il s'agit d'une fermeture.

open System.Text.RegularExpressions 

let isStrongPassword minLength = 
    fun (password : string) -> 
     password.Length >= minLength && 
     Regex.IsMatch(password, "\\d") && 
     Regex.IsMatch(password, "[A-Z]") 

Ceci est une utilisation triviale d'une fermeture parce que vous pouvez accomplir la même chose par currying en F #.

0

ESV - si je comprends bien (ce qui est également limité par une perspective OO) la fermeture a à voir avec la limitation de vos fonctions (comme isLongEnough) à la portée de la méthode qui les utilise. Cela ferme effectivement (d'où le terme de fermeture) ces fonctions du reste du code de votre application.

Je pense que je comprends les choses, sinon je vais me détromper, espérons aussi bien;)

3

@ Alex - qui est juste portée des fonctions. les fermetures se construisent là-dessus cependant.

@ESV -

la forme (fun c... une fermeture --Même si elle est une application directe et non passé autour. Je ne dirais pas que c'est un bon exemple. Quelque chose, plus simple et simple:

let derivative dx = 
    (fun f x -> (f (x+dx)) - (f x)/dx) 

Nous revenions un environnement (dx) et la fonction anonyme. Maintenant, vous n'avez pas nécessairement à retourner une fonction, comme l'exemple de Chris. Mais cela a toujours à voir avec l'utilisation de variables plus étendues (ou liées) - l'environnement - dans un contexte local.

let filter lst f_critera = 
    let rec loop_ = function 
     | hd :: tl -> 
      if f_critera hd then hd :: (loop_ tl) 
      else (loop_tl) 
     | [] -> [] 
    in 
    loop_ lst 

Donc, c'est une fermeture, bien que je l'ai forcé un peu, mais pour le bien de les définir c'est un cadre décent. f_criteria est lié, dans loop_ - c'est la variable d'environnement.

0

@ESV, les fermetures sont les trois let définitions qui sont en retrait (ie. isLongEnough, containsNumber et containsUpper). Ce sont des fermetures car ce sont des fonctions définies dans le cadre de la fonction isStrongPassword.

1

Dans le texte Functional Programming il y a cette définition:

Les fermetures sont des fonctions qui portent autour de certains des « environnement » dans lequel ils ont été définis. En particulier, une fermeture peut faire référence à des variables qui étaient disponibles au moment de sa définition.

Cette définition n'est probablement pas complète, mais est facile à comprendre pour quelqu'un du langage impératif/objectif.

8

Les fermetures sont utiles pour la mise en cache et la mémoisation. Par exemple:

let isWord (words: string list) = 
    let wordTable = Set.Create(words) 
    fun w -> wordTable.Contains(w) 

> let isCapital = isWord ["London";"Paris";"Warsaw";"Tokyo"];; 
val isCapital : (string -> bool) 
> isCapital "Paris";; 
val it : bool = true 
> isCapital "Manchester";; 
val it : bool = false 

Remarquez comment l'wordTable est calculé qu'une seule fois, lorsque la fermeture est créée. Plus généralement, les fermetures sont utiles pour garder un état privé. Par conséquent, vous pouvez même recréer des cellules avec des fonctions purement.

let cons a b = function 
    | true -> a 
    | false -> b 

let car p = p true 

let cdr p = p false 

> (car (cons 1 2)) 
val it : int = 1 
> (cdr (cons 1 2)) 
val it : int = 2 

Eh bien, ici les cellules contre sont immuables. Mais vous pouvez imaginer avoir un état mutable aussi. Faisons un petit comptoir:

let makeCounter() = 
    let x = ref 0 
    let tick() = 
     x := !x + 1 
     !x 
    tick 

let c1 = makeCounter() 
let c2 = makeCounter() 

> c1() 
val it : int = 1 
> c1() 
val it : int = 2 
> c2() 
val it : int = 1 

On sait que les fermetures sont les objets d'un pauvre homme, parce que les objets sont les fermetures d'un pauvre homme :) Vous pouvez simuler un avec l'autre.

4

J'ai aussi eu du mal avec ça - voir le mot «fermeture» lancé par les experts F #. Cela ressemble à "scope" (et portée imbriquée) qui sont familiers, mais il a en fait peu à voir avec cela, et n'est pertinent que dans le code qui passe autour des fonctions comme valeurs en dehors de sa portée originale.

a une bonne @ Robert citation ...

Les fermetures sont des fonctions qui portent autour de certains des « environnement » dans lequel ils ont été définis. En particulier, une fermeture peut faire référence à des variables qui étaient disponibles au moment de sa définition.

Le meilleur exemple que je l'ai vu ...

let Counter = 
    let count = ref 0 
    // *Return a function that carries the current context* (ie. "count", on 
    // the heap). Each time it is called, it will reference, and increment, 
    // the same location 
    (fun() -> incr count; !count) 

> Counter() 
val it : int = 1 
> Counter() 
val it : int = 2 <-- !! The anonymous function has retained the context 

Cela ne fonctionne que parce que le nombre est une variable "ref" (ie. Sur le tas), et parce que contre retourne une fonction (plutôt que d'un nombre entier)

Suivant est la mauvaise façon - en utilisant la pile à la place du tas ...

let Counter2 = 
    let mutable count = 0 
    // Attempt to reference mutable variable, from within a closure, is invalid 
    (fun() -> count <- count+1; count) 

Ce produit un message d'erreur très utile, qui nous en dit beaucoup sur les fermetures ..

La variable mutable 'count' est utilisée d'une manière invalide. Les variables mutables peuvent ne pas être capturées par des fermetures. Envisagez d'éliminer cette utilisation de la mutation ou d'utiliser une cellule de référence mutable allouée par tas via 'ref' et '!'

(Bravo à l'auteur de ce message!)

Syme, et al, "Expert F #", dit, sur l'utilisation des fermetures ...

C'est une technique puissante pour se cacher et d'encapsulation état mutable sans avoir recours à l'écriture de nouvelles définitions de type et classe . C'est une bonne pratique de programmation dans le code poli à s'assurer que tous les éléments connexes de l'état mutable sont collectés sous une structure de données nommée ou autre entité telle qu'une fonction.

0

La fermeture est formée lorsque des variables libres d'une fonction sont fixées. Ceci est similaire à une méthode de classe liée à une instance d'objet et faisant référence à des variables membres.

Les variables libres sont des variables qui ne sont pas transmises à la fonction en tant que paramètres, c'est-à-dire qui sont prises dans l'environnement où la fonction est définie.

Les fermetures sont très utiles pour les rappels et les gestionnaires d'événements, car ils permettent de former un objet temporaire sans en déclarer un. C'est un énorme bonus de programmation fonctionnelle sur OOP. Notez que la correction fait une chose similaire, mais c'est un concept différent: elle corrige certaines variables liées (c'est-à-dire, les paramètres de fonction). En revanche, la fermeture corrige des variables libres.