是否可以将区分的联合标记作为参数传递?

mon*_*res 9 f# pattern-matching discriminated-union

是否可以将有区别的联合标记的类型传递给另一个函数,以便它可以用于模式匹配?

我的意思是非工作的例子:

type Animal = Pig of string | Cow of string | Fish of string

let animals = [Pig "Mike"; Pig "Sarah"; Fish "Eve"; Cow "Laura"; Pig "John"]

let rec filterAnimals animalType animals =
    if animals = [] then
        []
    else
        let rest = filterAnimals animalType (List.tail animals)
        match List.head animals with
        |animalType animal -> animal::rest // <- this doesn't work
        |_ -> rest

printfn "%A" (filterAnimals Pig animals)
Run Code Online (Sandbox Code Playgroud)

Sør*_*ois 13

如果案例之间没有语义重叠,则受歧视的联合最有效.

在您的示例中,每个案例包含具有相同含义的相同组件,a string表示"动物的名称".但那是语义上的重叠!然后,鉴别工会会强迫你做你不想区分到:你希望被迫的,"一头牛的名字","一头猪的名字"来区分; 你只想想想"动物的名字".

让我们做一个更适合的类型:

type Animal = Pig  | Cow  | Fish
type Pet = Animal * string

let animals = [(Pig, "Mike"); (Fish, "Eve"); (Pig, "Romeo")
Run Code Online (Sandbox Code Playgroud)

使用该类型,过滤掉非Pigs是一个单行:

animals |> List.filter (fst >> (=) Pig) 
Run Code Online (Sandbox Code Playgroud)

如果不是每个动物都有名字,请使用选项类型:

type Pet = Animal * string option
Run Code Online (Sandbox Code Playgroud)

如果你知道每个人都有一个名字,你使用一个受歧视的联盟Pig,但是没有Fish:这些情况没有重叠.


Mar*_*ann 7

您可以更改filterAnimals函数以将" 部分活动模式"作为输入:

let rec filterAnimals (|IsAnimalType|_|) animals =
    if animals = [] then
        []
    else
        let rest = filterAnimals (|IsAnimalType|_|) (List.tail animals)
        match List.head animals with
        | IsAnimalType animal -> animal::rest
        | _ -> rest
Run Code Online (Sandbox Code Playgroud)

然后,您可以为猪定义活动部分模式:

let (|IsPig|_|) candidate =
    match candidate with
    | Pig(_) -> Some candidate
    | _ -> None
Run Code Online (Sandbox Code Playgroud)

你可以像这样调用函数(FSI示例):

> filterAnimals (|IsPig|_|) animals;;
val it : Animal list = [Pig "Mike"; Pig "Sarah"; Pig "John"]
Run Code Online (Sandbox Code Playgroud)

实际上,您可以像这样减少部分活动模式:

let (|IsPig|_|) = function | Pig(x) -> Some(Pig(x)) | _ -> None
Run Code Online (Sandbox Code Playgroud)

事实证明,你甚至可以内联它们:

> filterAnimals (function | Pig(x) -> Some(Pig(x)) | _ -> None) animals;;
val it : Animal list = [Pig "Mike"; Pig "Sarah"; Pig "John"]
> filterAnimals (function | Fish(x) -> Some(Fish(x)) | _ -> None) animals;;
val it : Animal list = [Fish "Eve"]
> filterAnimals (function | Cow(x) -> Some(Cow(x)) | _ -> None) animals;;
val it : Animal list = [Cow "Laura"]
Run Code Online (Sandbox Code Playgroud)

  • 如果你有数百种不同的动物,我认为可以说你不应该把它们塑造成一个有数百个案例的歧视联盟...... (4认同)
  • 在我的例子中,它们实际上是编程语言的标记(我在F#中编写解析器作为学习项目).当F#已经知道类型看起来不是很优雅时,必须创建"IsRightParen","IsLeftParen","IsKeyword". (3认同)