理解 F# 中的字符串常量

3

我正在尝试理解以下代码,特别是StringConstant:

type StringConstant = StringConstant of string * string 

[<EntryPoint>]
let main argv = 
    let x = StringConstant("little", "shack")
    printfn "%A" x

    0 // return an integer exit code

(为了提供背景信息,StringConstant在FParsec教程中使用,但此示例未使用FParsec。)
我想知道的是:
1. type语句到底是做什么的? 2. 一旦我实例化x,如何访问各个“部分”(“little”或“house”)?
4个回答

7

正如其他人已经指出的那样,从技术上讲,StringConstant是一个仅有单个案例的带标签联合类型,您可以使用模式匹配提取值。

在谈论F#中的领域建模时,我喜欢使用另一个有用的类比。通常,您可以从简单地说某个数据类型是元组开始:

type Person = string * int

这是一种非常简单的表示数据的方式,但问题在于当你写下"Tomas", 42时,编译器并不知道你的意思是Person,而是将其理解为string * int元组。单例鉴别联合是一种非常好的方式来给你的元组命名。
type Person = Person of string * int

这里使用了两次名称为Person的情况可能有点令人困惑,第一次是类型名称,第二次是案例的名称。这没有特殊含义 - 它只是意味着该类型将与案例具有相同的名称。
现在,您可以编写Person("Tomas", 42)来创建一个值,并且它将具有类型Person。您可以使用matchlet进行分解,但您也可以轻松编写接受Person的函数。例如,要返回名称,您可以编写:
let getName (Person(name, _)) =  
  name

我认为单例判别联合通常被广泛使用,主要是因为它们非常容易定义和使用。然而,在作为公共API暴露的代码中,我不会使用它们,因为它们有点不寻常并可能会令人困惑。
PS:还要注意,在提取值时需要使用括号:
// Correct. Defines symbols 'name' and 'age'
let (Person(name, age)) = tomas 

// Incorrect! Defines a function `Person` that takes a tuple 
// (and hides the `Person` case of the discriminated union)
let Person(name, age) = tomas

2

StringConstant 是一个带有唯一成员的判别联合类型(也被命名为 StringConstant)。您可以通过模式匹配来提取其部分,使用 match/function 或仅使用 let,因为只有一个成员:

let (StringConstant(firstPart, secondPart)) = x

1
+1(我不得不从我的正在进行的答案中删除第一段,因为它与你写的完全相同:-)) - Tomas Petricek

1
type StringConstant = StringConstant of string * string 

结果是一个带有一种类型的联合类型。

type StringConstant = | StringConstant of string * string 如果在 F# 交互中执行它,您就可以看到。

您可以在这里查看 msdn 文档 here

您可以像这样获取值:

let printValue opt =
    match opt with
    | StringConstant( x, y) -> printfn "%A%A" x y

1
其他人已经提到了如何从一个判别式联合中提取数据,但是更详细地解释一下判别式联合,可以说它们有点像强化版的枚举。它们在幕后作为类型层次结构实现,其中类型是基类,情况是该基类的子类,具有任何参数作为只读公共变量。
在Scala中,类似的数据结构称为case classes,这可能有助于您自己验证此实现方法。
判别式联合的一个好处是它们是自引用的,因此非常适合定义像树这样的递归结构。以下是霍夫曼编码树的定义,仅使用三行代码。在C#中,这可能需要大约5至10倍的代码行数。
type CodeTree =
     | Branch of CodeTree * CodeTree * list<char> * int
     | Leaf of char * int

关于区分联合类型的信息请参见msdn文档

有关使用区分联合类型作为树形结构的示例,请参见gist,这是大约60行F#代码实现的哈夫曼解码器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接