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

Shi*_*vel 8 f# discriminated-union namedtuple

我们可以像type Address = Address of string使用展开功能一样展开类型

let unwrapAddress (Address a) = a
let addr = Address "sdf"
let str = unwrapAddress addr
Run Code Online (Sandbox Code Playgroud)

所以str类型string,但如果有这样的类型方法将无法正常工作:

type Composite = Composite of integer:int * someStr:string
let unwrap (Composite c) = c
Run Code Online (Sandbox Code Playgroud)

会产生错误

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

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

Tom*_*cek 7

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

type Composite = Composite of integer:int * someStr:string
Run Code Online (Sandbox Code Playgroud)

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

let unwrap (Composite(i, s)) = i, s
Run Code Online (Sandbox Code Playgroud)

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

type Composite = Composite of (int * string)
Run Code Online (Sandbox Code Playgroud)

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

let unwrap (Composite c) = c
Run Code Online (Sandbox Code Playgroud)

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

let unwrap (Composite(i, s)) = i, s
Run Code Online (Sandbox Code Playgroud)

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


scr*_*wtp 6

在你的情况下,你可以写:

type Composite = Composite of int * string 

let unwrap (Composite (a, b)) = a, b
Run Code Online (Sandbox Code Playgroud)

对应于:

let unwrap x = 
    match x with
    | Composite (a, b) -> a, b
Run Code Online (Sandbox Code Playgroud)

这里发生的事情是 F# 允许您使用任意复杂的模式匹配内联解构函数参数。在介绍单例 DU 时经常提到这一点,但很少有人得出结论,这导致人们认为单例 DU 在某种程度上是特别的。

事实上,当你有多个 case 时,你可以使用它(只要每个 case 绑定相同的一组变量):

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

let unwrapString (Composite (_, s) | JustString s) = s
Run Code Online (Sandbox Code Playgroud)

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

let f (a, b, c) = ...
Run Code Online (Sandbox Code Playgroud)

或者更奇怪的是:

let f () = ...
Run Code Online (Sandbox Code Playgroud)

()是对单位类型的唯一值的模式匹配 - 而不是经常描述的某种“无参数函数的视觉标记”。


sgt*_*gtz 5

这些都对我有用。这是您的匹配语法,通常您会发现它与 match 语句一起使用,但它位于分配的 lhs 上。最初,这可能对元组最有意义,但您可以将其用于任何结构。

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

let (x,_) = (4,5)
Run Code Online (Sandbox Code Playgroud)

另外两件有趣的事情可以尝试:

let (head::tail) = [1;2;3;4]
Run Code Online (Sandbox Code Playgroud)

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)"
Run Code Online (Sandbox Code Playgroud)

关于你的代码:

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
Run Code Online (Sandbox Code Playgroud)

解决方法:

let xy = Composite(1,"abc") |> function (Composite(x,y))->(x,y)
Run Code Online (Sandbox Code Playgroud)

...但更好的方法是,假设您想保留单个案例 DU 的命名元素是...

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

let unwrap (Composite(x)) = x

let unwrap2 (Composite(x,y)) = (x,y)
Run Code Online (Sandbox Code Playgroud)

... 不是通过单个 case DU 严格分解,而是通过单个 case Active Pattern分解

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

module Composite = 
  let unwrap = function | Composite(x,y)->(x,y)
Run Code Online (Sandbox Code Playgroud)

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

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

签名 - val unwrap : Composite -> int