Создать дискриминированное объединенное дело из строки
Я пытаюсь создать DU случаи из строк. Единственный способ увидеть это - перечислить случаи DU с помощью Microsoft.FSharp.Reflection.FSharpType.GetUnionCases
а затем выбирая UnionCase
которая соответствует строке (используя .Name
), а затем сделать фактический случай DU из этого с помощью FSharpValue.MakeUnion
,
Нет ли более легкого / более элегантного способа сделать это? В моем сценарии у меня есть DU с парой сотен случаев для ключевых слов. Я должен прочитать строки (ключевые слова) из файла и сделать из них типы. Я сделал некоторую "оптимизацию", поместив эти случаи в карту, но я надеялся, что будет лучший способ сделать это.
У меня есть следующее, например:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let mkKeywords (file: string) =
use sr = new StreamReader(file)
let caseMap =
FSharpType.GetUnionCases(typeof<Keyword>)
|> Array.map (fun c -> (c.Name, FSharpValue.MakeUnion(c, [||]) :?> Keyword))
|> Map.ofArray
[
while not sr.EndOfStream do
let l = sr.ReadLine().Trim()
match caseMap.TryFind l with
| Some c -> yield c
| None -> failwith <| "Could not find keyword: " + l
]
3 ответа
Я нашел этот удобный фрагмент кода...
open Microsoft.FSharp.Reflection
let toString (x:'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
let fromString<'a> (s:string) =
match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
|[|case|] -> Some(FSharpValue.MakeUnion(case,[||]) :?> 'a)
|_ -> None
... что позволяет легко привязать две строки кода к любому DU...
type A = X|Y|Z with
override this.ToString() = toString this
static member fromString s = fromString<A> s
Я хотел бы использовать сопоставление с образцом, как это:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let matchKeyword (word:string) : Keyword option =
match word with
| "FOO" -> Some FOO
| "BAR" -> Some BAR
| "BAZ" -> Some BAZ
| "BLAH" -> Some BLAH
| _ -> None
И, возможно, автоматически сгенерировать оператор сопоставления в первый раз, используя регулярное выражение в моем редакторе, но только потому, что у вас есть сотни случаев. Но я не уверен, что это лучшее решение, чем ваше.
Поскольку регистры не имеют значения, другой вариант - использовать перечисления:
type Keyword =
| FOO = 0
| BAR = 1
| BAZ = 2
| BLAH = 3
let strings = ["FOO";"BAR"]
let keywords =
[for s in strings -> s, Keyword.Parse(typeof<Keyword>, s)]
|> Map.ofList
Тогда вы можете просто использовать Enum.Parse.