2010-09-05 5 views
0

Je rencontre des problèmes avec la mise en œuvre de la différenciation automatique en F #. Je pense que le problème est que l'évaluation n'est pas «paresseuse».F # recursion à l'intérieur de la définition de type

Voici mon code:

type Diff = 
    {d : double; df : Diff} 
    static member (+) (x : Diff, y : Diff) = 
     {d = x.d + y.d; df = x.df + y.df} 
    static member (-) (x : Diff, y : Diff) = 
     {d = x.d - y.d; df = x.df - y.df} 
    static member (*) (x : Diff, a : double) = 
     {d = x.d * a; df = x.df * a} 
    static member (*) (x : Diff, y : Diff) = 
     {d = x.d * y.d; df = (x.df * y) + (y.df * x)} 

let rec dZero = {d = 0.0; df = dZero} 

let dConst x = {d = x; df = dZero} 

let dId x = {d = x; df = dConst 1.0} 

let test = dId 5.0 

let add (x:Diff) = (x+x).d 

Si j'essaie d'utiliser « ajouter test » Je reçois une erreur de débordement de la pile, ce qui je pense est en baisse à la définition de (+) à l'intérieur mon type se reposant sur '+'.

Y at-il un moyen de résoudre ce problème? Toute aide serait grandement appréciée.

Un grand merci, Ash

Répondre

5

Comme vous pensiez, le problème est que le F # ne pas utiliser l'évaluation paresseuse et que la structure de données que vous créez est « infini » (parce que dZero références récursive lui-même). Lors du calcul du +, l'opérateur appelle + sur les valeurs df et appelle à son tour + sur les df.df valeurs et ainsi de suite ...

Une façon de corriger cela est de faire le membre df du dossier explicitement paresseux:

type Diff = 
    {d : double; df : Lazy<Diff>} 
    static member (+) (x : Diff, y : Diff) = 
     {d = x.d + y.d; df = lazy (x.df.Value + y.df.Value) } 
    static member (-) (x : Diff, y : Diff) = 
     {d = x.d - y.d; df = lazy (x.df.Value - y.df.Value) } 
    static member (*) (x : Diff, a : double) = 
     {d = x.d * a; df = lazy (x.df.Value * a) } 
    static member (*) (x : Diff, y : Diff) = 
     {d = x.d * y.d; df = lazy ((x.df.Value * y) + (y.df.Value * x)) } 

let rec dZero = {d = 0.0; df = lazy dZero} 
let dConst x = {d = x; df = lazy dZero} 
let dId x = {d = x; df = lazy dConst 1.0} 

Cette évaluera la valeur df que lorsqu'il est effectivement utilisé, l'opération + calcule la valeur de d et ne fournissent qu'une valeur paresseuse pour df (qui peut être évalué si quelqu'un a besoin d'elle). Une autre alternative serait de faire du type Diff une union discriminée et de représenter zéro comme une valeur spéciale (plutôt que comme un enregistrement récursif), ce qui fonctionnerait à moins que vous n'utilisiez des références récursives pour quelque chose d'autre. La déclaration serait à peu près quelque chose comme:

type Diff = 
    | DiffValue of double * Diff 
    | DiffZero 
    static member (+) // etc... 

Cela rendrait la mise en œuvre un peu plus longtemps, parce que vous devrez vérifier la Zero cas dans toutes les opérations primitives. Dans ce cas, vous ne créeriez que des structures de données finies (et les opérateurs les traiteraient avec empressement).