Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 除了从函数返回受歧视的并集,还有什么其他选择?_F#_Pattern Matching_Discriminated Union - Fatal编程技术网

F# 除了从函数返回受歧视的并集,还有什么其他选择?

F# 除了从函数返回受歧视的并集,还有什么其他选择?,f#,pattern-matching,discriminated-union,F#,Pattern Matching,Discriminated Union,我正在尝试用F#重写一段复杂的代码。 对于这个特殊的代码库,有差别的联合对我帮助很大,所以我将尽可能多地使用它们。具体来说,DU的穷尽性检查帮助我避免了许许多多的bug 然而,我面临着一个重复的模式,即必须使用match。。。在某种程度上,代码中的混乱抵消了我从穷尽性检查中获得的好处。 我尽可能地简化了我正在处理的模式,并试图给出一个示例来演示我正在编写的代码的结构。真正的代码库要复杂得多,它位于一个完全不同的领域,但在语言级别上,这个示例代表了这个问题 假设我们想根据购物者的分类来了解购物者:

我正在尝试用F#重写一段复杂的代码。 对于这个特殊的代码库,有差别的联合对我帮助很大,所以我将尽可能多地使用它们。具体来说,DU的穷尽性检查帮助我避免了许许多多的bug

然而,我面临着一个重复的模式,即必须使用
match。。。在某种程度上,代码中的混乱抵消了我从穷尽性检查中获得的好处。
我尽可能地简化了我正在处理的模式,并试图给出一个示例来演示我正在编写的代码的结构。真正的代码库要复杂得多,它位于一个完全不同的领域,但在语言级别上,这个示例代表了这个问题

假设我们想根据购物者的分类来了解购物者:他们要么是猫族,要么是狗族。这里的关键是通过DU对某些类型(元组)进行分类。 以下是域类型:

type PetPerson =
    |CatPerson
    |DogPerson

type CatFood =
    |Chicken
    |Fish

type DogFood =
    |Burger
    |Steak

//some cat food, shopper's age and address
type CatFoodShopper = CatFoodShopper of (CatFood list * int * string)    

//some dog food, shopper's age and number of children
type DogFoodShopper = DogFoodShopper of (DogFood list * int * int)
撇开我们喂养可怜动物的可怕方式不谈,这个域模型需要一个函数来映射
PetPerson
CatFoodShopper
dogfooddshopper

此时,我最初的想法是定义一个购物者类型,因为我无法根据模式匹配的结果从以下函数返回两种不同的类型:

type Shopper =
    |CatFShopper of CatFoodShopper
    |DogFShopper of DogFoodShopper

let ShopperViaPersonality = function
    |CatPerson -> CatFShopper (CatFoodShopper ([Chicken;Fish], 32, "Hope St"))
    |DogPerson -> DogFShopper (DogFoodShopper ([Burger;Steak], 45, 1))
这就解决了问题,但是我在代码中有很多地方(实际上很多地方)我最终得到了一个
PetPerson
,需要根据
PetPerson
值得到一个
CatFoodShopper
dogfooddshopper
。这导致了不必要的模式匹配的情况下,我知道我没有在手上。以下是一个例子:

let UsePersonality (x:int) (y:PetPerson) =
    //x is used in some way etc. etc.
    match y with
    |CatPerson as c -> //how can I void the following match?
        match (ShopperViaPersonality c) with
        |CatFShopper (CatFoodShopper (lst,_,_))-> "use lst and return some string "
        | _ -> failwith "should not have anything but CatFShopper"
    |DogPerson as d -> //same as before. I know I'll get back DogFShopper
        match (ShopperViaPersonality d) with
        |DogFShopper (DogFoodShopper (lst, _,_)) -> "use lst and return other string"
        |_ -> failwith "should not have anything but DogFShopper"
正如您所看到的,我必须编写模式匹配代码,即使我知道我将返回一个特定的值。我无法简单地将
CatPerson
值与
CatFoodShopper
值关联起来

为了提高调用站点的性能,我考虑使用F#通过接口模拟类型类的方法,基于以下大量示例:

type IShopperViaPersonality<'T> =
    abstract member ShopperOf: PetPerson -> 'T

let mappingInstanceOf<'T> (inst:IShopperViaPersonality<'T>) p = inst.ShopperOf p

