运算符行为

本部分定义各种 M 运算符的行为。

运算符优先级

如果某个表达式包含多个运算符,则运算符的优先顺序控制各个运算符的计算顺序。 例如,表达式 x + y * z 的计算结果为 x + (y * z),因为 * 运算符的优先级高于二进制 + 运算符。 运算符的优先级由其关联的文法产生式的定义来确定。 例如,additive-expression 由 +- 运算符分隔的一系列 multiplicative-expression 组成,因此 +- 运算符的优先级低于 */ 运算符 。

parenthesized-expression 产生式可用于更改默认优先级排序。

parenthesized-expression:
      ( 表达式 )

例如:

1 + 2 * 3       // 7 
(1 + 2) * 3     // 9

下表汇总了 M 运算符,并按从高到低的优先级顺序列出了运算符类别。 同一类别的运算符具有相等的优先级。

类别 表达式 说明
i
@i
标识符表达式
(x) 带圆括号表达式
x[i] LookUp
x{y} 项访问
x(...) 函数调用
{x, y, ...} 列表初始化
[ i = x, ... ] 记录初始化
... 未实现
一元 +x Identity
-x 否定
not x 逻辑非
元数据 x meta y 关联元数据
乘法性的 x * y 乘法
x / y 除法
累加性 x + y 加法
x - y 减法
关系 x < y 小于
x > y 大于
x <= y 小于或等于
x >= y 大于或等于
等式 x = y 等于
x <> y 不等于
类型断言 x as y 为兼容的可为 null 的基元类型或错误
类型一致性 x is y 测试是否为兼容的可为 null 的基元类型
逻辑与 x and y 短路合取
逻辑或 x or y 短路析取
Coalesce x ?? y Null 合并运算符

运算符和元数据

每个值都有一个关联的记录值,该记录值可以包含有关该值的其他信息。 此记录被称为值的元数据记录。 元数据记录可以与任何种类的值(甚至是 null)相关联。 此类关联的结果是具有给定元数据的新值。

元数据记录只是一个常规记录,可以包含常规记录可包含的任何字段和值,且其本身具有元数据记录。 将元数据记录与值关联是一种非侵入性行为。 此操作不会更改值在计算中的行为,除非是显式检查元数据记录。

每个值都有默认的元数据记录,即使是未指定的值也是如此。 默认元数据记录为空。 以下示例演示如何使用 Value.Metadata 标准库函数访问文本值的元数据记录:

Value.Metadata( "Mozart" )   // []

当值与构造新值的运算符或函数一起使用时,通常不保留元数据记录。 例如,如果使用 & 运算符来连接两个文本值,那么生成的文本值的元数据为空记录 []。 以下表达式等效:

"Amadeus " & ("Mozart" meta [ Rating = 5 ])  
"Amadeus " & "Mozart"

标准库函数 Value.RemoveMetadataValue.ReplaceMetadata 可用于从值中删除所有元数据,并替换值的元数据(而不是将元数据合并到可能已存在的元数据中)。

返回带有元数据的结果的唯一运算符是元运算符

结构递归运算符

值可以循环。 例如:

let l = {0, @l} in l
// {0, {0, {0, ... }}}
[A={B}, B={A}]
// [A = {{ ... }}, B = {{ ... }}]

M 通过将记录、列表和表的结构保持为延迟来处理循环值。 如果尝试构造循环值,而该值并不会得益于突然插入的结构延迟的值,则会生成错误:

[A=B, B=A] 
// [A = Error.Record("Expression.Error", 
//         "A cyclic reference was encountered during evaluation"), 
//  B = Error.Record("Expression.Error", 
//         "A cyclic reference was encountered during evaluation"), 
// ]

M 中的一些运算符由结构递归定义。 例如,记录和列表的相等性分别由相应的记录字段和项列表的结合相等性定义。

对于非循环值,应用结构递归会生成值的有限扩展:将重复遍历共享嵌套值,但递归过程始终终止。

在应用结构递归时,循环值存在无限扩展。 M 的语义不会针对此类无限扩展作出任何特殊适应,例如,尝试比较循环值的相等性通常会耗尽资源并异常终止。

选择和投影运算符

选择和投影运算符允许从列表和记录值中提取数据。

项访问

可使用 item-access-expression,根据某个值在列表或表中从零开始的位置从该列表或表中选择该值。

item-access-expression:
      item-selection
      optional-item-selection
item-selection:
      primary-expression
{ item-selector }
optional-item-selection:
      primary-expression
{ item-selector } ?
item-selector:
      expression

item-access-expression x{y} 返回

  • 对于列表 x 和数字 y,列表 x 的项位于 y。 列表的第一项被视为具有序号索引零。 如果列表中不存在请求的位置,则会引发错误。

  • 对于表 x 和数字 y,表 x 的行位于 y。 表的第一行被视为具有序号索引零。 如果表中不存在请求的位置,则会引发错误。

  • 对于表 x 和记录 y,表 x 的行与字段记录 y 的字段值匹配,并且字段名称与相应的表列名称匹配。 如果表中没有唯一的匹配行,则会引发错误。

例如:

{"a","b","c"}{0}                        // "a" 
{1, [A=2], 3}{1}                        // [A=2] 
{true, false}{2}                        // error 
#table({"A","B"},{{0,1},{2,1}}){0}      // [A=0,B=1] 
#table({"A","B"},{{0,1},{2,1}}){[A=2]}  // [A=2,B=1]  
#table({"A","B"},{{0,1},{2,1}}){[B=3]}  // error 
#table({"A","B"},{{0,1},{2,1}}){[B=1]}  // error

