2014-02-20 8 views
6

Gibt es eine Möglichkeit, eine Funktion über den Namen in F # aufzurufen? Bei einer gegebenen Zeichenfolge möchte ich einen Funktionswert aus dem globalen Namespace (oder im Allgemeinen einem bestimmten Modul) auswählen und aufrufen. Ich kenne den Typ der Funktion bereits.Kann ich eine Funktion über den Namen in f # aufrufen?

Warum sollte ich das tun? Ich versuche, um fsi zu arbeiten, keine --eval Option zu haben. Ich habe eine Skriptdatei, die viele int ->() Funktionen definiert, und ich möchte eine davon ausführen. Wie so:

fsianycpu --use:script_with_many_funcs.fsx --eval "analyzeDataSet 1" 

war mein Gedanke, ein Trampolin Skript zu schreiben, wie:

fsianycpu --use:script_with_many_funcs.fsx trampoline.fsx analyzeDataSet 1 

Um zu schreiben „trampoline.fsx“, würde ich brauchen die Funktion von Namen nachzuschlagen.

Antwort

6

Es gibt keine integrierte Funktion dafür, aber Sie können es mit .NET-Reflektion implementieren. Die Idee besteht darin, alle in der aktuellen Assembly verfügbaren Typen zu durchsuchen (hier wird der aktuelle Code kompiliert) und die Methode mit dem passenden Namen dynamisch aufzurufen. Wenn Sie dies in einem Modul hatten, müssten Sie auch den Typnamen überprüfen.

// Some sample functions that we might want to call 
let hello() = 
    printfn "Hello world" 

let bye() = 
    printfn "Bye" 

// Loader script that calls function by name 
open System 
open System.Reflection 

let callFunction name = 
    let asm = Assembly.GetExecutingAssembly() 
    for t in asm.GetTypes() do 
    for m in t.GetMethods() do 
     if m.IsStatic && m.Name = name then 
     m.Invoke(null, [||]) |> ignore 

// Use the first command line argument (after -- in the fsi call below) 
callFunction fsi.CommandLineArgs.[1] 

Dies läuft Hallo Welt, wenn sie aufgerufen von:

fsi --use:C:\temp\test.fsx --exec -- "hello" 
+0

Dank verwendet werden könnten, die für mich funktioniert . @ jbtules Antwort sieht komplexer, aber auch möglicherweise robuster aus. Wenn die einfache Antwort funktioniert, mag ich die einfache Antwort. :-) –

+1

Die gute einfache Antwort wird im interaktiven Szenario fallen, wenn jede partielle Kompilierung der ausführenden Assembly einen Typ und eine statische Methode desselben Namens hinzufügt. Und wie unterscheidet man zwischen gebundenen Funktionen und statischen Elementen auf Typen? – kaefer

3

Sie Reflexion können die Funktionen als MethodInfo ‚s von FSharp Funktionsnamen können

open System 
open System.Reflection 

let rec fsharpName (mi:MemberInfo) = 
    if mi.DeclaringType.IsNestedPublic then 
     sprintf "%s.%s" (fsharpName mi.DeclaringType) mi.Name 
    else 
     mi.Name 

let functionsByName = 
     Assembly.GetExecutingAssembly().GetTypes() 
       |> Seq.filter (fun t -> t.IsPublic || t.IsNestedPublic) 
       |> Seq.collect (fun t -> t.GetMethods(BindingFlags.Static ||| BindingFlags.Public)) 
       |> Seq.filter (fun m -> not m.IsSpecialName) 
       |> Seq.groupBy (fun m -> fsharpName m) 
       |> Map.ofSeq 
       |> Map.map (fun k v -> Seq.exactlyOne v) 

Sie dann das Aufrufen erhalten MethodInfo

functionsByName.[fsharpFunctionNameString].Invoke(null, objectArrayOfArguments) 

Aber wahrscheinlich müssen Sie mehr Arbeit tun, um Ihre String-Argumente mit den MethodInfo.GetParameters() Typen als Hinweis zu analysieren.

1

Sie könnten auch FSharp.Compiler.Service verwenden Sie Ihre eigenen fsi.exe mit einem eval Flagge

open System 
open Microsoft.FSharp.Compiler.Interactive.Shell 
open System.Text.RegularExpressions 

[<EntryPoint>] 
let main(argv) = 

    let argAll = Array.append [| "C:\\fsi.exe" |] argv 
    let argFix = argAll |> Array.map (fun a -> if a.StartsWith("--eval:") then "--noninteractive" else a) 
    let optFind = argv |> Seq.tryFind (fun a -> a.StartsWith "--eval:") 
    let evalData = if optFind.IsSome then 
         optFind.Value.Replace("--eval:",String.Empty) 
        else 
         String.Empty 
    let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() 
    let fsiSession = FsiEvaluationSession(fsiConfig, argFix, Console.In, Console.Out, Console.Error) 
    if String.IsNullOrWhiteSpace(evalData) then 
     fsiSession.Run() 
    else 
     fsiSession.EvalInteraction(evalData) 
    0 

Wenn die oben kompiliert wurde in fsieval.exe, um es als so

fsieval.exe --load:script_with_many_funcs.fsx --eval:analyzeDataSet` 1 
Verwandte Themen