Question

Sur F # WikiBook dans la section Encapsulation de l'état mutable , le code ci-dessous est respecté. .

> let incr =
    let counter = ref 0
    fun () ->
        counter := !counter + 1
        !counter;;

val incr : (unit -> int)

> incr();;
val it : int = 1

> incr();;
val it : int = 2

> incr();;
val it : int = 3

Au début, il semblait assez facile d'avaler le fait que, la valeur du compteur mutable incrémentait à chaque fois que incr était invoqué.

Mais après y avoir réfléchi pendant un moment, je ne comprenais pas bien quand compteur était libéré du tas et aussi comment compteur faisait toujours référence à la valeur précédente avant d'être incrémenté . Comment le compteur qui vit dans la portée de la incr survit-il à travers plusieurs appels de fonction?

Les principales questions sont donc les suivantes:

  • Quand compteur est-il libéré du tas?
  • Le compteur n'est-il pas une fuite de mémoire?
Était-ce utile?

La solution

La distinction entre "portée lexicale" (lorsqu'un nom a une signification dans le texte d'un programme) et "durée de vie" (durée d'exécution entre la création et la destruction de l'objet) peut parfois être source de confusion, car ces deux éléments sont souvent très corrélés. . Cependant, la technique illustrée par cet exemple est courante dans les langages fonctionnels: vous attribuez à un détail d'implémentation une petite portée lexicale (qui masque les détails d'implémentation des appelants), mais vous allongez sa durée de vie en la capturant dans une fermeture (de sorte que sa durée de vie devienne la durée de vie). de l'objet englobant - en l'occurrence la fonction 'incr'). C’est une façon courante de réaliser l’encapsulation dans la programmation fonctionnelle (par opposition à la technique d’encapsulation habituelle de public / privé dans les classes de la programmation orientée objet).

Maintenant, dans cet exemple particulier, il semble que "incr" est une fonction de niveau supérieur, ce qui signifie que sa valeur dure toute la vie du programme (ou une session interactive si vous tapez dans fsi.exe). Vous pourriez appeler cela une "fuite", mais cela dépend de l'intention. Si vous avez un compteur d'identifiant unique dont vous avez besoin pour toute la durée de vie de votre programme, vous devrez alors stocker ce compteur varable quelque part qu'il durera pour l'ensemble du programme. Il s’agit donc d’une «fuite» ou d’une «caractéristique intrinsèque», selon l’utilisation de «incr» (vous devrez utiliser cette fonction pour le reste du programme?). Quoi qu'il en soit, le point clé ici est que "incr" contient des ressources de mémoire. Par conséquent, si vous n'en avez pas besoin pour toujours, vous devez prendre des dispositions pour que la fermeture référencée par "incr" devienne inaccessible lorsqu'elle ne sera plus nécessaire. Généralement, cela peut être fait en le rendant local à une autre fonction, par exemple

.
let MyComplicatedFuncThatNeedsALocalCounter args =
    let incr = 
        // as before
    // other code that uses incr
    // return some result that does not capture incr

Autres conseils

Dans ce cas, incr est une fonction de niveau supérieur (implémentée sous forme de champ statique si je ne me trompe pas.) Elle contient une fermeture qui à son tour contient une référence à la cellule de référence nommée compteur . Tant que cette fermeture existe, la cellule ref est conservée en mémoire.

Désormais, cette liaison de niveau supérieur ne sera jamais récupérée car il s’agit d’un champ statique en lecture seule. (en termes C #). Si, toutefois, vous avez des fermetures de ce type avec une durée de vie limitée (liées localement ou dans un objet), la cellule ref sera libérée lorsque la fermeture est effacée. / p>

compteur est libéré du tas quand incr n'est plus accessible. Ce n'est pas une fuite de mémoire à cause de la récupération de place.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top