值是通过计算表达式生成的数据。 本部分介绍了 M 语言的值种类。 每种类型的值都与一种文字语法、一组该类型的值、一组在该组值上定义的运算符以及一种归属于新构造的值的内部类型相关联。

种类 文本
Null null
逻辑 true    false
数字 0    1    -1    1.5    2.3e-5
时间 #time(09,15,00)
Date #date(2013,02,26)
DateTime #datetime(2013,02,26, 09,15,00)
DateTimeZone #datetimezone(2013,02,26, 09,15,00, 09,00)
Duration #duration(0,1,30,0)
文本 "hello"
二进制 #binary("AQID")
列表 {1, 2, 3}
记录 [ A = 1, B = 2 ]
表格 #table({"X","Y"},{{0,1},{1,0}})
Function (x) => x + 1
类型 type { number }    type table [ A = any, B = text ]

以下各节详细介绍了每种值种类。 类型和类型归属在类型中正式定义。 函数值在函数中定义。 以下各节列出了为每种值类型定义的运算符,并提供了示例。 运算符语义的完整定义如运算符中所示。

Null

null 值用于表示缺失值,或者值的状态不确定或未知。 使用文本 null 写入 null 值。 为 null 值定义了以下运算符:

运算符 结果
x > y 大于
x >= y 大于或等于
x < y 小于
x <= y 小于或等于
x = y 等于
x <> y 不等于
x ?? y Coalesce

null 值的本机类型是内部类型 null

逻辑

逻辑值用于布尔运算,值为 true 或 false。 使用文本 truefalse 写入逻辑值。 为逻辑值定义了以下运算符:

运算符 结果
x > y 大于
x >= y 大于或等于
x < y 小于
x <= y 小于或等于
x = y 等于
x <> y 不等于
x or y 条件逻辑或
x ?? y Coalesce
x and y 条件逻辑和
not x 逻辑非

逻辑值(truefalse)的本机类型是内部类型 logical

数字

数值用于数字和算术运算。 下面是数字文本的示例:

3.14  // Fractional number 
-1.5  // Fractional number 
1.0e3 // Fractional number with exponent
123   // Whole number 
1e3   // Whole number with exponent 
0xff  // Whole number in hex (255)

数字至少以双精度表示(但可以保留更高的精度)。 双精度表示形式与 [IEEE 754-2008] 中定义的二进制浮点运算的 IEEE 64 位双精度标准一致。 (双精度表示形式的动态范围约为 5.0 x 10324 到 1.7 x 10308,精度为 15-16 位。)