let CatPersonShopper =
    {new IShopperViaPersonality<_> with
        member this.ShopperOf x =
            match x with
            |CatPerson -> CatFoodShopper ([Chicken;Fish], 32, "Hope St")
            | _ -> failwith "This implementation is only for CatPerson"}
let CatPersonToShopper = mappingInstanceOf CatPersonShopper

let DogPersonShopper =
    {new IShopperViaPersonality<_> with
        member this.ShopperOf x =
            match x with
            |DogPerson -> DogFoodShopper ([Burger;Steak], 45, 1)
            | _ -> failwith "This implementation is only for DogPerson"}
let DogPersonToShopper = mappingInstanceOf DogPersonShopper    
当使用PetPerson值时,这种方法工作得更好,但我现在的任务是定义这些单独的函数,以保持调用站点的整洁

let UsePersonality1 (x:int) (y:PetPerson) =
    match y with
    |CatPerson as c ->
        let (CatFoodShopper (lst,_,_)) = CatPersonToShopper c
        "use lst and return string"
    |DogPerson as d ->
        let (DogFoodShopper (lst,_,_)) = DogPersonToShopper d
        "use lst and return string"
请注意,这个示例旨在演示使用DU和使用接口返回基于分类DU参数的不同类型之间的权衡,如果我可以这样称呼它的话。所以不要挂断我对返回值等的毫无意义的使用


我的问题是:有没有其他方法可以实现对一组元组(或记录)类型进行分类的语义?如果您考虑的是活动模式,那么它们不是一个选项,因为在真正的代码库中,DU有七种以上的情况,这是活动模式的限制,以防它们会有所帮助。那么,对于上述方法,我还有其他改进方法吗

一个明显的方法是在匹配
PetPerson
之前打电话给
ShopperViaPersonality
,而不是在匹配
PetPerson
之后:

let UsePersonality (x:int) (y:PetPerson) =
    //x is used in some way etc. etc.
    match ShopperViaPersonality y with
    | CatFShopper (CatFoodShopper (lst,_,_))-> "use lst and return some string "
    | DogFShopper (DogFoodShopper (lst, _,_)) -> "use lst and return other string"
还请注意,如果
ShooperViaPersonality
的唯一目的是支持模式匹配,则最好将其设置为活动模式:

let (|CatFShopper|DogFShopper|) = function
    | CatPerson -> CatFShopper ([Chicken;Fish], 32, "Hope St")
    | DogPerson -> DogFShopper ([Burger;Steak], 45, 1)
然后你可以这样使用它:

let UsePersonality (x:int) (y:PetPerson) =
    //x is used in some way etc. etc.
    match y with
    | CatFShopper (lst,_,_) -> "use lst and return some string "
    | DogFShopper (lst, _,_) -> "use lst and return other string"

从逻辑上讲,活动模式与DU+a函数几乎相同,但在语法层面上,请注意嵌套现在少了很多。

谢谢Fyodor。首先调用ShopperViaPersonality是正确的,但不幸的是,在原始代码库中不可能这样做。这是一个过于简单的例子,重点是语言层面的权衡。如果它们不限于七种,我会使用它们。事实上,我在问题的末尾写了这个。原始代码在DUs中有七种以上的可能情况。我会考虑“不,你没有任何其他选择比这些”是一个有效的回答我的问题。我基本上是想确定在这种情况下,F#可以做什么。为什么在原始代码库中不可能在匹配之前调用函数?你的例子没有显示这一点,你也没有在文本中提及。我实际上试图在文本中提及这一点,但很抱歉,如果我没有说清楚的话。基本上,整个要点是使用DU对元组的编译时分类进行编码。逻辑由分类DU驱动(以PetPerson为例),调用的上下文要复杂得多。很难将问题归结为一个简单的例子,但这是我的设置:)好吧,如果你不描述问题,恐怕没有人能提供解决方案。从逻辑上考虑,如果有一种方法可以从分类中得到一个有区别的元组,那么为什么不使用它来代替分类本身呢。如果获取不同元组的方法不同,需要不同的上下文,那么它们肯定应该通过不同的函数而不是单一的
ShopperViaPersonality
?如果你认真描述你的问题,我相信社区会想出一个解决方案。我的答案解决了你的问题,如原帖子所述。