Wat zijn de prestatie-neveneffecten van het definiëren van functies binnen een recursieve functie versus buiten in F #

Als je een recursieve functie hebt die afhankelijk is van een andere functie, wat is dan de beste manier om dat te implementeren?

1) buiten de recursieve functie

let doSomething n = ...
let rec doSomethingElse x =
    match x with
    | yourDone -> ...
    | yourNotDone -> doSomethingElse (doSomething x)

2) binnen de recursieve functie

let rec doSomethingElse x =
    let doSomething n = ...
    match x with
    | yourDone -> ...
    | yourNotDone -> doSomethingElse (doSomething x)

3) inkapselen beide binnen de een derde functie

let doSomethingElse x =
    let doSomething n = ...
    let innerDoSomethingElse =
        match x with
        | yourDone -> ...
        | yourNotDone -> innerDoSomethingElse (doSomething x)

4) Iets nog beter?

6
Waarom de goede stemming? Dit lijkt een zeer goede/redelijke vraag.
toegevoegd de auteur Daniel, de bron

1 antwoord

module Test =

    let f x = 
      let add a b = a + b //inner function
      add x 1

    let f2 x =
      let add a = a + x //inner function with capture, i.e., closure
      add x

    let outerAdd a b = a + b

    let f3 x =
      outerAdd x 1

Vertaald naar:

[CompilationMapping(SourceConstructFlags.Module)]
public static class Test {

    public static int f(int x) {
        FSharpFunc> add = new [email protected]();
        return FSharpFunc.InvokeFast(add, x, 1);
    }

    public static int f2(int x) {
        FSharpFunc add = new [email protected](x);
        return add.Invoke(x);
    }

    public static int f3(int x) {
        return outerAdd(x, 1);
    }

    [CompilationArgumentCounts(new int[] { 1, 1 })]
    public static int outerAdd(int a, int b) {
        return (a + b);
    }

    [Serializable]
    internal class [email protected] : OptimizedClosures.FSharpFunc {
        internal [email protected]() { }

        public override int Invoke(int a, int b) {
            return (a + b);
        }
    }

    [Serializable]
    internal class [email protected] : FSharpFunc {
        public int x;

        internal [email protected](int x) {
            this.x = x;
        }

        public override int Invoke(int a) {
            return (a + this.x);
        }
    }
}

De enige extra kosten voor een innerlijke functie is het maken van een nieuw exemplaar van FSharpFunc - lijkt te verwaarlozen.

Tenzij je erg prestatiegevoelig bent, zou ik me beperken tot het bereik dat het meest logisch is, dat wil zeggen, de smalste reikwijdte mogelijk.

5
toegevoegd
Zie de laatste zin. Ik heb geen tijd om het te benchmarken, maar ik noemde het enige merkbare verschil.
toegevoegd de auteur Daniel, de bron
Het antwoord kan mogelijk worden afgeleid uit uw fragmenten, maar u moet het echt uitspreken.
toegevoegd de auteur Ramon Snir, de bron
Het maken van een FSharpFunc kan de prestaties van zwaar codegebruik schaden, maar in de meeste gevallen zou het er niet toe doen.
toegevoegd de auteur Ramon Snir, de bron