添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
爱健身的作业本  ·  Unity ...·  2 年前    · 

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

运算符优先级

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

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

parenthesized-expression:
( expression )

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

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

i
@i 标识符表达式 (x) 带圆括号表达式 x[i] LookUp x{y} x(...) {x, y, ...} 列表初始化 [ i = x, ... ] 记录初始化 Identity notx xmetay 关联元数据 x * y x / y x + y x - y x > y x<= y 小于或等于 x >= y 大于或等于 x = y xasy 为兼容的可为 null 的基元类型或错误 类型一致性 xisy 测试是否为兼容的可为 null 的基元类型 xandy xory 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-expressionx{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]? 作为对标识符 _(下划线)的速记引用。 以下两个表达式等效:

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

    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
    metaunary-expression

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

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

    在应用元数据组合运算符 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 是唯一不等于其自身的值。

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

  • 如果两个 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
          additive-expression
          additive-expression<relational-expression
          additive-expression
    >relational-expression
          additive-expression
    <= _relational-expression
          additive-expression >=relational-expression

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

  • 传播在计算 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
    orlogical-or-expression
    logical-and-expression:
          is-expression
          is-expression
    andlogical-and-expression

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

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

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

    false 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

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

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

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

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

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

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

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

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

    加法运算符

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

    在表中,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 具有相同符号的无穷值。

    持续时间之和

    两个持续时间之和是指这两个持续时间所表示的 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 所得到的值的类型,如下所示:

    在表中,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 具有相同符号的无穷值。

    持续时间之差

    两个持续时间之差是指每个持续时间所表示的 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 所得到的值的类型,如下所示:

    #nan * #infinity // #nan

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

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

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

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

    除法运算符

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

    #nan / #infinity // #nan

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

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

    缩放的持续时间

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

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

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

  • 如果 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

    一元表达式
    一元表达式
    一元表达式

    一元加运算符

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

    对于其他值,将引发原因代码为 "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) 针对以下类型的值定义:

    - #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 时引发的错误。

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

    结果为逻辑值。

    类型运算符

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

    类型兼容性运算符

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

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

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

          nullableopt primitive-type

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

  • 如果 x 为 null,则当且仅当 y 是可为 null 的类型或 any 类型时,它才是兼容的。

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

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

  • 传播在计算表达式 x 时引发的错误。
  • 类型断言运算符

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

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

    as-expression:
          equality-expression
          as-expression
    asnullable-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 时,才计算右操作数。

  •