2010-08-24 7 views
16

C'est une question assez simple, et je voulais juste vérifier que ce que je fais et comment j'interprète le F # est logique. Si je la déclarationF # Fonctions vs Valeurs

let printRandom = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

Au lieu de créer printRandom en fonction, F # fonctionne une fois, puis lui attribue une valeur. Ainsi, maintenant, quand j'appelle printRandom, au lieu d'obtenir une nouvelle valeur aléatoire et de l'imprimer, j'obtiens simplement ce qui a été retourné la première fois. Je peux contourner ce ma définir comme tel:

let printRandom() = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

Est-ce la bonne façon de faire cette distinction entre les fonctions de paramètres-moins et des valeurs? Cela semble moins que l'idéal pour moi. Cela a-t-il des conséquences sur la curry, la composition, etc.?

+2

Notez que vous voulez presque certainement ajouter un 'let' devant la première occurrence de' x' - sinon vous effectuez une comparaison, puis vous jetez le résultat. – kvb

+0

Question géniale, était d'avoir le * même * même problème. Malheureusement, j'ai trouvé cela après que j'ai découvert la solution. – ses011

Répondre

18

La meilleure façon de voir cela est que F # n'a pas de fonction sans paramètre. Toutes les fonctions doivent prendre un paramètre, mais parfois vous ne vous souciez pas de ce que c'est, donc vous utilisez () (la valeur singleton de l'unité de type). Vous pouvez également faire une fonction comme ceci:

let printRandom unused = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

ou ceci:

let printRandom _ = 
    x = MyApplication.getRandom() 
    printfn "%d" x 
    x 

Mais () est le chemin par défaut pour exprimer que vous n'utilisez pas le paramètre. Il exprime ce fait à l'appelant, car le type est unit -> int et non 'a -> int; ainsi que pour le lecteur, car le site d'appel est printRandom() et non printRandom "unused".

La planification et la composition reposent en fait sur le fait que toutes les fonctions prennent un paramètre et renvoient une valeur. Soit dit en passant, le moyen le plus commun d'écrire des appels avec unité est avec un espace, en particulier dans les parents non .NET de F # comme Caml, SML et Haskell. C'est parce que () est une valeur singleton, pas une chose syntaxique comme c'est en C#.

8

Votre analyse est correcte.

La première instance définit une valeur et non une fonction. J'avoue que cela m'a attrapé quelques fois quand j'ai commencé avec F # aussi. Venant de C#, il semble très naturel qu'une expression d'assignation qui contient plusieurs instructions soit un lambda et donc un délai d'évaluation.

Ce n'est pas le cas en F #. Les instructions peuvent être imbriquées de façon presque arbitraire (et il est préférable d'avoir des fonctions et des valeurs locales). Une fois que vous vous sentez à l'aise avec cela, vous commencez à le voir comme un avantage que vous pouvez créer des fonctions et des suites qui sont inaccessibles au reste de la fonction.

La deuxième approche est la manière standard de créer une fonction qui ne prend aucun argument de manière logique. Je ne connais pas la terminologie précise que l'équipe F # utiliserait pour cette déclaration (peut-être une fonction prenant un seul argument de type unit). Donc, je ne peux pas vraiment commenter sur comment cela affecterait currying.

+4

Currying ne s'applique pas vraiment ici car la fonction n'a qu'un seul paramètre, de type 'unit'. L'application partielle n'a pas de sens: soit vous appliquez complètement la fonction (en passant '()'), soit vous ne l'appelez pas du tout. –

7

Est-ce la bonne façon de tirer cette distinction entre les paramètres-moins fonctions et valeurs? Cela semble moins que l'idéal pour moi. A-t-il des conséquences dans la curry, composition, etc?

Oui, ce que vous décrivez est correct.

Pour ce que cela vaut, il a une conséquence très intéressante capable d'évaluer partiellement les fonctions sur déclaration. Comparez ces deux fonctions:

// val contains : string -> bool 
let contains = 
    let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] 
    fun person -> people.Contains(person) 

// val contains2 : string -> bool 
let contains2 person = 
    let people = set ["Juliet"; "Joe"; "Bob"; "Jack"] 
    people.Contains(person) 

Les deux fonctions produisent des résultats identiques, contains crée son peuple et mis sur la déclaration réutilise, alors que contains2 crée son peuple mis chaque fois que vous appelez la fonction. Résultat final: contains est légèrement plus rapide. Donc, connaître la distinction ici peut vous aider à écrire du code plus rapidement.

3

Les corps d'affectation ressemblant à des corps de fonctions n'ont pas été pris en compte par quelques programmeurs. Vous pouvez faire des choses en ayant la mission encore plus intéressante retourner une fonction:

let foo = 
    printfn "This runs at startup" 
    (fun() -> printfn "This runs every time you call foo()") 

Je viens d'écrire un billet de blog à ce sujet à http://blog.wezeku.com/2010/08/23/values-functions-and-a-bit-of-both/.

Questions connexes