运算符
运算符
运算符优先级
运算符优先级控制运算符的计算顺序。括号可以修改默认的优先级。
parenthesized-expression:
(
expression )
运算符由高到低:
类别 | 表达式 | 说明 |
---|---|---|
主要 | i @i | 标识符表达式 |
(x) | 括号表达式 | |
x[i] | 字段访问 | |
x | 项访问 | |
x(...) | 函数调用 | |
{x, y, ...} | 列表初始化 | |
[i = x, ...] | 记录初始化 | |
... | 未实现 | |
一元 | +x | 正 |
-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 | 返回兼容的值或错误 |
类型兼容 | x is y | 返回是否兼容 |
逻辑与 | x and y | 短路逻辑与 |
逻辑或 | x or y | 短路逻辑或 |
合并 | x ?? y | null值合并运算符 |
注:截止2024年8月,??
还不能用在Power Query SDK中。
运算符和元数据
每个值都有一个关联的记录值,该记录值存储值的其他信息。这个记录被称为值的元数据记录。元数据记录可以与任何值相关联。
元数据记录只是一个常规的记录,该记录和普通的记录没有区别。
元数据记录与值关联是一种非侵入性行为。该操作不会影响值在计算时的行为,除非时显式的检查元数据记录。
元数据记录值通常用于程序和用户需要额外的附加数据时使用。如自定义函数的友好提示。
每个值都有一个元数据记录,默认元数据记录是空记录。
标准库函数Value.Metadata
、Value.ReplaceMetadata
和Value.RemoveMetadata
分别可以查看、替换和清空值的元数据记录。
添加元数据记录。
1 meta [a = 1, b = "ok"]
Value.ReplaceMetadata(1, [b = "ok"])
查看元数据记录。
// []
Value.Metadata("okay")
// [a = 1, b = "ok"]
Value.Metadata(1 meta [a = 1, b = "ok"])
元数据记录不会影响值的计算行为,仅在需要时由程序和用户主动读取。
通常情况下,含有元数据的值在计算后元数据会被清空。
// 6
1 meta [a = 1, b = "ok"] + 5
// []
Value.Metadata(1 meta [a = 1, b = "ok"] + 5)
// []
Value.Metadata(+(1 meta [a = 1, b = "ok"] + 5))
结构递归运算符
值可以循环。
// a = {0, {0, ...}}
let a = {0, @a} in a
// [a = {{ ... }}, b = {{ ... }}]
[a = {b}, b = {a}]
[a = b, b = {a}]
M通过保持列表、记录和表的构造惰性来处理循环值。
M一些运算符由结构递归定义的。例如:记录和列表的相等性分别由记录字段和项列表的联合相等性定义。
对于非循环值,应用结构递归会值的有限拓展:共享嵌套值将被重复遍历,但递归过程总是终止。
应用结构递归时,循环值具有无限拓展。M的语义对这种无限拓展没有特别的适应。例如:尝试比较循环值是否相等,通常会耗尽资源并异常终止。
选择和投影运算符
项访问
根据值从列表或表中从0开始的位置选择数据。
item-access-expression:
item-selection
optional-item-selection
item-selection:
primary-expression {
item-selector }
optional-item-selection:
primary-expression {
item-selector }
?
item-selector:
expression
对于x{y}
:
x
是表y
是数字,返回表行记录y
是记录,返回表中唯一符合记录(条件)的行
x
是列表y
是数字,返回列表的项值
如果y
是负数,将会引发错误。
对于x{y}?
,如果无法找到结果,将返回null
。
如果x
是表、y
是记录,但是表中存在多个符合记录的表行时,会产生错误。如果使用?
修饰,依然会报错,因为?
仅处理找不到结果的情况。
{0, 1, 2, 3}{2} // 2
{0, 1, 2, 3}{4} // error
{0, 1, 2, 3}{4}? // null
#table({"A","B"},{{1,2},{2,2}}){1} // [A = 2, B = 2]
#table({"A","B"},{{1,2},{2,2}}){[A=2]} // [A = 2, B = 2]
#table({"A","B"},{{1,2},{2,2}}){[A=0]} // error
#table({"A","B"},{{1,2},{2,2}}){[A=0]}? // null
#table({"A","B"},{{1,2},{2,2}}){[B=2]} // error
#table({"A","B"},{{1,2},{2,2}}){[B=2]}? // error
项访问不会对列表或表中正在访问的项之外的项进行求值。
{error "0", 1, error "2"}{1} // 1
对于流式处理的列表和表,仅会计算正在访问的项,对于其它的内容可能会被计算,这取决于列表和表的数据源。
表达式x{y}
求值时,会传播在计算表达式x
或y
期间引发的错误。
字段访问
字段访问从记录或表中选择字段值,或将记录或表投影到具有较少字段或列的记录或表。
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
访问类型
- 字段选择
exp [x]
exp [x] ?
- 投影
exp [[x]]
exp[[x], [y]]
exp[[x], [y]] ?
- 隐式目标(implicit target):从当前环境中的变量
_
中提取数据,此时可以省略exp
。
x[y]?
中的?
可以在访问遇到不存在的字段或列时返回null
。对于投影,?
会将对应的字段或列的所有行填充为null
。
[A = 1, B = 2][A] // 1
[A = 1, B = 2][C] // error
[A = 1, B = 2][C]? // null
[A = 1, B = 2][[A], [B]] // [A = 1, B = 2]
[A = 1, B = 2][[A], [C]] // error
[A = 1, B = 2][[A], [C]]? // [A = 1, C = null]
#table({"A","B"},{{1,2},{2,2}})[A] // {1, 2}
#table({"A","B"},{{1,2},{2,2}})[C] // error
#table({"A","B"},{{1,2},{2,2}})[C]? // null
#table({"A","B"},{{1,2},{2,2}})[[A], [C]] // error
#table({"A","B"},{{1,2},{2,2}})[[A], [C]]? // #table({"A","C"}, {{1, null}, {2, null}})
表达式x[y]
、x[y]?
、x[[y]]
、x[[y]]?
求值时:
- 传播在计算表达式
x
期间引发的错误。 - 计算字段
y
时引发的错误与字段y
永久关联,然后进行传播。将来对y
字段的访问都会引发相同的错误。
附加each说明
each
是简化的函数声明,each _ + 5
等价于(__) => _+ 5
。
当出现each
关键字时就隐式声明了形参_
。在字段访问时,_
可以省略,即_[x]
可以简化成[x]
。
对于多层each
语句,规则同多层环境中的同名变量,使用就近原则取最近一层(算当前层且仅向父层级)的_
的值。
元数据运算符
为值添加元数据记录。
metadata-expression:
unary-expression
unary-expression meta
unary-expression
1 meta [type = "number"]
let f = 1, g = f meta [type = "number"] in g
对于表达式x meta y
:
- 传播在计算表达式
x
或y
期间引发的错误。 y
必须是记录值。
其它详见本文【运算符和元数据】。
备注:
unary-expression:
type-expression
+
unary-expression
-
unary-expression
not
unary-expression
type-expression:
primary-expression
type
primary-type
primary-expression:
literal-expression
list-expression
record-expression
identifier-expression
section-access-expression
parenthesized-expression
field-access-expression
item-access-expression
invoke-expression
not-implemented-expression
相等运算符
相等运算符=
用于确定两个值是否相等。不相等运算符<>
用于确定两个值是否不相等。
equality-expression:
relational-expression
relational-expression=
equality-expression
relational-expression<>
equality-expression
元数据记录不影响比较结果。
在计算表达式x = y
或x <> y
时:
- 传播计算表达式
x
或y
时引发的错误。 - 忽略元数据记录。
- 始终存在
(x = y) = not (x <> y)
。 - 两种类型一定不相等。
// 都是false
null = 1
null = "A"
0 = #time(0, 0, 0)
false = 1
1 meta [A = 1] = 1 meta [A = 2]
// 都是true
null = null
1 = 1.0
#nan
不等于自身,也是唯一不等于自身的值。
#nan = #nan // false
#nan <> #nan // true
- 非
#nan
的数字会使用按位比较。 datetimezone
会使用修正后的日期比较。
// false
#datetimezone(1, 1, 1, 12, 0, 0, 0, 0) = #datetimezone(1, 1, 1, 12, 0, 0, 1, 0)
// true
#datetimezone(1, 1, 1, 11, 0, 0, 0, 0) = #datetimezone(1, 1, 1, 12, 0, 0, 1, 0)
#datetimezone(1, 1, 1, 0, 0, 0, -12, 0) = #datetimezone(1, 1, 2, 0, 0, 0, 12, 0)
- 文本会区分大小写。
"a" = "A" // false
- 表达式会求值后再比较
#time(0, 0, 1) = #time(0, 0, 0.999999999999999) // true
- 列表必须项个数相等且相同位置的值相等。
#nan
。
{1, 2, 3} = {1, 3, 2} // false
{1, 2, 3} = {1, 2, 3} // true
{1, 2, null} = {1, 2, null} // true
{1, 2, #nan} = {1, 2, #nan} // false
- 记录必须字段名相同且对应的字段值相等,字段顺序可以不同。
#nan
。
[A=1, B=2] = [B=2, A=1] // true
[A=1, B=1] = [A=1, C=1] // false
[A=1, B=#nan] = [A=1, C=#nan] // false
- 表必须行数相等、列数相等、列名相同且列的每一行的值相等,列顺序可以不同。
#nan
。
// 列顺序可以不同
#table({"A","B"},{{1,2},{3,4}}) = #table({"B","A"},{{2,1},{4,3}})
// 行顺序需要一样
#table({"A","B"},{{3,4},{1,2}}) = #table({"B","A"},{{2,1},{4,3}})
// false
#table({"A","B"},{{1,2},{3,#nan}}) = #table({"A","B"},{{1,2},{3,#nan}})
- 函数值等于自身,不同的函数可能相等也可能不相等。如果两个函数值相等,则调用它们时具有相同的行为。
(() => 1) = (() => 1) // false
- 修改函数类型的函数值与修改前的函数值依然相等。
let
f1 = (x as number) as number => x + 1,
f2 = Value.ReplaceType(f1, type function (y as text) as text)
in
f1 = f2
- 类型值等于自身,不同的类型值可能相等也可能不相等,如果两个类型值相等,则查询一致性时它们拥有相同的行为。
type number = type any // false
type number = Currency.Type // false
type number = Double.Type // false
type number = Value.Type(1) // true
type {number} = Value.Type({1, 2}) // false
type {any} = Value.Type({1, 2}) // true
补充:浮点数误差
// 浮点误差
0.1 + 0.2 = 0.3
关系运算符
关系运算符<
、>
、<=
、>=
用于比较值的大小关系。
relational-expression:
additive-expression
additive-expression<
relational-expression
additive-expression>
relational-expression
additive-expression<=
relational-expression
additive-expression>=
relational-expression
计算表达式x op y
时:
- 传播在计算表达式
x
或y
时产生的错误。 - 表达式
x
和y
的计算结果必须是可以比较的标量值,具体为非抽象类型且不是type
、list
、record
的值。
提示
标量值通常理解为传统意义上的单个具体值。
列表、记录、表在M中定义为值,但通常不认为它们是标量值,因为还可以拆分为更多具体的值。
函数等也不能是标里值,因为它们是抽象的指代。
- 操作数除非有
null
,否则必须是相同类型。除null
以外的不同类型比较会报错。
提示
如果你试图比较不同类型并且仔细观察错误你会发现一个问题:操作符和操作数可能不是实际的内容。
如:"A" >= 1或"A" > 1或"A" < 1或"A" <= 1。
####################错误信息####################
在“”查询中出错。Expression.Error: 无法将运算符 < 应用于类型 Number 和 Text。
详细信息:
Operator=<
Left=1
Right=A
############################################
- 任意个操作数为
null
,结果一定是null
。 - 非
null
的不同类型的值比较,会引发错误。 - 逻辑值
true
大于false
。 - 文本区分大小写。
"a" > "A"
"56" > "123456"
"123456" > "123"
- 表达式计算后的结果再进行比较。
#duration(0, 0, 1, 0) > #duration(0, 0, 0, 9999) // false
datetimezone
会使用修正后的日期比较。
#datetimezone(1, 1, 1, 0, 0, 0, -5, 0) > #datetimezone(1, 1, 1, 0, 0, 0, -4, 0)
- 两个
#nan
比较,除了<>
以外,任何关系或相等操作符都是false
。 #nan
不等于任何值且比任何值都小。
提示
此处官方规范描述为:如果任一操作数为#nan
,则所有关系运算符的结果都为false
。
但实际Excel测试结果为#nan
永远最小。
- 当两个操作数都是数字且不是
#nan
时,比较关系为-#infinity
<...
<-0.0
=+0.0
<...
<infinity
。
条件逻辑运算符
条件逻辑运算符有and
和or
。
logical-or-expression:
logical-and-expression
logical-and-expression or
logical-or-expression
logical-and-expression:
is-expression
is-expression and
logical-and-expression
逻辑与and
仅操作数同时为true
时,结果才为true
;否则结果为fasle
。
逻辑或or
仅操作数同时为fasle
时,结果才为false
;否则结果为true
。
条件逻辑操作符使用逻辑短路机制,因此操作数的位置不同可能导致结果不同。
提示
逻辑短路
根据逻辑运算符的左操作数判断是否需要继续计算右操作数,如果没必要则直接返回结果。
假定在x op y
中,x
和y
均为逻辑值。
- 对于
x and y
,如果x
是false
,则直接返回false
,否则继续计算y
。 - 对于
x or y
,如果x
是true
,则直接返回true
,否则继续计算y
。
下面的真值表,纵向为左操作符,横向为右操作符。
and
真值表总结:
- 操作数同时为
true
时结果为true
。 - 遵循逻辑短路(
null
为左操作数时会计算右操作数)并在符合预期结果的情况下尽可能返回右操作数。 - 只要
error
被计算就会引发错误。
and | false | |||
---|---|---|---|---|
false | ||||
false | false | false | false | false |
false | ||||
or
真值表总结:
- 操作数都为
false
时结果为false
。 - 只要
error
被计算就会引发错误。 false
和null
始终返回null
。- 遵循逻辑短路(
null
为左操作数时会计算右操作数)并在符合预期结果的情况下尽可能返回右操作数。
or | false | |||
---|---|---|---|---|
false | false | |||
对于表达式x op y
:
- 传播在计算表达式
x
和y
时引发的错误。 x
和y
只能是logical
或null
类型的值。
算术运算符
+
、-
、*
、/
被称为算术运算符。
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支持两种精度:
精度 | 语义 |
---|---|
Precision.Decimal | 128位(16字节)十进制表现形式。表示范围从±1.0e-28到±7.9e28,有效位数28-29。 |
Precision.Double | 符合IEEE 754-2008标准的64位(8字节)双精度浮点数。 |
相关信息
浮点数的机制导致其无法准确的计算小数,因此出现了如货币(或叫定点小数)的高精度小数类型,实际这类数字都是使用整数存储,在需要显示时才表现出小数。
执行算术运算的方式是:先选择一个精度,将两个操作数都转换成该精度,然后执行相应的计算,最后返回之前选择的精度下的结果。
不同精度之间的转换:
- 十进制转双精度会通过四舍五入转换到最接近的等效双精度。
- 双精度转十进制会通过四舍五入转换到最接近的等效十进制,如果溢出会得到正或负的
#infinity
。
内置的算术操作符(+
、-
、*
、/
)使用双精度。标准库函数(Value.Add
、Value.Subtract
、Value.Multiply
、Value.Divide
)可以指定精度。还有一些函数也支持指定精度,如:List.Sum
、List.Product
、List.Average
。
加法运算符
加法运算符(x + y
)可以计算不同类型的值。
T
表示date
、time
、datetime
、datetimezone
。
x | y | 结果 | 可调换 | 说明 |
---|---|---|---|---|
number | number | number | 是 | 数值求和 |
duration | duration | duration | 是 | 持续时间求和 |
T | duration | T | 是 | T 偏移时间 |
T 、number 、duration 、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值求和
对两个数值进行加法计算,得到一个新的数值。
操作符+
使用默认的双精度,如果需要十进制精度需要使用Value.Add
。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
+ | y | +0 | -0 | +∞ | -∞ | #nan |
---|---|---|---|---|---|---|
x | x+y | x | x | +∞ | -∞ | #nan |
+0 | y | +0 | +0 | +∞ | -∞ | #nan |
-0 | y | +0 | -0 | +∞ | -∞ | #nan |
+∞ | +∞ | +∞ | +∞ | +∞ | #nan | #nan |
-∞ | -∞ | -∞ | -∞ | #nan | -∞ | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间之和
两个持续时间之和即表示单位是100ns的时间刻度总数的和。
持续时间的日期时间偏移
T
类型的值和duration
类型的值求和表示对T
类型的值进行偏移并得到T
类型的值。类型T
可以是date
、time
、datetime
、datetimezone
。
- 对
time
和date
类型的值进行偏移可以认为是datetime
类型的值进行偏移,然后截取需要的部分。 - 对
datetimezone
类型的值进行偏移不会改变时区部分。
减法运算符
减法运算符(x - y
)可以计算不同类型的值。
T
表示date
、time
、datetime
、datetimezone
。
x | y | 结果 | 可调换 | 说明 |
---|---|---|---|---|
number | number | number | 是 | 数值相减 |
duration | duration | duration | 是 | 持续时间相减 |
T | duration | T | 否 | 偏移T 类型值。 |
T 、number 、duration 、null | null | null | 是 | |
T | 类型同左的T | duration | 是 | 两个相同T 类型的值的时间差 |
两个datetimezone
类型的值的计算会先对日期时间进行修正,然后再计算。
非上表中的类型的操作数会引发错误。
传播在计算操作数期间引发的错误。
数值差
对两个数值进行减法计算,得到一个新的数值。
操作符-
使用默认的双精度,如果需要十进制精度需要使用Value.Subtract
。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
- | y | +0 | -0 | +∞ | -∞ | #nan |
---|---|---|---|---|---|---|
x | x-y | x | x | -∞ | +∞ | #nan |
+0 | -y | +0 | +0 | -∞ | +∞ | #nan |
-0 | -y | -0 | +0 | -∞ | +∞ | #nan |
+∞ | +∞ | +∞ | +∞ | #nan | +∞ | #nan |
-∞ | -∞ | -∞ | -∞ | -∞ | #nan | #nan |
#nan | #nan | #nan | #nan | #nan | #nan | #nan |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间之差
两个持续时间之差即表示单位是100ns的时间刻度总数的差。
持续时间的日期时间偏移
T
类型的值和duration
类型的值求和表示对T
类型的值进行偏移并得到T
类型的值。类型T
可以是date
、time
、datetime
、datetimezone
。
- 对
time
和date
类型的值进行偏移可以认为是datetime
类型的值进行偏移,然后截取需要的部分。 - 对
datetimezone
类型的值进行偏移不会改变时区部分。
两个日期时间的持续时间
- 两个相同的
date
、time
、datetime
类型的值直接减去对应的日期时间部分,结果可以是负数。 - 两个
datetimezone
类型的值相减,会先修正时区,然后相减,结果可以是负数。
乘法运算符
乘法运算符(x * y
)可以计算不同类型的值。
x | y | 结果 | 可调换 | 说明 |
---|---|---|---|---|
number | number | number | 是 | 数值乘积 |
duration | number | duration | 是 | 持续时间的倍数 |
number 、duration 、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值乘积
对两个数值进行乘法计算,得到一个新的数值。
操作符*
使用默认的双精度,如果需要十进制精度需要使用Value.Multiply
。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
* | +y | -y | +0 | -0 | +∞ | -∞ | #nan |
---|---|---|---|---|---|---|---|
+x | x*y | -x*y | +0 | -0 | +∞ | -∞ | #nan |
-x | -x*y | x*y | -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 |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间的倍数
持续时间与数字的倍数即表示单位是100ns的时间刻度总数重复一定次数的总长度。
除法运算符
除法运算符(x / y
)可以计算不同类型的值。
x | y | 结果 | 可调换 | 说明 |
---|---|---|---|---|
number | number | number | 是 | 数值乘积 |
duration | number | duration | 是 | 持续时间的分数 |
duration | duration | number | 是 | 持续时间的商 |
number 、duration 、null | null | null | 是 |
非上表中的类型的操作数会引发错误。
传播在计算操作数时引发的错误。
数值除法
对两个数值进行乘法计算,得到一个新的数值。
操作符*
使用默认的双精度,如果需要十进制精度需要使用Value.Multiply
。
双精度计算使用64位二进制双精度IEEE754算法的规则计算。
* | +y | -y | +0 | -0 | +∞ | -∞ | #nan |
---|---|---|---|---|---|---|---|
+x | x/y | -x/y | +∞ | -∞ | +0 | -0 | #nan |
-x | -x/y | x/y | -∞ | +∞ | -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 |
十进制精度求和时会尽可能使用精度最大的操作数的精度。尽可能是指:如果一个29位整数和一个小数计算,则结果无法带小数。
持续时间除法
两个持续时间之商即表示单位是100ns的时间刻度总数的商。
缩放持续时间
将持续时间缩放一定倍数。
结构组合
组合运算符(x & y
)可以使用多种类型:
x | y | 结果 | 可调换 | 说明 |
---|---|---|---|---|
text | text | text | 是 | 字符串拼接 |
date | time | datetime | 是 | 日期时间拼接 |
text 、date 、time 、null | null | null | 是 | |
list | list | list | 是 | 拼接列表 |
record | record | record | 是 | 拼接记录 |
table | table | table | 是 | 拼接表 |
- 其它不在表中的类型合并会报错。
连接
使用x & y
可以连接文本、列表、记录或表。
- 传播在计算表达式
x
或y
时引发的错误。 - 如果
x
或y
的项包含错误,错误不会传播。 null
与text
或null
连接会得到null
;与list
、record
、table
连接会报错。- 连接
list
是在左操作数的结尾拼接上右操作数。 - 连接
record
是在左操作数的结尾拼接上右操作数中左操作数没有的字段。如果有共有的字段名,右操作数的字段值会替换左操作数的字段值。 - 连接
table
时,不是公共列的列按照原始的左右顺序排列,表行会按照原始顺序上下拼接,左表在上,右表在下。公共列的单元格值,以右操作数中值为准。空白的部分填充null
值。- 表最终的行数两表行数之和,列数是去重列名的列数之和。
- 如果两个表的列名相同,相当于右表拼在左表下。
- 如果两个表没有公共列,右表将拼接在左表的右下方,其余位置填充
null
值。
// "ABCD"
"AB" & "CD"
// {1, 2, 1, 2}
{1, 2} & {1, 2}
// [a = 3, b = 2, c = 4]
[a = 1, b = 2] & [a = 3, c = 4]
// 结果如下表
#table({"a", "b"}, {{1, 2}, {3, 4}}) & #table({"c", "b"}, {{5, 6}, {7, 8}, {9, 10}})
合并表结果,来自b表结果为红色。
a | b | c |
---|---|---|
1 | 2 | null |
3 | 4 | null |
null | ||
null | ||
null |
合并
记录合并
可以使用x & y
拼接两个记录。
- 传播在计算表达式
x
或y
时引发的错误。 - 记录字段内的错误不会传播。
- 合并记录不会导致字段值计算。
- 如果
x
和y
中有字段名重复,则x
中的重复字段使用y
中对应的字段值。 - 生成的记录顺序是
x
后跟y
中不属于x
的字段,顺序与y
的原始顺序一样。
// [a = 1, b = 2]
[a = 1] & [b = 2]
// [a = 3, b = 2, c = 4]
[a = 1, b = 2] & [a = 3, c = 4]
日期时间合并
使用x & y
可以将date
类型的值和time
类型的值拼接为datetime
类型的值。
- 传播在计算表达式
x
或y
时引发的错误。
// 0001-01-01T02:02:02.2220000
#date(1, 1, 1) & #time(2, 2, 2.222)
#time(2, 2, 2.222) & #date(1, 1, 1)
一元运算符
一元运算符包括:+
、-
、not
。
unary-expression:
type-expression
+
unary expression
-
unary expression
not
unary expression
一元加运算符
一元加运算符(+x
)可以使用不同类型:
x | 结果 | 说明 |
---|---|---|
number | number | 一元加 |
duration | duration | 一元加 |
null | null |
// #nan
+ #nan
- 传播计算表达式
x
时引发的错误。 - 一元加操作符的结果是操作数本身。
一元减运算符
一元减运算符(+x
)可以使用不同类型:
x | 结果 | 说明 |
---|---|---|
number | number | 一元减 |
duration | duration | 一元减 |
null | null |
- 传播计算表达式
x
时引发的错误。 - 一元减操作符的结果是操作数的负值,某些值可能还是本身,比如:
#nan
、null
。
逻辑取反运算符
一元取反运算符(not x
)可以使用不同类型:
x | 结果 | 说明 |
---|---|---|
logical | logical | 一元取反 |
null | null |
- 传播计算表达式
x
时引发的错误。 - 一元取反操作符只能是逻辑值或
null
值。
not false // true
not true // fasle
not null // null
类型运算符
运算符is
和as
称为类型运算符。
类型兼容性运算符
类型兼容运算符(x is y
)定义:
x | y | 结果 |
---|---|---|
any | nullable-primitive-type | logical |
如果x
值的归属类型兼容于类型y
,则表达式x is y
返回true
,否则返回false
。y
必须是nullable-primitive-type
。
is-expression:
as-expression
is-expression is
nullable-primitive-type
nullable-primitive-type:
nullable
opt primitive-type
is
运算符支持的类型兼容是常规类型兼容性的子集。
- 当
x
为null
时,仅当y
是可为空的类型或any
类型才是兼容的。 - 当
x
不为null
时,仅当x
的原始类型与y
相同才是兼容的。
在计算x is y
时:
- 传播计算表达式
x
或y
时引发的错误。
类型断言运算符
类型断言运算符(x as y
)定义:
x | y | 结果 |
---|---|---|
any | nullable-primitive-type | any |
断言表达式x as y
,根据is
运算符判断值x
的类型是否和y
兼容,如果不兼容则会出现错误。y
必须是nullable-primitive-type
。
as-expression:
equality-expression
as-expression as
nullable-primitive-type
表达式x as y
计算如下:
- 执行类型兼容性检查
x is y
,如果测试成功,则断言返回未修改的x
。 - 如果兼容性检查失败,则引发错误。
在计算x as y
时:
- 传播计算表达式
x
或y
时引发的错误。
合并运算符
如果合并运算符??
的左操作数不为null
,则将它作为结果返回,否则将右操作数作为结果返回。如果左操作数不是null
,右操作数不会被计算。