处理相同类型但形状不同的对象的惯用方法是什么?

时间:2017-09-26 12:04:27

标签: dictionary f# discriminated-union

我有一张地图,其键可能有各种各样的形状(我使用的是"形状"松散地)。为了处理这个问题,我创建了一个有区别的联合(DU):

type AuxDataKey =
    | OneString         of string
    | TwoStrings        of string * string
    | OneStringInt      of string * int
    | TwoStringsInt     of string * string * int
    | OneStringTwoInts  of string * int * int
    | TwoStringsTwoInts of string * string * int * int

type AuxData = Map<AuxDataKey,AuxDataItem>

可以通过这种方式创建密钥:

let key = AuxDataKey.TwoStringsInt("Vol", ticker, count)

可以从即时创建的密钥中检索信息:

let vols = auxData.[AuxDataKey.TwoStringsInt("Vol", ticker, count)]

这很有效,但我发现它非常麻烦。

另一种方法是创建另一个DU:

type StringInt = String of string | Int of int

然后让Map键成为这些对象的列表。没有必要为地图键创建DU,StringInt DU更简单。另一方面,这个替代方案看起来不太安全,因为编译器可以接受列表,例如,第一个元素是Int,第二个是String,它不适合AuxDataKey中的任何一个案例。 DU。我没有在实践中尝试过,因为可能有更好的选择。

是否有惯用的方法来处理must具有相同类型但具有不同形状的对象?

2 个答案:

答案 0 :(得分:5)

你这样做的方式非常惯用。请注意,在引用DU案例时,您不需要为类型名称AuxDataKey添加前缀,因此您只需编写:

let key = TwoStringsInt("Vol", ticker, count)

有可能将您的类型分解为有意义的部分,但在不知道组成部分的真正含义的情况下很难分辨。这可以摆脱一些重复。例如,只需拉出其中一个字符串,所有情况都有共同点就可以了:

type SubKey =
    | A of string
    | B of int
    | C of string * int
    | D of int * int
    | E of string * int * int

type AuxDataKey =
    { Key : string
      SubKey : SubKey }

其他一些语言具有这样的功能,其中联合案例由类型系统根据返回的不同值自动推断。我相信TypeScript可以做到这一点,而OCaml与其多态变体有类似之处。但是,F#在这方面完全名义上输入,这意味着每个DU案例必须在使用之前进行定义和命名。

答案 1 :(得分:1)

我明白你真正想要的是这样的:

ow2.asm

但是有太多的情况让这感觉像是毫无意义的运动?

你的问题告诉我的方式,你感到被expression problem错误的一面感到痛苦。你有一个完全不同类型的宇宙,你试图在它们的形状中找到一个模式,它将使用(闭合的)区分联合来表示它们是可行的。

F#通过使用该语言的OO功能为您提供了一种简单的切换方式。我之前使用和看过的模式是创建一个开放的类型层次结构,它继承了一个公共基类型(接口或抽象类),并使用部分活动模式扩充它们,这些模式为您提供了一个访问它们的API,感觉很自然。 F#。

type AuxDataKey = 
    | Vol of string * int
    | AnotherKey of string * int * int * string
    | ...

并像这样使用它们:

type IAuxDataKey = interface end 

type VolKey =
    {
        arg1: string
        arg2: int
    }
    interface IAuxDataKey

let (|VolKey|_|) (value: IAuxDataKey) =
    match value with
    | :? VolKey as key -> Some (key.arg1, key.arg2)
    | _ -> None

let x = { arg1 = "test"; arg2 = 42 } :> IAuxDataKey match x with | VolKey (arg1, arg2) -> printfn "arg1: %s, arg2: %d" arg1 arg2 | _ -> printfn "unknown" 这里只是一个标记界面,但是如果在你的上下文中有意义的话,你可以在那里添加常用成员(例如,如果密钥来自同源,则可以使用解析或创建方法)。

你在这里失去的是能够进行详尽的比赛,你需要全面的案例。但我认为这不是你用例中的一个问题。