是否有一个通用函数来识别嵌套的区分联合的情况?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是否有一个通用函数来识别嵌套的区分联合的情况?相关的知识,希望对你有一定的参考价值。

我创建了一个嵌套的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 DU交易OptimizationCriterion的函数,并能够识别这个案例。沿着generic的线路是否有stringToDUCase功能可以做同样的事情?

2)使用OptimizationCriterion*OptimizationPeriod类型的元组而不是嵌套的DU会更好吗? (我可能不得不两次打电话给stringToDUCase,但这不是问题)

答案

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|]
另一答案

我认为现有的答案应该直接回答你的问题。但是,我认为值得再提两点。首先,如果您将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直接写这个,但是使用自定义反射代码是一个快速导致无法维护的代码。

以上是关于是否有一个通用函数来识别嵌套的区分联合的情况?的主要内容,如果未能解决你的问题,请参考以下文章

空F#区分联合案例的C#类型

Excel中IF函数的多条件判断(嵌套),与LOOKUP函数的联合应用

具有通用联合约束的 TypeScript 函数返回值

展开 F# 单例区分联合元组类型

TypeScript:推断嵌套联合类型的类型

Android 逆向加壳技术识别 ( 函数抽取 与 Native 化加壳的区分 | VMP 加壳与 Dex2C 加壳的区分 )