以下特殊值也被视为数值:

  • 正零和负零。 在大多数情况下,正零和负零的行为与简单值零相同,但是某些运算区分这两个值

  • 正无穷 (#infinity) 和负无穷 (-#infinity)。 无穷大是由非零数除以零等运算产生的。 例如,1.0 / 0.0 产生正无穷,-1.0 / 0.0 产生负无穷。

  • 非数字值 (#nan),通常缩写为 NaN。 NaN 是由无效的浮点运算生成的,例如用零除以零。

使用精度执行二进制数学运算。 精度决定将操作数舍入到的域和执行操作的域。 如果没有显式指定的精度,则使用双精度执行此类操作。

  • 如果数学运算的结果对于目标格式来说太小,则运算结果将变为正零或负零。

  • 如果数学运算的结果对于目标格式来说太大,则运算的结果将变为正无穷或负无穷。

  • 如果数学运算无效,则运算结果变为 NaN。

  • 如果浮点运算的一个或两个操作数都是 NaN,则运算结果变为 NaN。

为数值定义了以下运算符:

运算符 结果
x > y 大于
x >= y 大于或等于
x < y 小于
x <= y 小于或等于
x = y 等于
x <> y 不等于
x + y 求和
x - y
x * y 产品
x / y
x ?? y Coalesce
+x 一元加
-x 否定

数值的本机类型是内部类型 number

时间

时间值存储一天中时间的不透明表示形式。 时间编码为自午夜起的时钟周期数,即 24 小时制中已经过去的 100 纳秒的数量。 自午夜起的最大时钟周期数对应 23:59:59.9999999 这一时刻。

虽然没有针对时间的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #time 来构造时间:

#time(hour, minute, second)

以下内容必须保持不变,否则将引发原因代码为 Expression.Error 的错误:

0 ≤ hour ≤ 24
0 ≤ minute ≤ 59
0 ≤ second ≤ 59

此外,如果 hour = 24,则 minute 和 second 必须为零。

为时间值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

以下运算符允许其一个或两个操作数为日期:

运算符 左操作数 右操作数 含义
x + y time duration 持续时间的日期偏移
x + y duration time 持续时间的日期偏移
x - y time duration 求反持续时间的日期偏移
x - y time time 日期之间的持续时间
x & y date time 合并的 DateTime

时间值的本机类型是内部类型 time

日期

日期值存储特定日期的不透明表示形式。 日期编码为有史以来经过的天数,从公历 0001 年 1 月 1 日开始。 有史以来的最大天数为 3652058,对应 9999 年 12 月 31 日。

虽然没有针对日期的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #date 来构造日期:

#date(year, month, day)

以下内容必须保持不变,否则将引发原因代码为 Expression.Error 的错误:

1 ≤ year ≤ 9999
1 ≤ month ≤ 12
1 ≤ day ≤ 31

此外,日期必须对选定的月份和年份有效。

为日期值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

以下运算符允许其一个或两个操作数为日期:

运算符 左操作数 右操作数 含义
x + y date duration 持续时间的日期偏移
x + y duration date 持续时间的日期偏移
x - y date duration 求反持续时间的日期偏移
x - y date date 日期之间的持续时间
x & y date time 合并的 DateTime

日期值的本机类型是内部类型 date

DateTime

日期时间值同时包含日期和时间。

虽然没有针对日期/时间的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #datetime 来构造日期/时间:

#datetime(year, month, day, hour, minute, second)

以下内容必须保持不变,否则将引发原因代码为 Expression.Error 的错误:1 ≤ year ≤ 9999
1 ≤ month ≤ 12
1 ≤ day ≤ 31
0 ≤ hour ≤ 23
0 ≤ minute ≤ 59
0 ≤ second ≤ 59

此外,日期必须对选定的月份和年份有效。

为日期时间值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

以下运算符允许其一个或两个操作数为日期时间:

运算符 左操作数 右操作数 含义
x + y datetime duration 持续时间的日期时间偏移
x + y duration datetime 持续时间的日期时间偏移
x - y datetime duration 求反持续时间的日期时间偏移
x - y datetime datetime 日期时间之间的持续时间

日期时间值的本机类型是内部类型 datetime

时区

时区值包含日期时间和时区。 时区编码为从 UTC 偏移的分钟数,即日期时间的时间部分从全球协调时 (UTC) 偏移的分钟数 。 UTC 偏移分钟数的最小值为 -840,表示 UTC 偏移量 -14:00,或者说比 UTC 早 14 小时。 UTC 偏移分钟数的最大值为 840,对应于 UTC 偏移量 14:00。

虽然没有针对日期时间和时区的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #datetimezone 来构造日期时间和时区:

#datetimezone(
       year, month, day,
       hour, minute, second,
       offset-hours, offset-minutes)

以下内容必须保持不变,否则将引发原因代码为 Expression.Error 的错误:

1 ≤ year ≤ 9999
1 ≤ month ≤ 12
1 ≤ day ≤ 31
0 ≤ hour ≤ 23
0 ≤ minute ≤ 59
0 ≤ second ≤ 59
-14 ≤ offset-hours ≤ 14
-59 ≤ offset-minutes ≤ 59

此外,日期必须对所选月份和年份有效,如果偏移小时数 = 14,则偏移分钟数 <= 0,如果偏移小时数 = -14,则偏移分钟数 >= 0。

为时区值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

以下运算符允许其一个或两个操作数为时区:

运算符 左操作数 右操作数 含义
x + y datetimezone duration 持续时间的 Datetimezone 偏移
x + y duration datetimezone 持续时间的 Datetimezone 偏移
x - y datetimezone duration 求反持续时间的 Datetimezone 偏移
x - y datetimezone datetimezone datetimezone 之间的持续时间

时区值的本机类型是内部类型 datetimezone

持续时间

持续时间值存储以 100 纳秒为单位的时间轴上两点之间的距离的不透明表示。 持续时间的大小可以是正的,也可以是负的,正值表示时间向前推进,负值表示时间向后推进。 在持续时间中可以存储的最小值是 -9,223,372,036,854,775,808 个时钟周期,即往前 10,675,199 天 2 小时 48 分 05.4775808 秒。 在持续时间中可以存储的最大值是 9,223,372,036,854,775,807 个时钟周期,即往后 10,675,199 天 2 小时 48 分 05.4775807 秒。

虽然没有针对持续时间的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #duration 来构造持续时间:

#duration(0, 0, 0, 5.5)          // 5.5 seconds 
#duration(0, 0, 0, -5.5)         // -5.5 seconds 
#duration(0, 0, 5, 30)           // 5.5 minutes 
#duration(0, 0, 5, -30)          // 4.5 minutes 
#duration(0, 24, 0, 0)           // 1 day 
#duration(1, 0, 0, 0)            // 1 day

为持续时间值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

此外,以下运算符允许其一个或两个操作数为持续时间值:

运算符 左操作数 右操作数 含义
x + y datetime duration 持续时间的日期时间偏移
x + y duration datetime 持续时间的日期时间偏移
x + y duration duration 持续时间之和
x - y datetime duration 求反持续时间的日期时间偏移
x - y datetime datetime 日期时间之间的持续时间
x - y duration duration 持续时间之差
x * y duration number 持续时间的 N 倍
x * y number duration 持续时间的 N 倍
x / y duration number 持续时间的分数

持续时间值的本机类型是内部类型 duration

文本

文本值表示 Unicode 字符序列。 文本值具有符合以下语法的文本形式:

_text-literal:
      " 文本文本字符选择 "
text-literal-characters:
      text-literal-character text-literal-charactersopt
text-literal-character:
      single-text-character
      character-escape-sequence
      double-quote-escape-sequence
single-text-character:

      除后跟 ( (U+0028) 的 " (U+0022) 或 # (U+0023) 外的任何字符
double-quote-escape-sequence:
      ""U+0022U+0022

以下是文本值的示例:

"ABC" // the text value ABC

为文本值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x & y 串联
x ?? y Coalesce

文本值的本机类型是内部类型 text

二进制

二进制值表示字节序列。

虽然没有针对二进制值的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #binary 来构造二进制值。

以下示例从字节列表构造二进制值:

#binary( {0x00, 0x01, 0x02, 0x03} )

在二进制值上定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x >= y 大于或等于
x > y 大于
x < y 小于
x <= y 小于或等于
x ?? y Coalesce

二进制值的本机类型是内部类型二进制。

列表

列表值是在枚举时生成一系列值的值。 列表生成的值可以包含任何类型的值,包括列表。 可以使用初始化语法构造列表,如下所示:

list-expression:
      { item-listopt }
item-list:
      item
      item-list
,
item:
      表达式
      表达式
..

下面是一个 list-expression 示例,它定义了包含三个文本值的列表:"A""B""C"

{"A", "B", "C"}

"A" 是列表中的第一项,值 "C" 是列表中的最后一项。

  • 在访问列表项之前,不会对其计算。
  • 虽然使用 list 语法构造的列表值将按其在 item-list 中出现的顺序生成项,但通常,从库函数返回的列表在每次枚举时可能生成不同的集合或不同数量的值。

若要在列表中包含整数序列,可以使用 a..b 形式:

{ 1, 5..9, 11 }     // { 1, 5, 6, 7, 8, 9, 11 }

列表中的项数,即列表计数,可以使用 List.Count 函数来确定。

List.Count({true, false})  // 2 
List.Count({})             // 0

列表可以有效地包含无限个项;这样的列表的 List.Count 未定义,可能引发错误或无法终止。

如果列表不包含任何项,则将其称为空列表。 空列表写为:

{}  // empty list

为列表定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x & y Concatenate
x ?? y Coalesce

例如:

{1, 2} & {3, 4, 5}   // {1, 2, 3, 4, 5} 
{1, 2} = {1, 2}      // true 
{2, 1} <> {1, 2}     // true

列表值的本机类型是内部类型 list,它指定项类型 any

记录

记录值是字段的有序序列。 字段由字段名和字段值组成 。字段名是在记录中定义字段的独特文本值。 字段值可以是任何类型的值,包括记录。 可以使用初始化语法构造记录,如下所示:

record-expression:
      [ field-listopt ]
field-list:
      field
      field-list
,
字段中输入名称的配置设置:
      field-name
= 表达式
field-name:
      generalized-identifier
      quoted-identifier

下面的示例使用名为 x、值为 1 的字段和名为 y、值为 2 的字段构造记录。

[ x = 1, y = 2 ]

下面的示例构造了一条记录,其中包含一个名为 a、值为嵌套记录的 a 字段。 嵌套记录包含名为 b、值为 2 的字段。

[ a = [ b = 2 ] ]

在计算记录表达式时,以下条件适用:

  • 分配给每个字段名的表达式用于确定关联字段的值。

  • 如果分配给字段名的表达式在计算时生成一个值,则该值将成为结果记录的字段值。

  • 如果分配给字段名的表达式在计算时引发错误,将记录引发了错误这一事实,还将记录该字段以及引发的错误值。 对该字段的后续访问将导致重新引发具有记录的错误值的错误。

  • 表达式是在类似父环境的环境中计算的,只合并了与记录的每个字段(正在初始化的字段除外)的值相对应的变量。

  • 在访问相应字段之前,不会计算记录中的值。

  • 记录中的值最多计算一次。

  • 表达式的结果是包含空元数据记录的记录值。

  • 记录中字段的顺序由它们在 record-initializer-expression 中出现的顺序定义。

  • 指定的每个字段名在记录中都必须是唯一的,否则为错误。 使用序号比较来比较名称。

    [ x = 1, x = 2 ] // error: field names must be unique 
    [ X = 1, x = 2 ] // OK

不含字段的记录称为空记录,并按如下方式写入:

[] // empty record

虽然在访问一个字段或比较两个记录时,记录字段的顺序并不重要,但在其他上下文中(如枚举记录字段时),它是重要的。

获得字段时,这两条记录会产生不同的结果:

Record.FieldNames([ x = 1, y = 2 ]) // [ "x", "y" ] 
Record.FieldNames([ y = 1, x = 2 ]) // [ "y", "x" ]

可以使用 Record.FieldCount 函数确定记录中的字段数。 例如:

Record.FieldCount([ x = 1, y = 2 })  // 2 
Record.FieldCount([])                // 0

除了使用记录初始化语法 [ ],还可以从值列表、字段名列表或记录类型构造记录。 例如:

Record.FromList({1, 2}, {"a", "b"})

以上等效于:

[ a = 1, b = 2 ]

为记录值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x & y 合并​​
x ?? y Coalesce

以下示例说明了上述运算符。 请注意,如果字段名有重叠,记录合并使用来自右操作数的字段替代来自左操作数的字段。

[ a = 1, b = 2 ] & [ c = 3 ]    // [ a = 1, b = 2, c = 3 ] 
[ a = 1, b = 2 ] & [ a = 3 ]    // [ a = 3, b = 2 ] 
[ a = 1, b = 2 ] = [ b = 2, a = 1 ]         // true 
[ a = 1, b = 2, c = 3 ] <> [ a = 1, b = 2 ] // true

记录值的本机类型是内部类型 record,它指定一个打开的空字段列表。

表值是行的有序序列。 行是列值的有序序列。 表的类型决定了表中所有行的长度、表列的名称、表列的类型以及表键(如果有的话)的结构。

虽然没有针对表的文本语法,但提供了几个标准库函数来构造它们。 可以使用内部函数 #table 来构造表。

以下示例从列名列表和行列表来构造表。 生成的表将包含两列 type any 和三行。

#table({"x", "x^2"}, {{1,1}, {2,4}, {3,9}})

#table 也可用于指定完整表类型:

#table(
    type table [Digit = number, Name = text],  
    {{1,"one"}, {2,"two"}, {3,"three"}} 
    )

在这里,新表值具有指定列名和列类型的表类型。

为表值定义了以下运算符:

运算符 结果
x = y 等于
x <> y 不等于
x & y 串联
x ?? y Coalesce

表串联将对齐名称相似的列,并为仅出现在一个操作数表中的列填充 null。 下面的示例演示表串联:

  #table({"A","B"}, {{1,2}}) 
& #table({"B","C"}, {{3,4}})
A B C
1 2 null
null 3 4

表值的本机类型是一个自定义表类型(派生自内在类型 table),它列出列名,指定所有列类型为任意,并且没有键。 (有关表类型的详细信息,请转到表类型。)

函数

函数值是将一组参数映射到单个值的值。 函数值的详细信息在函数中描述。

类型

类型值是一个对其他值进行分类的值。 类型值的详细信息在类型中描述。