item-access-expression 还支持 x{y}? 形式,如果列表或表 x 中不存在位置(或匹配项)y,这种形式将返回 null。 如果存在多个 y 匹配项,仍会引发错误。

例如:

{"a","b","c"}{0}?                       // "a" 
{1, [A=2], 3}{1}?                       // [A=2] 
{true, false}{2}?                       // null 
#table({"A","B"},{{0,1},{2,1}}){0}?     // [A=0,B=1] 
#table({"A","B"},{{0,1},{2,1}}){[A=2]}? // [A=2,B=1]  
#table({"A","B"},{{0,1},{2,1}}){[B=3]}? // null 
#table({"A","B"},{{0,1},{2,1}}){[B=1]}? // error

项访问不会强制对列表或表项(正在访问的项除外)进行求值。 例如:

{ error "a", 1, error "c"}{1}  // 1 
{ error "a", error "b"}{1}     // error "b"

对项访问运算符 x{y} 求值时,存在以下情况:

  • 传播在计算表达式 xy 期间引发的错误。

  • 表达式 x 生成列表或表值。

  • 表达式 y 生成数值,或在 x 生成表值时生成记录值。

  • 如果 y 生成数值,并且 y 的值为负,则会引发一个原因代码为 "Expression.Error" 的错误。

  • 如果 y 生成数值,并且 y 的值大于或等于 x 的计数,则除非使用了可选运算符形式 x{y}?(在这种情况下,将返回值 null),否则将引发原因代码为 "Expression.Error" 的错误。

  • 如果 x 生成表值,y 生成记录值,并且 x 中的 y 没有匹配项,则除非使用了可选运算符形式 x{y}?(在这种情况下,将返回值 null),否则将引发原因代码为 "Expression.Error" 的错误。

  • 如果 x 生成表值,y 生成记录值,并且 x 中的 y 具有多个匹配项,则会引发原因代码为 "Expression.Error" 的错误。

在项选择过程中,不会计算 x 中的任何项(位于 y 位置的项除外)。 (对于流式处理列表或表,会跳过位置 y 前面的项或行,这可能会导致计算这些项或行,具体取决于列表或表的源。)

字段访问

field-access-expression 用于从记录中选择值,或将记录或表分别投影到具有较少字段或列的记录或表 。

field-access-expression:
      field-selection
      implicit-target-field-selection
      投影 (projection)
      implicit-target-projection
field-selection:
      primary-expression field-selector
field-selector:
      required-field-selector
      optional-field-selector
required-field-selector:

      [ field-name ]
optional-field-selector:
      [ field-name ] ?
field-name:
      generalized-identifier
      quoted-identifier
implicit-target-field-selection:
      field-selector
projection:
      primary-expression required-projection
      primary-expression optional-projection
required-projection:

      [ required-selector-list ]
optional-projection:
      [ required-selector-list ] ?
required-selector-list:
      required-field-selector
      required-selector-list
, required-field-selector
implicit-target-projection:
      required-projection
      optional-projection

字段访问的最简单形式是必需字段选择。 它使用运算符 x[y] 按字段名称在记录中查找字段。 如果 x 中不存在字段 y,则会引发错误。 x[y]? 形式用于执行可选字段选择,如果记录中不存在请求的字段,则返回 null

例如:

[A=1,B=2][B]       // 2 
[A=1,B=2][C]       // error 
[A=1,B=2][C]?      // null

对于必需记录投影和可选记录投影,运算符支持对多个字段进行集体访问 。 运算符 x[[y1],[y2],...] 将记录投影到具有更少字段的新记录(通过 y1y2... 选择)。 如果不存在选定字段,则会引发错误。 运算符 x[[y1],[y2],...] 将记录投影到具有通过 y1y2... 选择的字段的新记录:如果某一字段缺失,则改用 null。 例如:

[A=1,B=2][[B]]           // [B=2] 
[A=1,B=2][[C]]           // error 
[A=1,B=2][[B],[C]]?      // [B=2,C=null]

支持将形式 [y][y]? 作为对标识符 _(下划线)的速记引用。 以下两个表达式等效:

[A]                 
_[A]

以下示例演示了字段访问的速记形式:

let _ = [A=1,B=2] in [A] //1

还支持将形式 [[y1],[y2],...][[y1],[y2],...]? 作为速记,以下两个表达式同样等效:

[[A],[B]]                 
_[[A],[B]]

在结合使用 each 速记(一种引入单个参数 _ 的函数的方法)时,速记形式尤其有用(有关详细信息,请参阅简化的声明)。 这两种速记共同简化了常见的高阶函数表达式:

List.Select( {[a=1, b=1], [a=2, b=4]}, each [a] = [b]) 
// {[a=1, b=1]}

上面的表达式与以下这种看上去更为费解的普通写法等效:

List.Select( {[a=1, b=1], [a=2, b=4]}, (_) => _[a] = _[b]) 
// {[a=1, b=1]}

字段访问不会强制对字段(正在访问的字段除外)进行求值。 例如:

[A=error "a", B=1, C=error "c"][B]  // 1 
[A=error "a", B=error "b"][B]       // error "b"

