繁体   English   中英

展开F#单例区分联合元组类型

[英]Unwrap F# single-case discriminated union tuple type

我们可以像使用展开函数一样打开类型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

我可以以某种方式将复合类型解包为一个简单的元组吗?

您将类型定义为具有命名字段的单个案例区分联合:

type Composite = Composite of integer:int * someStr:string

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

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

但是,您也可以定义单个案例并集,其中字段是普通元组。 (请注意,您需要围绕元组类型的括号 - 否则,它最终也会以特殊方式处理,除了项目将被编译为Item1Item2 。)

type Composite = Composite of (int * string)

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

let unwrap (Composite c) = c

您还可以使用嵌套模式来获取数字和字符串,如上例所示:

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

根据您是否编写A of (T1 * T2)或者是否编写A of T1 * T2 ,这种情况会有所不同,这两者可能需要区分,以便编译器知道是否编译字段作为两个单独的字段或作为System.Tuple<T1, T2>类型的一个字段。 我无法想象任何其他情况下差异会很重要。

这些都适合我。 这是你的匹配语法,通常你会发现它与匹配语句一起使用,但它是在赋值的lhs上。 可能,这对于元组来说最有意义,但是你可以在任何结构中使用它。

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

let (x,_) = (4,5)

另外两个有趣的事情:

let (head::tail) = [1;2;3;4]

FSI响应警告FS0025:此表达式上的不完整模式匹配。 例如,值“[]”可以指示模式未涵盖的情况。

“那是真的,”你大声说。 “我应该把它表达为一个匹配,并列出一个空列表作为一种可能性”。 最好将这些类型的警告冒充为完全真实的错误(参见: 警告错误,例如--warnaserror +:25 )。 不要忽视它们。 通过习惯或编译器强制方法解决它们。 单个案例没有歧义,所以代码开启。

更有用+有趣的是函数赋值的lhs上的匹配语法。 这很酷。 对于精辟的函数,您可以解压缩内部的东西,然后在一个步骤中对内部进行操作。

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)

...但更好的方式,假设你想保留你的单个案例DU的命名元素将...

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

let unwrap (Composite(x)) = x

let unwrap2 (Composite(x,y)) = (x,y)

...不是通过单个案例DU严格分解,而是通过单个案例的活动模式进行分解

最后,您可以将方法附加到Composite结构...

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

关于使用这种技术的最佳讨论之一就在这里

另外,查看unwrap给我们的签名:一个复合(斜体)的函数,并返回一个int(以粗体显示)

Signature - val unwrap: Composite - > 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时经常会提到这一点,但很少有结论,这导致人们相信单个案例DU在某种程度上是特殊的。

实际上,当您有多个案例时(只要每个案例绑定同一组变量),您就可以使用它:

type Composite = Composite of int * string | JustString of string

let unwrapString (Composite (_, s) | JustString s) = s

但大多数时候,你会在更简单的类型上进行模式匹配,比如元组:

let f (a, b, c) = ...

甚至更奇怪的是:

let f () = ...

Here ()是单位类型的单独值的模式匹配 - 而不是某种“无参数函数的可视标记”,正如它经常描述的那样。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM