9
votes

Éviter les fuites de mémoire Scala - Constructeurs Scala

Je travaillais dans le livre "Programmation in Scala", et j'ai été frappé par un peu de problème dans la mise en œuvre de la classe rationnel au chapitre 6.

Ceci est ma version initiale de la classe rationnel (basé sur le livre) xxx

Le problème ici est que le champ g reste pour la durée de vie de la classe, même si jamais encore accessible. Ce problème peut être vu en exécutant le programme simulateur suivant: xxx

Sa sortie va être: xxx

A Solution que j'ai trouvée sur le Scala Wiki implique les éléments suivants: xxx < / Pré>

Ici, le champ G est local à son bloc, mais en exécutant la petite application de test, j'ai trouvé un autre champ x 1 $ qui conserve une copie du tuple composé de (numer, denom) ! xxx

existe-t-il un moyen de construire une rationnelle dans Scala avec l'algorithme ci-dessus, sans aucune fuite de mémoire?

merci,

flaviu cipcigan


4 commentaires

Même question: Stackoverflow.com/Questtions/1118669/...


Merci, et désolé de poser la question à nouveau :). Les réponses dans le poste lié ont clarifié ma question.


Avez-vous confirmé que denom et numer sont vraiment des valeurs? Je ne serais pas du tout surpris s'ils étaient "seulement" des méthodes d'accesseur de la forme def Denom = x $ 1._2 .


Ce n'est pas une fuite de mémoire, c'est une mémoire de mémoire.


7 Réponses :


6
votes

Vous pouvez le faire:

val numer = numerator / gcd(numerator.abs, denominator.abs)
val denom = denominator / (numerator / numer)


1 commentaires

Merci, votre deuxième solution fonctionne (bien que je n'ai pas fait de test rigoureux) et vous débarrassez de tout champ superflu avec des frais généraux négligeables.



13
votes

Un objet compagnon peut fournir la flexibilité dont vous avez besoin. Il peut définir une méthode d'usine "statique" qui remplace le constructeur.

object Rational{

    def apply(numerator: Int, denominator: Int) = {
        def gcd(a: Int, b: Int): Int = if(b == 0) a else gcd(b, a % b)
        val g = gcd(numerator, denominator)
        new Rational(numerator / g, denominator / g)
    }
}

class Rational(numerator: Int, denominator: Int) {
  require(denominator != 0)

  override def toString  = numerator + "/" + denominator
  // other methods go here, neither access g
}

val r = Rational(10,200)


3 commentaires

Merci pour la réponse, je pensais aussi à une usine, mais cela ajouterait quelques complications. Par exemple, un utilisateur pourrait appeler le constructeur de l'objet (par exemple, le nouveau rationnel (10,20)) et dans le processus de création d'une rationnelle non valide. On pourrait ajouter un besoin (GCD (numérateur, dénominateur) == 1) au constructeur ou rendre le constructeur de classe privé et obliger les utilisateurs à utiliser l'usine. Je ne suis pas sûr de ce qui serait le mieux ... une usine semble un peu excentrée pour un rationnel :)


Notez que, puisque le nom de la méthode d'usine est appliquer , il peut être appelé comme ceci: rationnel (10, 20) .


Pas une overkill - c'est la bonne réponse. Ceci est extrêmement typique pour Scala et est le modèle recommandé - CTOR privé et utilisez le compagnon applicable. :)



6
votes

Il y a un petit problème avec l'exemple de Thomas Jung; Il vous permet toujours de créer un objet rationnel avec un terme commun dans le numérateur et le dénominateur - si vous créez l'objet rationnel à l'aide de «nouveau» vous-même, au lieu de via l'objet compagnon:

class Rational private (numerator: Int, denominator: Int) {
    // ...
}


0 commentaires

13
votes

Vous pouvez le faire:

object Rational {
    def gcd(a: Int, b: Int): Int =
        if(b == 0) a else gcd(b, a % b)
}

class Rational private (n: Int, d: Int, g: Int) {
    require(d != 0)

    def this(n: Int, d: Int) = this(n, d, Rational.gcd(n.abs, d.abs))

    val numer = n / g

    val denom = d / g

    override def toString = numer + "/" + denom

}


0 commentaires

3
votes

... En fait, je ne vois pas comment cela constitue une "fuite de mémoire".

Vous déclarez un champ final dans le champ d'application de l'instance de classe et sont alors apparemment surpris que cela "se bloque". Quel comportement es-tu attendu?

Est-ce que je manque quelque chose ici?


3 commentaires

Le problème est qu'il n'y a pas de moyen propre de définir une variable temporaire utilisée uniquement lors de la construction de l'objet, comme vous le pouviez avec un constructeur Java.


Ce n'est pas officiellement une fuite de mémoire, car elle est toujours accessible de variables globales ou locales (c'est pourquoi le GC ne le nettoie pas). C'est certainement une "fuite" au sens informel que ce sont des données qui vivent même si vous n'en aurez jamais besoin.


Je pense que le choix du titre est un peu trompeur.



0
votes

Je suis tombé sur cet article que vous pourriez trouver utile: http://dailail-scala.blogspot.com/ 2010/02 / temporaire-variables-pendant-objet.html

Il semble que vous puissiez écrire ceci: xxx


0 commentaires

0
votes

pourrait être comme: xxx

au lieu de val


0 commentaires