对字段访问运算符 x[y]x[y]?x[[y]]x[[y]]? 求值时,存在以下情况:

  • 传播在计算表达式 x 期间引发的错误。

  • 计算字段 y 时引发的错误与字段 y 永久关联,然后进行传播。 将来对字段 y 进行的任何访问都将引发相同的错误。

  • 表达式 x 生成记录或表值,或引发错误。

  • 如果标识符 y 命名 x 中不存在的字段,则除非使用了可选运算符形式 ...?(在这种情况下,将返回值 null),否则将引发原因代码为 "Expression.Error" 的错误。

在字段访问过程中,不会计算 x 的任何字段(由 y 命名的字段除外)。

元数据运算符

值的元数据记录使用元运算符 (x meta y) 进行修正。

metadata-expression:
      unary-expression
      unary-expression
meta unary-expression

以下示例使用 meta 运算符构造一个包含元数据记录的文本值,然后使用 Value.Metadata 访问生成的值的元数据记录:

Value.Metadata( "Mozart" meta [ Rating = 5 ] ) 
// [Rating = 5 ]
Value.Metadata( "Mozart" meta [ Rating = 5 ] )[Rating] 
// 5

在应用元数据组合运算符 x meta y 时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • y 表达式必须为记录,否则将引发原因代码为 "Expression.Error" 的错误。

  • 生成的元数据记录是与 y 合并的 x 的元数据记录。 (有关记录合并的语义,请参阅记录合并。)

  • 生成的值是 x 表达式中的值(不包含其元数据),并且附加了新计算的元数据记录。

标准库函数 Value.RemoveMetadataValue.ReplaceMetadata 可用于从值中删除所有元数据,并替换值的元数据(而不是将元数据合并到可能已存在的元数据中)。 以下表达式等效:

x meta y  
Value.ReplaceMetadata(x, Value.Metadata(x) & y) 
Value.RemoveMetadata(x) meta (Value.Metadata(x) & y)

相等运算符

相等运算符 = 用于确定两个值是否相等。 不相等运算符 <> 用于确定两个值是否不相等

equality-expression:
      relational-expression
      relational-expression
= equality-expression
      relational-expression
<> equality-expression

例如:

1 = 1            // true 
1 = 2            // false 
1 <> 1           // false 
1 <> 2           // true 
null = true      // false 
null = null      // true

元数据不属于相等比较或不相等比较。 例如:

(1 meta [ a = 1 ]) = (1 meta [ a = 2 ]) // true 
(1 meta [ a = 1 ]) = 1                  // true

在应用相等运算符 x = yx <> y 时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • 如果值相等,则 = 运算符的结果为 true,否则为 false

  • 如果值相等,则 <> 运算符的结果为 false,否则为 true

  • 比较中不包含元数据记录。

  • 如果 xy 表达式的计算结果不是同一类值,则这些值不相等。

  • 如果 xy 表达式的计算结果是同一类值,则可以应用一些特定的规则来确定它们是否相等,定义如下。

  • 以下情况始终存在:

    (x = y) = not (x <> y)

相等运算符针对以下类型定义:

  • null 值仅等于其自身。
    null = null    // true 
    null = true    // false 
    null = false   // false
  • 逻辑值 truefalse 仅等于其自身。 例如:
    true = true      // true 
    false = false    // true 
    true = false     // false 
    true = 1         // false
  • 使用指定的精度比较数字:

    • 如果任一数字为 #nan,则这两个数字不相同。

    • 如果这两个数字都不为 #nan,则将使用数值的按位比较对这两个数字进行比较。

    • #nan 是唯一不等于其自身的值。

      例如:

        1 = 1,              // true 
        1.0 = 1             // true 
        2 = 1               // false 
        #nan = #nan         // false 
        #nan <> #nan        // true
  • 如果两个持续时间均表示 100 毫微秒的时间刻度,则这两个持续时间相等。

  • 如果两个时间的各个部分(时、分、秒)的度量值相等,则这两个时间相等。

  • 如果两个日期的各个部分(年、月、日)的度量值相等,则这两个日期相等。

  • 如果两个日期时间的各个部分(年、月、日、时、分、秒)的度量值相等,则这两个日期时间相等。

  • 如果两个 DateTimeZone 时区的相应 UTC 日期时间相等,则这两个 DateTimeZone 相等。 要到达相应的 UTC 日期时间,需要从 DateTimeZone 的日期时间部分减去小时/分钟偏移量。

  • 如果使用序号、区分大小写和不区分区域性的比较,两个文本值在相应位置具有相同长度和相同字符,则这两个文本值相等。

  • 如果满足以下所有条件,则两个列表值相等:

    • 这两个列表均包含相同数量的项。

    • 列表中每个值在位置上相对应的项都相等。 这意味着,不仅列表需要包含相等的项,而且这些项需要采用相同的顺序。

      例如:

        {1, 2} = {1, 2}     // true 
        {2, 1} = {1, 2}     // false 
        {1, 2, 3} = {1, 2}  // false
      
  • 如果满足以下所有条件,则两条记录相等:

    • 字段数相同。

    • 一条记录的每个字段名称也出现在另一条记录中。

    • 一条记录的每个字段的值等于另一条记录中名称相同的字段。

      例如:

        [ A = 1, B = 2 ] = [ A = 1, B = 2 ]        // true 
        [ B = 2, A = 1 ] = [ A = 1, B = 2 ]        // true 
        [ A = 1, B = 2, C = 3 ] = [ A = 1, B = 2 ] // false 
        [ A = 1 ] = [ A = 1, B = 2 ]               // false
      
  • 如果满足以下所有条件,则两个表相等:

    • 列数相同。

    • 一个表中的每个列名称也出现在另一个表中。

    • 行数相同。

    • 每行的相应单元格中的值相等。

      例如:

        #table({"A","B"},{{1,2}}) = #table({"A","B"},{{1,2}}) // true 
        #table({"A","B"},{{1,2}}) = #table({"X","Y"},{{1,2}}) // false 
        #table({"A","B"},{{1,2}}) = #table({"B","A"},{{2,1}}) // true
      
  • 函数值等于其自身,但可能等于也可能不等于另一函数值。 如果两个函数值被视为相等,则调用时它们将具有相同的行为。

    两个给定函数值将始终具有相同的相等关系。

  • 类型值等于其自身,但可能等于也可能不等于另一类型值。 如果两个类型值被视为相等,则查询一致性时它们将具有相同的行为。

    两个给定类型值将始终具有相同的相等关系。

