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#_Discriminated Union - Fatal编程技术网

F# 为什么可以';我是否可以通过一个歧视联盟的成员列表来简化这个迭代?

F# 为什么可以';我是否可以通过一个歧视联盟的成员列表来简化这个迭代?,f#,discriminated-union,F#,Discriminated Union,通常,人们希望通过异构对象(不同类型)的集合进行迭代(使用map、iter或fold)。处理这个问题的一种方法是创建一个有区别的并集,它允许创建一个列表,其中的对象被适当地转换为DU案例。下面的代码在一个简单的示例中实现了这一点: type MYDU = | X1 of int | X2 of float | X3 of string let bar (y: MYDU) = match y with | X1 x -> pr

通常,人们希望通过异构对象(不同类型)的集合进行迭代(使用map、iter或fold)。处理这个问题的一种方法是创建一个有区别的并集,它允许创建一个列表,其中的对象被适当地转换为DU案例。下面的代码在一个简单的示例中实现了这一点:

type MYDU = | X1 of int
            | X2 of float
            | X3 of string

let bar (y: MYDU) =
    match y with
    | X1 x -> printfn "%A" x  
    | X2 x -> printfn "%A" x
    | X3 x -> printfn "%A" x

[X1(1); X2(2.0); X3("3"); X1(4)]
|> List.map bar |> ignore
这段代码运行良好,可以打印

1
2.0
"3"
4
太好了!但我想知道是否可以避免重复调用
printfn
。我尝试了以下操作,但无法编译:

let baz (y: MYDU) =
    match y with
    | X1 x | X2 x | X3 x -> printfn "%A" x // red squiggly line under X1 x
编译器发出以下消息:

此表达式应具有类型“int”,但此处具有类型“float”


我怀疑避免重复是可行的,但我肯定犯了一个基本的错误。有什么建议吗?

你没有犯错误,这是F#的类型系统所不允许的

在匹配大小写箭头的左侧可以有多个图案,但它们必须绑定同一组值(包括类型)。这里,
x
对于每个模式都有不同的类型,这足以让编译器抱怨


有一些方法可以减轻痛苦(您可以在DU上有一个成员返回装箱值,或者您可以有一个活动模式在比赛中装箱),但它们是高度情景化的。将模式拆分为单独的案例,并在真空中为每一个案例重复右侧始终是一个更好的解决方案。

您可以改为将参数转换为通用类型以进行打印,然后打印该值。您仍然可以获得模式匹配和有区别的联合的优势:)

下面是这种方法的一个例子

type MYDU = 
  | X1 of int
  | X2 of float
  | X3 of string

let bar y =
    let myStr = 
      match y with
      | X1 x -> string x  
      | X2 x -> string x
      | X3 x -> x
    printfn "%s" myStr

bar (X1 5)

通过将值“装箱”到
obj
类型,可以在一定程度上避免这种重复。这是.NET的别名:“.NET Framework中所有类的最终基类”。这意味着任何类型的任何值也是一个
obj

但是,当您框选对象时,将丢失静态键入。您正在颠覆F#type系统并增加出错的可能性。这就是为什么通常应该避免,除非你有充分的理由这样做

函数
printfn“%A”
可以采用任何类型,因此其类型签名有效地是
obj->unit
。如果您只想对值运行此函数,那么使用装箱可能是合理的。您可以定义此活动模式,它使用
功能:

let (|Box|) x = box x
然后使用如下模式:

let printMyDu myDu =
    match myDu with
    | X1 (Box x)
    | X2 (Box x)
    | X3 (Box x) -> printfn "%A" x

同样,如果可能的话,您应该避免这样做,因为在许多情况下您正在失去类型安全性。例如,如果您只是在稍后检查值的类型时才选中一个值,那么您可能对F#采取了错误的方法。在本例中,我们将值
x
放入框中,然后立即使用并丢弃它,这样我们就不会降低总体类型安全性。

从示例代码片段中很难判断,但是在实践中,具有真正异构列表的场景应该非常罕见,这样的事情就不会麻烦了。在CodeReview中可能值得一提的是,您可能以一种太低级的方式对域进行建模。一般来说,无论何时您在执行
List.map f |>ignore
,您都应该使用
List.iter
,尤其是如果
f
是一个返回
单位的函数(就像您的
bar
函数那样). 如果您计划切换到使用
Seq.map
而不是
List.map
,这一点尤其重要,因为
Seq.map
是惰性的,在必须之前不会迭代整个Seq,但是
Seq.iter
将按照您的预期迭代整个Seq。因此,使用
List.iter
而不是
List.map
将保护您在切换到使用
Seq
时免受意外。我认为在一般情况下,转换为普通类型是不可能的。中的
kvb
的(接受的)答案似乎正是我想做的。问题的标题是“在联合类型的F#列表上操作”,所以我仍然感到困惑。@Soldalma:在这个答案中,他绑定的值(
s
l
)在匹配大小写的两种模式中都具有相同的类型(
string
Section List
)。这里不是这种情况-
x
在每个模式中都有不同的类型。仅此一点就足以使编译失败,即使匹配的右侧分别与这些值中的每一个“起作用”。谢谢