展开F#单格判别联合元组类型

展开F#单格判别联合元组类型,f#,namedtuple,discriminated-union,F#,Namedtuple,Discriminated Union,我们可以使用如下展开函数展开类型:type Address=Address of string let unwrapAddress (Address a) = a let addr = Address "sdf" let str = unwrapAddress addr 因此str将是string类型,但如果有类似的类型,这种方法将不起作用: type Composite = Composite of integer:int * someStr:string let unwrap (Compo

我们可以使用如下展开函数展开类型:
type Address=Address of string

let unwrapAddress (Address a) = a
let addr = Address "sdf"
let str = unwrapAddress addr
因此
str
将是
string
类型,但如果有类似的类型,这种方法将不起作用:

type Composite = Composite of integer:int * someStr:string
let unwrap (Composite c) = c
会产生误差

let unwrap (Composite c) = c;;
------------^^^^^^^^^^^
error FS0019: This constructor is applied to 1 argument(s) but expects 2

我能把复合类型展开成一个简单的元组吗?

这些都适合我。这是你的语法,你经常会发现它与match语句一起使用,但它在赋值的l.h.s.上。最初,这可能对元组最有意义,但您可以将其用于任何结构

let (a,b) = (1,2)

let (x,_) = (4,5)
还有两件有趣的事情可以尝试:

let (head::tail) = [1;2;3;4]
FSI响应警告FS0025:此表达式上的模式匹配不完整。例如,值“[]”可能表示模式未涵盖的情况

“那是真的,”你大声推理。“我应该将其表示为匹配,并可能包含一个空列表”。最好将这些类型的警告冒泡成完全真实的错误(参见:例如--warnaserror+:25)。不要忽视它们。通过习惯或编译器强制执行的方法解决这些问题。单一情况下没有歧义,所以继续编写代码

更有用、更有趣的是函数赋值的l.h.s.上的匹配语法。这很酷。对于简洁的函数,您可以将里面的东西解包,然后在一个步骤中对内部进行操作

let f (Composite(x,y)) = sprintf "Composite(%i,%s)" x y

f (Composite(1,"one"))

> val it : string = "Composite(1,one)"
关于您的代码:

type Address = Address of string //using unwrapping function like

let unwrapAddress (Address a) = a
let addr = Address "sdf"
let str = unwrapAddress addr

type Composite = Composite of integer:int * someStr:string
let unwrap (Composite(c,_)) = c
let cval = Composite(1,"blah")
unwrap cval
解决方法:

let xy = Composite(1,"abc") |> function (Composite(x,y))->(x,y)
。。。但是更好的方法是,假设您想保留单个case DU的命名元素,那么

let (|Composite|) = function | Composite(x,y)->(x,y)

let unwrap (Composite(x)) = x

let unwrap2 (Composite(x,y)) = (x,y)
。。。不是严格地通过单个案例进行分解,而是通过单个案例进行分解

最后,您可以将方法附加到复合结构

module Composite = 
  let unwrap = function | Composite(x,y)->(x,y)
关于使用这种技术的最佳讨论之一已经结束

另外,请查看unwrap给我们的签名:一个函数,它接受一个组合(斜体),并返回一个int(粗体)


签名——val展开:复合->int在您的情况下,您可以写:

type Composite = Composite of int * string 

let unwrap (Composite (a, b)) = a, b
对应于:

let unwrap x = 
    match x with
    | Composite (a, b) -> a, b
这里发生的事情是F#允许您使用任意复杂的模式匹配来解构内联函数参数。在介绍单例DU's时经常提到这一点,但很少得出结论,这导致人们相信单例DU's在某种程度上具有特殊性

事实上,您可以在有多个案例时使用它(只要每个案例绑定相同的变量集):

但大多数情况下,您需要在更简单的类型(如元组)上进行模式匹配:

let f (a, b, c) = ...
更奇怪的是:

let f () = ...

这里的
()
是单元类型的单个值上的模式匹配,而不是通常所描述的某种“无参数函数的可视标记”。

您将该类型定义为具有命名字段的区分大小写的并集:

type Composite = Composite of integer:int * someStr:string
以这种方式定义时,union case的字段不是简单的元组。它们以一种特殊的方式处理,例如,这些名称在编译代码中用作属性名称。模式匹配不会自动将元素转换为元组,因此必须单独展开它们:

let unwrap (Composite(i, s)) = i, s
但是,您也可以在字段为普通元组的情况下定义单事例并集。(请注意,元组类型周围需要括号-否则,它也会以特殊的方式处理,除非这些项将被编译为
Item1
Item2

使用此定义,
unwrap
函数将正常工作并提取元组值:

let unwrap (Composite c) = c
也可以使用嵌套模式获取数字和字符串,如前一种情况:

let unwrap (Composite(i, s)) = i, s

根据您是编写(T1*T2)的
A还是编写T1*T2的
A
这两个字段,其行为会有所不同,这一事实有点微妙-可能需要区分这两个字段,以便编译器知道是将字段编译为两个单独的字段,还是将字段编译为
System.Tuple
类型的一个字段。我无法想象任何其他情况下的差异会有多大。

好吧,这就像PM一样,所以你可以写:
让展开(复合(I,s))=I,s
我已经更正了你问题中的错误消息。您可能得到了另一个,因为您忘记将类型定义运行到FSI中。问得好!我不知道这个edge案例。
“这些都适合我。”
。代码实际上并没有编译(尽管编译错误与报告的不同),所以这实际上并不能回答问题。我发现有一些模式匹配行为是出乎意料的,这在托马斯的回答中已经解释过了。@QuickBrownFox:当然。我读到这个问题的意思是以类似元组的方式进行分解,因此对基础知识进行了回顾。也许这就是初衷?无论如何,您对问题的编辑会使问题更清楚。我跳过了这个,因为我认为它会好的。在编译器方面整理一下会很好。这是否已作为github请求提交?“最好能捕捉到所有内容。@QuickBrownFox:本着原始问题(命名为DU元素)的精神,我添加了一个活动模式识别器作为解决方案。谢谢你指出这一点。
let unwrap (Composite c) = c
let unwrap (Composite(i, s)) = i, s