为 `[<EntryPoint>]` 调用 `argv` 中定义的特定函数或动态命名要调用的函数
Calling a specific function defined in `argv` for `[<EntryPoint>]` or dynamically naming the function to be called
是否可以以某种方式定义名为 and/or 模块名称 的 函数 名称在 F# 中动态打开?
谷歌搜索互联网似乎表明只有当“名称”是一个类型的成员时才可能使用内省。
我问的原因是因为我对 Advent of Code 的 [<EntryPoint>]
的解决方案开始看起来很样板化,绝对不是 DRY。
我所有的“解谜”函数都在特定于 Day 的模块中定义,并具有相同的签名 String -> Unit -> int64
即它们采用文件名作为输入(为了测试,大多数(如果不是全部)谜题都有测试向量)和单元(以便它们在模式匹配时不会全部执行)
open AoC2020.Utils
open AoC2020.Day1
open AoC2020.Day2
open AoC2020.Day3
open AoC2020.Day4
open AoC2020.Day5
open AoC2020.Day6
open AoC2020.Day7
open AoC2020.Day8
open AoC2020.Day9
[<EntryPoint>]
let main argv =
let day = argv |> getProblem
match day with
| "1" -> day1 "1" ()
| "1b" -> day1part2 "1" ()
| "2" -> day2 "2" ()
| "2b" -> day2part2 "2" ()
| "3" -> day3 "3" ()
| "3b" -> day3part2 "3" ()
| "4" -> day4 "4" ()
| "4b" -> day4part2 "4" ()
| "5" -> day5 "5" ()
| "5b" -> day5part2 "5" ()
| "6" -> day6 "6" ()
| "6b" -> day6part2 "6" ()
| "7" -> day7 "7" ()
| "7b" -> day7part2 "7" ()
| "8" -> day8 "8" ()
| "8b" -> day8part2 "8" ()
| _ -> 1L
|> printfn "%d"
0
..或者我做的事情完全错了,有更好的方法来实现同样的事情吗?
更一般地说,我可能不会费心为代码的降临创建一个复杂的启动器 - 对于我做的几个谜题,我只是将每个谜题的代码保存在一个脚本文件中,然后 运行 通过选择这一切,运行在 F# Interactive 中将其整合 - 而不是使用命令行界面。
但是,如果您更喜欢命令行界面,那么您应该能够使用反射来做您想做的事。每个 F# 模块都编译为 class,您可以使用 Assembly.GetExecutingAssembly().GetTypes()
获取所有已加载 class 的列表,找到您想要的 class 并调用您想要的方法知道它有诸如 run
.
例如:
module Day1 =
let run () = printfn "day 1"
module Day2 =
let run () = printfn "day 2"
module Day2b =
let run () = printfn "day 2b"
一个(非常基本的)函数,它采用模块名称和 运行 它的 run
函数如下所示:
open System.Reflection
let run name =
let typ =
Assembly.GetExecutingAssembly().GetTypes()
|> Seq.find (fun t -> t.Name = name)
typ.GetMethod("run").Invoke(null, [||]) |> ignore
要对此进行测试:
run "Day1"
run "Day2"
run "Day2b"
是否可以以某种方式定义名为 and/or 模块名称 的 函数 名称在 F# 中动态打开?
谷歌搜索互联网似乎表明只有当“名称”是一个类型的成员时才可能使用内省。
我问的原因是因为我对 Advent of Code 的 [<EntryPoint>]
的解决方案开始看起来很样板化,绝对不是 DRY。
我所有的“解谜”函数都在特定于 Day 的模块中定义,并具有相同的签名 String -> Unit -> int64
即它们采用文件名作为输入(为了测试,大多数(如果不是全部)谜题都有测试向量)和单元(以便它们在模式匹配时不会全部执行)
open AoC2020.Utils
open AoC2020.Day1
open AoC2020.Day2
open AoC2020.Day3
open AoC2020.Day4
open AoC2020.Day5
open AoC2020.Day6
open AoC2020.Day7
open AoC2020.Day8
open AoC2020.Day9
[<EntryPoint>]
let main argv =
let day = argv |> getProblem
match day with
| "1" -> day1 "1" ()
| "1b" -> day1part2 "1" ()
| "2" -> day2 "2" ()
| "2b" -> day2part2 "2" ()
| "3" -> day3 "3" ()
| "3b" -> day3part2 "3" ()
| "4" -> day4 "4" ()
| "4b" -> day4part2 "4" ()
| "5" -> day5 "5" ()
| "5b" -> day5part2 "5" ()
| "6" -> day6 "6" ()
| "6b" -> day6part2 "6" ()
| "7" -> day7 "7" ()
| "7b" -> day7part2 "7" ()
| "8" -> day8 "8" ()
| "8b" -> day8part2 "8" ()
| _ -> 1L
|> printfn "%d"
0
..或者我做的事情完全错了,有更好的方法来实现同样的事情吗?
更一般地说,我可能不会费心为代码的降临创建一个复杂的启动器 - 对于我做的几个谜题,我只是将每个谜题的代码保存在一个脚本文件中,然后 运行 通过选择这一切,运行在 F# Interactive 中将其整合 - 而不是使用命令行界面。
但是,如果您更喜欢命令行界面,那么您应该能够使用反射来做您想做的事。每个 F# 模块都编译为 class,您可以使用 Assembly.GetExecutingAssembly().GetTypes()
获取所有已加载 class 的列表,找到您想要的 class 并调用您想要的方法知道它有诸如 run
.
例如:
module Day1 =
let run () = printfn "day 1"
module Day2 =
let run () = printfn "day 2"
module Day2b =
let run () = printfn "day 2b"
一个(非常基本的)函数,它采用模块名称和 运行 它的 run
函数如下所示:
open System.Reflection
let run name =
let typ =
Assembly.GetExecutingAssembly().GetTypes()
|> Seq.find (fun t -> t.Name = name)
typ.GetMethod("run").Invoke(null, [||]) |> ignore
要对此进行测试:
run "Day1"
run "Day2"
run "Day2b"