我何时应该使用元组上的记录?

Sco*_*rod 8 f#

我正在尝试在F#中练习域驱动设计,并偶然发现了以下问题:

为什么我在使用元组时使用记录似乎需要较少的语法,并且在模式匹配和整体使用场景方面似乎更强大?

例如,如果我使用元组代替,我觉得好像我不再需要创建一个记录类型来实现一个有区别的联合.

type Name = { First:string 
              Middle:string option
              Last:string }

type Duration = { Hours:int
                  Minutes:int
                  Seconds:int }

type Module = 
    | Author of Name
    | Title of string
    | Duration of Duration

let tryCreateName (first, middle, last) =
    { First=first; Middle=Some middle; Last=last }

let tryCreateDuration (hours, minutes, seconds) =
    { Hours=hours; Minutes=minutes;Seconds=seconds }

let name = tryCreateName ("Scott", "K", "Nimrod")

let hours = 1
let minutes = 30
let seconds = 15

let duration = tryCreateDuration (hours, minutes, seconds)
Run Code Online (Sandbox Code Playgroud)

我的想法准确吗?

在大多数情况下,元组是否需要记录?

Mar*_*ann 15

对于域建模,我建议使用带有命名元素的类型; 也就是说,记录,歧视联盟,也许是偶尔的类或界面.

在结构上,记录和元组是相似的; 在代数数据类型中,它们都是产品类型.

不同之处在于,对于元组,值的顺序很重要,每个元素的作用都是隐含的.

> (2016, 1, 2) = (2016, 1, 2);;
val it : bool = true
> (2016, 1, 2) = (2016, 2, 1);;
val it : bool = false
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,您可能会猜测这些元组建模日期,但究竟是哪些?这是2016年1月的第二天吗?或者它是2016年2月的第一个?

另一方面,对于记录,元素的顺序无关紧要,因为您绑定它们并按名称访问它们:

> type Date = { Year : int; Month : int; Day : int };;

type Date =
  {Year: int;
   Month: int;
   Day: int;}

> { Year = 2016; Month = 1; Day = 2 } = { Year = 2016; Day = 2; Month = 1 };;
val it : bool = true
Run Code Online (Sandbox Code Playgroud)

当你想要提取成分值时,它也更清晰.您可以轻松地从记录值中获取年份:

> let d = { Year = 2016; Month = 1; Day = 2 };;

val d : Date = {Year = 2016;
                Month = 1;
                Day = 2;}

> d.Year;;
val it : int = 2016
Run Code Online (Sandbox Code Playgroud)

从元组中提取值更难:

> let d = (2016, 1, 2);;

val d : int * int * int = (2016, 1, 2)

> let (y, _, _) = d;;

val y : int = 2016
Run Code Online (Sandbox Code Playgroud)

对于对,您可以使用内置函数fstsnd访问元素,但对于具有三个或更多元素的元组,除非模式匹配,否则无法轻松获取值.

即使您定义自定义函数,每个元素的角色仍然由其序数隐式定义.如果它们属于同一类型,则很容易使值的顺序错误.

因此对于域建模,我总是喜欢显式类型,因此很清楚发生了什么.

那些元组永远不合适吗?

元组在其他环境中很有用.当您需要使用ad hoc类型来编写函数时,它们比记录更合适.

例如,考虑一下,Seq.zip它可以组合两个序列:

let alphalues = Seq.zip ['A'..'Z'] (Seq.initInfinite ((+) 1)) |> Map.ofSeq;;

val alphalues : Map<char,int> =
  map
    [('A', 1); ('B', 2); ('C', 3); ('D', 4); ('E', 5); ('F', 6); ('G', 7);
     ('H', 8); ('I', 9); ...]

> alphalues |> Map.find 'B';;
val it : int = 2
Run Code Online (Sandbox Code Playgroud)

正如您在此示例中所看到的,元组只是向实际目标迈出的一步,这是一个按字母顺序排列的值的映射.如果我们每次想要在表达式中组合值时必须定义记录类型,那就太尴尬了.元组非常适合这项任务.

  • 元素的顺序在记录类型声明_中很重要,因为编译器生成的IComparable实现依赖于它. (2认同)