Могу ли я вызвать функцию по имени в f#?
есть ли способ вызвать функцию по имени в F#? Учитывая строку, я хочу вырвать значение функции из глобального пространства имен (или, в общем случае, данного модуля) и вызвать его. Я уже знаю тип функции.
зачем мне это делать? Я пытаюсь обойти fsi, не имея опции -- eval. У меня есть файл скрипта, который определяет много функций int -> (), и я хочу выполнить одну из них. Вот так:
fsianycpu --use:script_with_many_funcs.fsx --eval "analyzeDataSet 1"
моя мысль была написать батут сценарий, например:
fsianycpu --use:script_with_many_funcs.fsx trampoline.fsx analyzeDataSet 1
для того, чтобы написать " trampoline.fsx", мне нужно будет найти функцию по имени.
3 ответов
для этого нет встроенной функции, но вы можете реализовать ее с помощью .NET reflection. Идея состоит в том, чтобы искать все типы, доступные в текущей сборке (здесь компилируется текущий код), и динамически вызывать метод с соответствующим именем. Если бы у вас было это в модуле, вам также пришлось бы проверить имя типа.
// 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]
это работает hello world при вызове:
fsi --use:C:\temp\test.fsx --exec -- "hello"
вы можете использовать отражение, чтобы получить функции как MethodInfo
по имени функции FSharp
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)
затем вы можете вызвать MethodInfo
functionsByName.[fsharpFunctionNameString].Invoke(null, objectArrayOfArguments)
но вам, вероятно, нужно сделать больше работы, чтобы разобрать строковые аргументы с помощью MethodInfo.GetParameters()
типы в качестве подсказки.
вы также можете использовать FSharp.Компилятор.Сервис сделать свой собственный fsi.exe
С флагом eval
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
если выше было скомпилировано в fsieval.exe
он может быть использован как так
fsieval.exe --load:script_with_many_funcs.fsx --eval:analyzeDataSet` 1