关系运算符

<><=>= 运算符称为关系运算符。

relational-expression:
      additive-expression
      additive-expression
< relational-expression
      additive-expression
> relational-expression
      additive-expression
<= relational-expression
      additive-expression
>= relational-expression

这些运算符用于确定两个值之间的相对排序关系,如下表所示:

操作 结果
x < y 如果 x 小于 y,则为 true;否则为 false
x > y 如果 x 大于 y,则为 true;否则为 false
x <= y 如果 x 小于等于 y,则为 true;否则为 false
x >= y 如果 x 大于等于 y,则为 true;否则为 false

例如:

0 <= 1            // true 
null < 1          // null 
null <= null      // null 
"ab" < "abc"      // true 
#nan >= #nan      // false  
#nan <= #nan      // false

在计算包含关系运算符的表达式时,存在以下情况:

  • 传播在计算 xy 操作数表达式时引发的错误。

  • 通过计算 xy 表达式而生成的值必须是二进制、日期、日期时间、datetimezone、持续时间、逻辑、数字、null、文本或时间值。 否则,将引发原因代码为 "Expression.Error" 的错误。

  • 两个操作数必须是相同类型的值或都为 null。 否则,将引发原因代码为 "Expression.Error" 的错误。

  • 如果其中一个操作数或两个操作数都为 null,则结果为 null 值。

  • 两个二进制值进行逐字节比较。

  • 通过比较两个日期的年份部分(如果相等,则比较月份部分,如果月份部分也相等,则比较日部分)来比较这两个日期。

  • 通过比较两个日期时间的年份部分(如果相等,则比较月份部分,如果月份部分也相等,则比较日部分,如果日部分也相等,则比较小时部分,如果小时部分也相等,则比较分钟部分,如果分钟部分也相等,则比较秒数部分)来比较这两个日期时间。

  • 通过减去两个 datetimezone 的小时/分钟偏移量将其规范化为 UTC,然后比较其日期时间部分,来比较这两个 datetimezone。

  • 两个持续时间将根据它们所表示的 100 毫微秒时间刻度的总数来进行比较。

  • 比较了两个逻辑,true 被视为大于 false

  • 根据 IEEE 754 标准的规则比较 xy 这两个数字:

    • 如果任一操作数为 #nan,则所有关系运算符的结果都为 false

    • 当两个操作数都不是 #nan 时,运算符比较两个 floatingpoint 操作数相对于排序 -∞ < -max < ... < -min < -0.0 = +0.0 < +min < ... < +max < +∞ 的值,其中 min 和 max 是可表示的最小和最大正有限值。 -∞ 和 +∞ 的 M 名称是 -#infinity#infinity

      此排序的明显效果是:

      • 负零和正零被视为相等。

      • -#infinity 值被视为小于其他所有数值,但等于另一 -#infinity

      • #infinity 值被视为大于其他所有数值,但等于另一 #infinity

  • 使用逐字符序号、区分大小写和不区分区域性的比较方式,对两个文本进行了对比。

  • 通过比较两个时间的小时部分(如果相等,则比较分钟部分,如果分钟部分也相等,则比较秒数部分)来比较这两个时间。

条件逻辑运算符

andor 运算符称为条件逻辑运算符。

logical-or-expression:
      logical-and-expression
logical-and-expression
or logical-or-expression
logical-and-expression:
      is-expression
      is-expression
and logical-and-expression

or 运算符在至少一个操作数为 true 时返回 true。 当且仅当左操作数不为 true 时,才计算右操作数。

and 运算符在至少一个操作数为 false 时返回 false。 当且仅当左操作数不为 false 时,才计算右操作数。

下面显示了 orand 运算符的真值表,其中包含对垂直轴上左操作数表达式的求值结果和对水平轴上右操作数表达式的求值结果。

and true false null error
true true false null error
false false false false false
null null false null error
error error error error error
or true false null error
or true false null error
true true true true true
false true false null error
null true null null error
error error error error error

在计算包含条件逻辑运算符的表达式时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • 条件逻辑运算符通过类型 logicalnull 定义。 如果操作数的值不是这些类型,则会引发原因代码为 "Expression.Error" 的错误。

  • 结果为逻辑值。

  • xy 表达式中,当且仅当 x 的计算结果不为 true 时,才会计算 y 表达式。

  • xy 表达式中,当且仅当 x 的计算结果不为 false 时,才会计算 y 表达式。

最后两个属性赋予了条件逻辑运算符“条件”资格;属性也称为“短路”。 这些属性对于编写精简的受保护的谓词非常有用。 例如,以下表达式等效:

d <> 0 and n/d > 1 if d <> 0 then n/d > 1 else false

算术运算符

