运算符优先级
当表达式包含多个运算符时,运算符的优先级控制计算各个运算符的顺序。例如,表达式x y * z
被评估为x (y * z)
因为*
运算符的优先级高于二元
运算符。运算符的优先级由其相关文法产生式的定义确定。例如,加法表达式由一系列乘法表达式组成,由
or-
运算符分隔,因此
and-
运算符的优先级低于*
and/
运算符。
在括号的表达式生产可用于更改默认的优先排序。
括号表达式:
(
表达式 )
例如:
复制
代码语言:javascript复制1 2 * 3 // 7
(1 2) * 3 // 9
下表总结了 M 个运算符,按优先级从高到低的顺序列出了运算符类别。同一类别中的运算符具有相同的优先级。
类别 | 表达 | 描述 |
---|---|---|
基本的 | 我@我 | 标识符表达式 |
( x ) | 括号表达式 | |
x [我] | 抬头 | |
x { y } | 物品存取 | |
x ( ... ) | 函数调用 | |
{ x , y , ... } | 列表初始化 | |
[ i = x , ... ] | 记录初始化 | |
... | 未实现 | |
一元 | x | 身份 |
- x | 否定 | |
not X | 逻辑否定 | |
元数据 | X meta ÿ | 关联元数据 |
乘法 | x * y | 乘法 |
x / y | 分配 | |
添加剂 | x y | 添加 |
x - y | 减法 | |
关系型 | x < y | 少于 |
x > y | 比...更棒 | |
x <= y | 小于或等于 | |
x >= y | 大于或等于 | |
平等 | x = y | 平等的 |
x <> y | 不相等 | |
类型断言 | X as ÿ | 是否兼容可空原始类型或错误 |
类型一致性 | X is ÿ | 测试是否兼容可空原始类型 |
逻辑与 | X and ÿ | 短路连接 |
逻辑或 | X or ÿ | 短路分离 |
合并 | X ?? ÿ | 空合并运算符 |
运算符和元数据
每个值都有一个关联的记录值,可以携带有关该值的附加信息。此记录称为值的元数据记录。元数据记录可以与任何类型的值相关联,甚至null
. 这种关联的结果是具有给定元数据的新值。
元数据记录只是一个常规记录,可以包含常规记录可以包含的任何字段和值,并且本身具有元数据记录。将元数据记录与值相关联是“非侵入性的”。除了那些明确检查元数据记录的行为外,它不会改变评估中值的行为。
每个值都有一个默认的元数据记录,即使没有指定。默认元数据记录为空。以下示例显示使用Value.Metadata
标准库函数访问文本值的元数据记录:
复制
代码语言:javascript复制Value.Metadata( "Mozart" ) // []
当值与构造新值的运算符或函数一起使用时,通常不会保留元数据记录。例如,如果使用&
运算符连接两个文本值,则结果文本值的元数据是空记录[]
。以下表达式是等效的:
复制
代码语言:javascript复制"Amadeus " & ("Mozart" meta [ Rating = 5 ])
"Amadeus " & "Mozart"
标准库函数Value.RemoveMetadata
和Value.ReplaceMetadata
可用于从一个值中删除所有元数据和替换值的元数据(而不是合并入元数据可能存在的元数据)。
返回携带元数据的结果的唯一运算符是元运算符。
结构递归运算符
值可以是循环的。例如:
复制
代码语言:javascript复制let l = {0, @l} in l
// {0, {0, {0, ... }}}
[A={B}, B={A}]
// [A = {{ ... }}, B = {{ ... }}]
M 通过保持记录、列表和表的构建延迟来处理循环值。试图构造一个不能从插入的惰性结构值中受益的循环值会产生一个错误:
复制
代码语言:javascript复制[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
该项目的访问表达 x{y}
的回报:
- 对于一个列表
x
和一个数字y
,x
位置列表中的项目y
。列表的第一项被认为具有零序数索引。如果请求的位置在列表中不存在,则会引发错误。 - 对于一个表格
x
和一个数字y
,表格所在x
位置的行y
。表的第一行被认为具有零序数索引。如果表中不存在请求的位置,则会引发错误。 - 对于 table
x
和 recordy
,与字段名称匹配相应表列名称x
的字段的记录字段值y
匹配的表行。如果表中没有唯一匹配的行,则会引发错误。
例如:
复制
代码语言:javascript复制{"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
的项目存取表达也支持的格式x{y}?
,它返回null
时的位置(或匹配)y
的列表或表中不存在x
。如果 有多个匹配项y
,仍会引发错误。
例如:
复制
代码语言:javascript复制{"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
项目访问不会强制评估列表或表格项目,而不是被访问的项目。例如:
复制
代码语言:javascript复制{ error "a", 1, error "c"}{1} // 1
{ error "a", error "b"}{1} // error "b"
当x{y}
评估项目访问运算符时,以下内容成立:
- 在表达式求值过程中引发
x
或y
传播的错误。 - 该表达式
x
生成一个列表或一个表值。 - 该表达式
y
生成一个数字值,如果x
生成一个表值,则生成一个记录值。 - 如果
y
产生一个数字值并且 的值为y
负,"Expression.Error"
则会引发带有原因代码的错误。 - 如果
y
产生一个数值并且 的值y
大于或等于 的计数x
,"Expression.Error"
则会引发带有原因代码的错误,除非使用可选运算符形式x{y}?
,在这种情况下null
返回值。 - 如果
x
生成一个表值并y
生成一个记录值并且没有匹配的y
inx
,"Expression.Error"
则会引发带有原因代码的错误,除非使用可选运算符形式x{y}?
,在这种情况下null
返回值。 - 如果
x
生成一个表值并y
生成一个记录值并且有多个匹配项y
inx
,"Expression.Error"
则会引发带有原因代码的错误。
在没有项目x
比在其他位置y
的项目选择的过程中被评估。(对于流式列表或表格,在位置之前的项目或行将y
被跳过,这可能会导致它们的评估,具体取决于列表或表格的来源。)