2010-08-24 11 views
16

Dies ist eine ziemlich einfache Frage, und ich wollte nur überprüfen, dass das, was ich mache und wie ich die F # interpretiere, sinnvoll ist. Wenn ich die AussageF # Funktionen vs. Werte

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

haben Statt printRandom als Funktion der Erstellung läuft F # einmal und dann weist sie einen Wert. So, jetzt, wenn ich printRandom anrufe, bekomme ich, anstatt einen neuen zufälligen Wert zu bekommen und es zu drucken, einfach, was beim ersten Mal zurückgegeben wurde. Ich kann dieses Problem umgehen, meine definiert es als solches:

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

Ist dies der richtige Weg, diese Unterscheidung zwischen parameterlose Funktionen und Werten zu ziehen? Das erscheint mir nicht ideal. Hat es Konsequenzen in Curry, Komposition usw.?

+2

Beachten Sie, dass Sie fast sicher ein 'let' vor dem ersten Vorkommen von' x' hinzufügen möchten - andernfalls führen Sie einen Vergleich durch und werfen dann das Ergebnis weg. – kvb

+0

Awesome Frage, hatte das genaue * gleiche Problem. Leider habe ich das gefunden, nachdem ich die Lösung entdeckt habe. – ses011

Antwort

18

Der richtige Weg, dies zu betrachten, ist, dass F # keine parameterlosen Funktionen hat. Alle Funktionen müssen einen Parameter haben, aber manchmal ist es egal, was es ist, also verwenden Sie () (der Singleton-Wert des Typs Einheit). Sie können auch eine Funktion wie diese machen:

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

oder dies:

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

Aber () ist die Standardmethode zum Ausdruck bringen, dass Sie nicht den Parameter verwenden. Es drückt diese Tatsache dem Aufrufer aus, weil der Typ unit -> int nicht 'a -> int ist; sowie für den Leser, weil die Anrufseite printRandom() nicht printRandom "unused" ist.

Curry und Zusammensetzung beruhen in der Tat auf der Tatsache, dass alle Funktionen einen Parameter nehmen und einen Wert zurückgeben.

Die gebräuchlichste Art, Anrufe mit Einheit zu schreiben, ist übrigens mit einem Leerzeichen, besonders in den Nicht-.NET-Verwandten von F # wie Caml, SML und Haskell. Das liegt daran, dass () ein Singleton-Wert ist, keine syntaktische Sache wie in C#.

8

Ihre Analyse ist korrekt.

Die erste Instanz definiert einen Wert und keine Funktion. Ich gebe zu, das hat mich ein paar Mal erwischt, als ich mit F # angefangen habe. Aus C# kommend erscheint es sehr natürlich, dass ein Zuweisungsausdruck, der mehrere Anweisungen enthält, ein Lambda sein muss und daher verzögert ausgewertet wird.

Dies ist in F # nicht der Fall. Anweisungen können fast beliebig verschachtelt werden (und es rockt, weil sie lokal begrenzte Funktionen und Werte haben). Sobald Sie damit vertraut sind, sehen Sie es als einen Vorteil, da Sie Funktionen und Fortsetzungen erstellen können, die für den Rest der Funktion nicht zugänglich sind.

Der zweite Ansatz ist die Standardmethode zum Erstellen einer Funktion, die logisch keine Argumente annimmt. Ich kenne die genaue Terminologie nicht, die das F # -Team für diese Deklaration verwenden würde (vielleicht eine Funktion, die ein einzelnes Argument des Typs unit verwendet). Ich kann also nicht wirklich sagen, wie das Currying beeinflussen würde.

+4

Currying trifft hier nicht wirklich zu, da die Funktion nur einen Parameter vom Typ Einheit hat. Eine teilweise Anwendung macht keinen Sinn: Sie wenden entweder die Funktion vollständig an (übergeben '()'), oder Sie nennen sie überhaupt nicht. –

7

Ist dies der richtige Weg, um diese Unterscheidung zwischen Parametern weniger Funktionen und Werten zu ziehen? Das scheint weniger für mich ideal als . Hat es Folgen in Curry, Zusammensetzung, etc?

Ja, was Sie beschreiben, ist korrekt.

Für was es wert ist, hat es eine sehr interessante Konsequenz in der Lage, Funktionen bei der Deklaration teilweise zu bewerten. Vergleichen Sie diese beiden Funktionen:

// 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) 

Beide Funktionen identische Ergebnisse erzeugen, schafft contains seine Menschen auf Erklärung festgelegt und verwendet es, während contains2 die Menschen jedes Mal eingestellt schafft Sie die Funktion aufrufen. Endergebnis: contains ist etwas schneller. Wenn Sie die Unterscheidung hier kennen, können Sie schneller Code schreiben.

3

Zuweisungskörper, die wie Funktionskörper aussehen, haben einige Programmierer nicht bemerkt. Sie können Dinge noch interessanter durch die Zuordnung einer Funktion zurückkehren zu müssen:

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

Ich schrieb einen Blog-Post über sie bei http://blog.wezeku.com/2010/08/23/values-functions-and-a-bit-of-both/.