+-*/ 运算符是算术运算符。

additive-expression:
      multiplicative-expression
      additive-expression
+ multiplicative-expression
      additive-expression
- multiplicative-expression
multiplicative-expression:
      metadata- expression
      multiplicative-expression
* metadata-expression
      multiplicative-expression
/ metadata-expression

Precision

M 中的数字使用多种表示形式进行存储,以尽可能多地保留来自各种源的数字相关信息。 应用于数字的运算符只会根据需要将数字从一种表示形式转换为另一种表示形式。 M 支持两种精度:

精度 语义
Precision.Decimal 128 位十进制表示形式,范围为 ±1.0 x 10-28 至 ±7.9 x 1028,有效位数为 28-29。
Precision.Double 使用尾数和指数的科学表示形式;符合 64 位二进制双精度 IEEE 754 算术标准 IEEE 754-2008

执行算术运算的方式如下:选择一个精度,将两个操作数都转换为该精度(如有必要),然后执行实际运算,最后返回一个采用所选精度的数字。

内置算术运算符(+-*/)使用双精度。 标准库函数(Value.AddValue.SubtractValue.MultiplyValue.Divide)可用于使用特定精度模型请求这些操作。

  • 不可能出现数值溢出:#infinity-#infinity 表示因度量值过大而无法表示的值。

  • 不可能出现数值下溢:0-0 表示因度量值过小而无法表示的值。

  • IEEE 754 特殊值 #nan(NaN 不是数字)用于涵盖在算数上无效的案例,例如零除以零。

  • 从十进制精度到双精度的转换通过将十进制数字舍入到最接近的等效双精度值来实现。

  • 从双精度到十进制精度的转换通过将双精度数字舍入到最接近的等效十进制值(如有必要,溢出到 #infinity-#infinity 值)来实现。

加法运算符

加法运算符 (x + y) 的解释取决于计算表达式 x 和 y 所得到的值的类型,如下所示:

x y 结果 解释
type number type number type number 数值求和
type number null null
null type number null
type duration type duration type duration 度量值的数值求和
type duration null null
null type duration null
type 日期时间 type duration type 日期时间 持续时间的日期时间偏移
type duration type 日期时间 type 日期时间
type 日期时间 null null
null type 日期时间 null

在表中,type 日期时间代表 type datetype datetimetype datetimezonetype time 中的任何一项。 在添加某些日期时间类型的持续时间和值时,生成的值为相同类型。

对于表中所列值之外的其他值组合,将引发原因代码为 "Expression.Error" 的错误。 以下各部分将介绍每种组合。

传播在计算任一操作数时引发的错误。

数值求和

两个数字的求和使用加法运算符进行计算,并生成一个数字。

例如:

1 + 1             // 2 
#nan + #infinity  // #nan

对数字采用的加法运算符 + 使用双精度;标准库函数 Value.Add 可用于指定十进制精度。 计算数字之和时,存在以下情况:

  • 采用双精度的求和根据 64 位二进制双精度 IEEE 754 算法 IEEE 754-2008 的规则计算得出。 下表列出了非零有限值、零、无限值和 NaN 的所有可能组合的结果。 在表中,xy 是非零有限值,zx + y 的结果。 如果 xy 度量值相同但异号,则 z 为正零。 如果 x + y 由于过大而无法采用目标类型表示,则 z 为与 x + y 具有相同符号的无穷值。

    + y +0 -0 +∞ -∞ NaN
    x z x x +∞ -∞ NaN
    +0 y +0 +0 +∞ -∞ NaN
    -0 y +0 -0 +∞ -∞ NaN
    +∞ +∞ +∞ +∞ +∞ NaN NaN
    -∞ -∞ -∞ -∞ NaN -∞ NaN
    NaN NaN NaN NaN NaN NaN NaN
  • 采用十进制精度计算总和时不会丢失精度。 结果的小数位数是两个操作数的小数位数中较大的一个。

持续时间之和

两个持续时间之和是指这两个持续时间所表示的 100 毫微秒时间刻度数的总和。 例如:

#duration(2,1,0,15.1) + #duration(0,1,30,45.3) 
// #duration(2, 2, 31, 0.4)

持续时间的日期时间偏移

可使用 x + y 添加日期时间 x 和持续时间 y,以计算其与线性时间线上 x 的距离的度良值完全等同于 y 的新日期时间。 此处,日期时间表示 DateDateTimeDateTimeZoneTime 中的任何一项,并且非 null 结果将为同一类型。 可按如下所示计算持续时间的日期时间偏移:

  • 如果指定了时期值后的日期时间天数,请使用以下信息元素构造新的日期时间:

    • 计算时期后的新天数,相当于将 y 的度量值除以 24 小时内的 100 毫微秒时间刻度数,然后截断结果的小数部分,并将此值添加到时期后的 x 天数。

    • 计算午夜后的新时间刻度,相当于将 y 的量级与午夜后 x 的时间刻度相加,除以 24 小时内的 100 毫微秒时间刻度数。 如果 x 自午夜后未指定任何时间刻度值,则假定一个值 0。

    • 复制 x 的值,表示相对于 UTC 的分钟偏移量未更改。

  • 如果未指定时期值后的日期时间天数,请使用以下指定的信息元素构造新的日期时间:

    • 计算午夜后的新时间刻度,相当于将 y 的量级与午夜后 x 的时间刻度相加,除以 24 小时内的 100 毫微秒时间刻度数。 如果 x 自午夜后未指定任何时间刻度值,则假定一个值 0。

    • 复制 x 的值,表示 epoch 后的天数和相对于 UTC 的分钟偏移量未更改。

以下示例显示如何在日期时间指定时期后的天数时,计算绝对时间总和:

#date(2010,05,20) + #duration(0,8,0,0) 
    //#datetime( 2010, 5, 20, 8, 0, 0 ) 
    //2010-05-20T08:00:00 
 
#date(2010,01,31) + #duration(30,08,0,0) 
    //#datetime(2010, 3, 2, 8, 0, 0) 
    //2010-03-02T08:00:00 
 
#datetime(2010,05,20,12,00,00,-08) + #duration(0,04,30,00) 
    //#datetime(2010, 5, 20, 16, 30, 0, -8, 0) 
    //2010-05-20T16:30:00-08:00 
 
#datetime(2010,10,10,0,0,0,0) + #duration(1,0,0,0) 
   //#datetime(2010, 10, 11, 0, 0, 0, 0, 0) 
   //2010-10-11T00:00:00+00:00

以下示例显示如何计算给定时间的持续时间的日期时间偏移:

#time(8,0,0) + #duration(30,5,0,0) 
   //#time(13, 0, 0) 
   //13:00:00

减法运算符

减法运算符 (x - y) 的解释取决于计算表达式 xy 所得到的值的类型,如下所示:

x Y 结果 解释
type number type number type number 数值差
type number null null
null type number null
type duration type duration type duration 度量值的数值差
type duration null null
null type duration null
type 日期时间 type 日期时间 type duration 日期时间之间的持续时间
type 日期时间 type duration type 日期时间 求反持续时间的日期时间偏移
type 日期时间 null null
null type 日期时间 null

在表中,type 日期时间代表 type datetype datetimetype datetimezonetype time 中的任何一项。 在从某些日期时间类型的值中减去持续时间时,生成的值为相同类型。

对于表中所列值之外的其他值组合,将引发原因代码为 "Expression.Error" 的错误。 以下各部分将介绍每种组合。

传播在计算任一操作数时引发的错误。

数值差

两个数字之间的差使用减法运算符进行计算,并生成一个数字。 例如:

1 - 1                // 0 
#nan - #infinity     // #nan

对数字采用的减法运算符 - 使用双精度;标准库函数 Value.Subtract 可用于指定十进制精度。 计算数字之差时,存在以下情况:

  • 采用双精度的求差根据 64 位二进制双精度 IEEE 754 算法 IEEE 754-2008 的规则计算得出。 下表列出了非零有限值、零、无限值和 NaN 的所有可能组合的结果。 在表中,xy 是非零有限值,zx - y 的结果。 如果 xy 相等,则 z 为正零。 如果 x - y 由于过大而无法采用目标类型表示,则 z 为与 x - y 具有相同符号的无穷值。

    - y +0 -0 +∞ -∞ NaN
    x z x x -∞ +∞ NaN
    +0 -y +0 +0 -∞ +∞ NaN
    -0 -y -0 +0 -∞ +∞ NaN
    +∞ +∞ +∞ +∞ NaN +∞ NaN
    -∞ -∞ -∞ -∞ -∞ NaN NaN
    NaN NaN NaN NaN NaN NaN NaN
  • 采用十进制精度计算差值时不会丢失精度。 结果的小数位数是两个操作数的小数位数中较大的一个。

持续时间之差

两个持续时间之差是指每个持续时间所表示的 100 毫微秒时间刻度数之间的差值。 例如:

#duration(1,2,30,0) - #duration(0,0,0,30.45) 
// #duration(1, 2, 29, 29.55)

求反持续时间的日期时间偏移

可使用 x - y 减去日期时间 x 和持续时间 y,以计算新的日期时间。 此处,日期时间表示 datedatetimedatetimezonetime 中的任何一项。 生成的日期时间与线性时间线上 x 的距离的度量值与 y 完全相同,方向与 y 符号相反。 减去正数持续时间将生成在时间上相对于 x 向后推移的结果,而减去负值将生成在时间上向前推移的结果。

#date(2010,05,20) - #duration(00,08,00,00) 
   //#datetime(2010, 5, 19, 16, 0, 0) 
   //2010-05-19T16:00:00 
#date(2010,01,31) - #duration( 30,08,00,00) 
   //#datetime(2009, 12, 31, 16, 0, 0) 
   //2009-12-31T16:00:00

两个日期时间之间的持续时间

可使用 t - u 将两个日期时间 tu 相减,以计算它们之间的持续时间。 此处,日期时间表示 datedatetimedatetimezonetime 中的任何一项。 从 t 中减去 u 所得到的持续时间在添加 u 时必须生成 t

#date(2010,01,31) - #date(2010,01,15) 
// #duration(16,00,00,00) 
// 16.00:00:00 
 
#date(2010,01,15)- #date(2010,01,31) 
// #duration(-16,00,00,00) 
// -16.00:00:00 
 
#datetime(2010,05,20,16,06,00,-08,00) - 
#datetime(2008,12,15,04,19,19,03,00) 
// #duration(521,22,46,41)
// 521.22:46:41

u > t 结果为负持续时间时,减去 t - u

#time(01,30,00) - #time(08,00,00) 
// #duration(0, -6, -30, 0)

使用 t - u 将两个日期时间相减时,存在以下情况:

  • u + (t - u) = t

乘法运算符

乘法运算符 (x * y) 的解释取决于计算表达式 x 和 y 所得到的值的类型,如下所示:

X Y 结果 解释
type number type number type number 数值乘积
type number null null
null type number null
type duration type number type duration 持续时间倍数
type number type duration type duration 持续时间倍数
type duration null null
null type duration null

对于表中所列值之外的其他值组合,将引发原因代码为 "Expression.Error" 的错误。 以下各部分将介绍每种组合。

传播在计算任一操作数时引发的错误。

数值乘积

两个数字的乘积使用乘法运算符进行计算,并生成一个数字。 例如:

2 * 4                // 8 
6 * null             // null 
#nan * #infinity     // #nan

对数字采用的乘法运算符 * 使用双精度;标准库函数 Value.Multiply 可用于指定十进制精度。 计算数字的乘积时,存在以下情况:

  • 采用双精度的求积根据 64 位二进制双精度 IEEE 754 算法 IEEE 754-2008 的规则计算得出。 下表列出了非零有限值、零、无限值和 NaN 的所有可能组合的结果。 在表中,xy 为正有限值。 x * y 的结果为 z。 如果结果相对于目标类型而言太大,则 z 为无穷值。 如果结果相对于目标类型而言太小,则 z 为零。

    * +y -y +0 -0 +∞ -∞ NaN
    +x +z -Z +0 -0 +∞ -∞ NaN
    -x -Z +z -0 +0 -∞ +∞ NaN
    +0 +0 -0 +0 -0 NaN NaN NaN
    -0 -0 +0 -0 +0 NaN NaN NaN
    +∞ +∞ -∞ NaN NaN +∞ -∞ NaN
    -∞ -∞ +∞ NaN NaN -∞ +∞ NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • 采用十进制精度计算乘积时不会丢失精度。 结果的小数位数是两个操作数的小数位数中较大的一个。

持续时间的倍数

持续时间和数字的乘积是指由持续时间操作数乘以数字操作数表示的 100 毫微秒时间刻度数。 例如:

#duration(2,1,0,15.1) * 2 
// #duration(4, 2, 0, 30.2)

除法运算符

除法运算符 (x / y) 的解释取决于计算表达式 xy 所得到的值的类型,如下所示:

X Y 结果 解释
type number type number type number 数值商
type number null null
null type number null
type duration type number type duration 持续时间的分数
type duration type duration type number 持续时间的数值商
type duration null null
null type duration null

对于表中所列值之外的其他值组合,将引发原因代码为 "Expression.Error" 的错误。 以下各部分将介绍每种组合。

传播在计算任一操作数时引发的错误。

数值商

两个数字的商使用除法运算符进行计算,并生成一个数字。 例如:

8 / 2               // 4 
8 / 0               // #infinity 
0 / 0               // #nan 
0 / null            // null 
#nan / #infinity    // #nan

对数字采用的除法运算符 / 使用双精度;标准库函数 Value.Divide 可用于指定十进制精度。 计算数字的商时,存在以下情况:

  • 采用双精度的求商根据 64 位二进制双精度 IEEE 754 算法 IEEE 754-2008 的规则计算得出。 下表列出了非零有限值、零、无限值和 NaN 的所有可能组合的结果。 在表中,xy 为正有限值。 x / y 的结果为 z。 如果结果相对于目标类型而言太大,则 z 为无穷值。 如果结果相对于目标类型而言太小,则 z 为零。

    / +y -y +0 -0 +∞ -∞ NaN
    +x +z -Z +∞ -∞ +0 -0 NaN
    -x -Z +z -∞ +∞ -0 +0 NaN
    +0 +0 -0 NaN NaN +0 -0 NaN
    -0 -0 +0 NaN NaN -0 +0 NaN
    +∞ +∞ -∞ +∞ -∞ NaN NaN NaN
    -∞ -∞ +∞ -∞ +∞ NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • 采用十进制精度计算总和时不会丢失精度。 结果的小数位数是两个操作数的小数位数中较大的一个。

持续时间之商

两个持续时间之商是指这两个持续时间所表示的 100 毫微秒时间刻度数的商。 例如:

#duration(2,0,0,0) / #duration(0,1,30,0) 
// 32

缩放的持续时间

持续时间 x 和数字 y 的商是表示商的持续时间,该商是由持续时间 x 和数字 y 所表示的 100 毫微秒时间刻度数的商。 例如:

#duration(2,0,0,0) / 32 
// #duration(0,1,30,0)

结构组合

组合运算符 (x & y) 通过以下类型的值定义:

X Y 结果 解释
type text type text type text 串联
type text null null
null type text null
type date type time type datetime 合并​​
type date null null
null type time null
type list type list type list 串联
type record type record type record 合并​​
type table type table type table 串联

串联

可使用 x & y 串联两个文本、两个列表或两个表值。

下面的示例演示文本值串联:

"AB" & "CDE"     // "ABCDE"

下面的示例演示列表串联:

{1, 2} & {3}     // {1, 2, 3}

在使用 x & y 串联两个值时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • 如果 xy 的项包含错误,则不会传播任何错误。

  • 串联两个文本值将生成一个文本值,该值包含 x 的值,随后紧跟 y 的值。 如果其中一个操作数为 null,另一个为文本值,则结果为 null。

  • 串联两个列表将生成一个列表,该列表包含 x 的所有项,后跟 y 的所有项。

  • 串联两个表将生成一个表,该表包含这两个操作数表的所有列。 保留 x 的列排序,后跟仅显示在 y 中的列,保留其相对顺序。 对于仅出现在其中一个操作数中的列,null 用于填充另一个操作数的单元格值。

合并​​

记录合并

可使用 x & y 来合并两条记录,并生成一条记录,其中包括来自 xy 的字段。

以下示例说明了如何合并记录:

[ x = 1 ] & [ y = 2 ]                // [ x = 1, y = 2 ] 
[ x = 1, y = 2 ] & [ x = 3, z = 4 ]  // [ x = 3, y = 2, z = 4 ]

在使用 x + y 合并两条记录时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • 如果某一字段同时出现在 xy 中,则使用 y 中的值。

  • 生成的记录中字段的顺序为 x,后跟 y 中不属于 x 的字段,其顺序与它们在 y 中的显示顺序相同。

  • 合并记录不会导致对值进行计算。

  • 由于字段包含错误,因此不会引发错误。

  • 结果是一条记录。

日期时间合并

可使用 x & y 合并日期 x 与时间 y,并生成一个将 xy 中的各个部分组合在一起的日期时间。

以下示例演示了如何合并日期和时间:

#date(2013,02,26) & #time(09,17,00) 
// #datetime(2013,02,26,09,17,00)

在使用 x + y 合并两条记录时,存在以下情况:

  • 传播在计算 xy 表达式时引发的错误。

  • 结果是一个日期时间。

一元运算符

+-not 运算符是一元运算符。

unary-expression:
      type-expression

      + 一元表达式
      - 一元表达式
      not 一元表达式

一元加运算符

一元加运算符 (+x) 针对以下类型的值定义:

X 结果 解释
type number type number 一元加
type duration type duration 一元加
null `null

对于其他值,将引发原因代码为 "Expression.Error" 的错误。

一元加运算符允许将 + 符号应用于数字、日期时间或 null 值。 结果是该相同值。 例如:

+ - 1                 // -1 
+ + 1                 // 1 
+ #nan                // #nan 
+ #duration(0,1,30,0) // #duration(0,1,30,0)

在计算一元加运算符 +x 时,存在以下情况:

  • 传播在计算 x 时引发的错误。

  • 如果计算 x 所得到的结果不是数字值,则会引发原因代码为 "Expression.Error" 的错误。

一元减运算符

一元减运算符 (-x) 针对以下类型的值定义:

X 结果 解释
type number type number 否定
type duration type duration 否定
null null

对于其他值,将引发原因代码为 "Expression.Error" 的错误。

一元减运算符用于更改数字或持续时间的符号。 例如:

- (1 + 1)       // -2 
- - 1           // 1 
- - - 1         // -1 
- #nan          // #nan 
- #infinity     // -#infinity 
- #duration(1,0,0,0)  // #duration(-1,0,0,0) 
- #duration(0,1,30,0) // #duration(0,-1,-30,0)

在计算一元减运算符 -x 时,存在以下情况:

  • 传播在计算 x 时引发的错误。

  • 如果表达式为数字,则结果为来自表达式 x 的数字值,但符号发生了更改。 如果该值为 NaN,则结果也为 NaN。

逻辑求反运算符

逻辑求反运算符 (not) 针对以下类型的值定义:

X 结果 解释
type logical type logical 否定
null null

此运算符计算指定逻辑值的逻辑 not 运算。 例如:

not true             // false 
not false            // true 
not (true and true)  // false

在计算逻辑求反运算符 not x 时,存在以下情况:

  • 传播在计算 x 时引发的错误。

  • 计算表达式 x 所生成的值必须是逻辑值,否则将引发原因代码为 "Expression.Error" 的错误。 如果该值为 true,则结果为 false。 如果操作数为 false,则结果为 true

结果为逻辑值。

类型运算符

运算符 isas 称为类型运算符。

类型兼容性运算符

类型兼容性运算符 x is y 针对以下类型的值定义:

X Y 结果
type any nullable-primitive-type type logical

如果 x 的先赋类型与 y 兼容,则表达式 x is y 返回 true,如果 false 的先赋类型与 x 不兼容,则返回 yy 必须是可为 null 的基元类型。

is-expression:
      as-expression
      is-expression
is nullable-primitive-type
nullable-primitive-type:

      nullableopt primitive-type

is 运算符支持的类型兼容性是常规类型兼容性的子集,并使用以下规则定义:

  • 如果 x 为 null,则当 yany 类型、null 类型或可为 null 的类型时,它是兼容的。

  • 如果 x 不为 null,则仅当 x 的基元类型与 y 相同时,它才是兼容的。

在计算表达式 x is y 时,存在以下情况:

  • 传播在计算表达式 x 时引发的错误。

类型断言运算符

类型断言运算符 x as y 针对以下类型的值定义:

X Y 结果
type any nullable-primitive-type type any

表达式 x as y 断言,根据 is 运算符,值 xy 兼容。 如果不兼容,则会出现错误。 y 必须是可为 null 的基元类型。

as-expression:
      equality-expression
      as-expression
as nullable-primitive-type

表达式 x as y 的计算如下:

  • 执行类型兼容性检查 x is y,如果测试成功,则断言返回未更改的 x

  • 如果兼容性检查失败,则会引发原因代码为 "Expression.Error" 的错误。

示例:

1 as number               // 1 
"A" as number             // error 
null as nullable number   // null

在计算表达式 x as y 时,存在以下情况:

  • 传播在计算表达式 x 时引发的错误。

合并运算符

如果合并运算符 ?? 的左操作数不为 null,则它将返回其结果,否则它将返回其右操作数的结果。 当且仅当左操作数不为 null 时,才计算右操作数。