我创建了一个嵌套的Discriminated Union(DU),如下所示:
type OptimizationPeriod = | All
| Long
| Short
type OptimizationCriterion = | SharpeRatio of OptimizationPeriod
| InformationRatio of OptimizationPeriod
| CalmarRatio of OptimizationPeriod
还有一个非嵌套的DU:
type Parallelism = Sequential | PSeq
我有一个JSON配置文件,其中包含定义DU情况的字符串。以下函数设法识别非嵌套Parallelism
DU:
let stringToDUCase<'t> (name: string) : 't =
let dUCase =
Reflection.FSharpType.GetUnionCases( typeof<'t> )
|> Seq.tryFind (fun uc -> uc.Name = name)
|> Option.map (fun uc -> Reflection.FSharpValue.MakeUnion( uc, [||] ) :?> 't)
match dUCase with
| Some x -> x
| _ -> let msg = sprintf "config.json - %s is not a case in DU %A" name typeof<'t>
failwith msg
注意:我当然从某个地方复制了它,因为这个功能有点过头了,向作者道歉,因为它不记得它来自哪里。
不幸的是,这个函数无法识别嵌套DU的情况:
stringToDUCase<OptimizationCriterion> config.Trading.Criterion
System.Exception: config.json - SharpeRatio All is not a case in DU FractalTypes.OptimizationCriterion
两个问题:
1)我能够编写一个函数,将specifically
与OptimizationCriterion
DU交易,并能够识别案例。 generic
的{{1}}函数是否可以执行相同的操作?
2)使用stringToDUCase
类型的元组而不是嵌套的DU会更好吗? (我可能需要两次调用OptimizationCriterion*OptimizationPeriod
,但这不是问题)
答案 0 :(得分:4)
像All
这样的“空”DU案例只是一个值,但像SharpeRatio
这样的“非空”DU案例实际上是一个取一个值并返回类型的函数。在这种情况下,SharpeRatio
的类型为OptimizationPeriod -> OptimizationCriterion
。
您现有的stringToDUCase
函数始终将空数组传递给MakeUnion
(表示空DU案例)。所以这里是适用于任何DU情况的函数的修改版本:
let stringToParamDUCase<'t> (name: string) =
Reflection.FSharpType.GetUnionCases(typeof<'t>)
|> Seq.tryFind (fun uc -> uc.Name = name)
|> Option.map (fun uc ->
fun (parameters:obj []) -> Reflection.FSharpValue.MakeUnion(uc, parameters) :?> 't)
|> Option.defaultWith (fun () ->
failwith (sprintf "config.json - %s is not a case in DU %A" name typeof<'t>))
请注意,它返回obj [] -> 't
的函数。我也简化了错误处理。
您可以使用它:
let myOptimizationPeriod = stringToParamDUCase<OptimizationPeriod> "All" [||]
let f = stringToParamDUCase<OptimizationCriterion> "SharpeRatio"
let myOptimizationCriterion = f [|All|]
答案 1 :(得分:3)
我认为现有的答案应该直接回答你的问题。但是,我认为值得再提两点。首先,如果您将OptimizationCriterion
表示为记录可能会更容易,因为您的所有DU案例都包含相同的值:
type OptimizationPeriod =
| All | Long | Short
type OptimizationRatio =
| SharpeRatio | InformationRatio | CalmanRatio
type OptimizationCriterion =
{ Ratio : OptimizationRatio
Period : OptimizationPeriod }
这也解决了你的问题,因为现在你只需要没有参数的DU,但我认为这也是更好的设计,因为你避免重复第二个参数。
其次,我认为你真的不需要使用花哨的自定义反射函数进行反序列化。如果你想用JSON存储你的数据,你应该使用标准库(Newtonsoft.JSON或Chiron会做得很好),或者你可以使用F#Data中的JsonValue
直接编写它,但是使用自定义反射代码是导致无法维护的代码的快速方法。