函数
函数是一个值,该值表示从一组参数值到单个值的映射。 通过提供一组输入值(参数值)来调用函数,并生成单个输出值(返回值)。
写入函数
使用 function-expression 写入函数:
function-expression:
(
parameter-listopt)
function-return-typeopt=>
function-body
function-body:
表达式
parameter-list:
fixed-parameter-list
fixed-parameter-list,
optional-parameter-list
optional-parameter-list
fixed-parameter-list:
参数 (parameter)
parameter,
fixed-parameter-list
参数:
parameter-name parameter-typeopt
parameter-name:
标识符
parameter-type:
assertion
function-return-type:
assertion
assertion:
as
nullable-primiitve-type
optional-parameter-list:
optional-parameter
optional-parameter,
optional-parameter-list
optional-parameter:
optional
parameter
nullable-primitve-type
nullable
optprimitive-type
下面是一个函数的示例,该函数正好需要两个值 x
和 y
,并生成对这些值应用 +
运算符的结果。 x
和 y
是形参,它们是函数的 parameter-list 的一部分,而 x + y
是 function-body:
(x, y) => x + y
计算 function-expression 的结果是生成函数值(而不是计算 function-body) 。 本文档中的约定是,函数值(而不是函数表达式)与 parameter-list 一起显示,但带有省略号(...
),而不是 function-body。 例如,计算上述函数表达式后,它将显示为以下函数值:
(x, y) => ...
为函数值定义了以下运算符:
运算符 | 结果 |
---|---|
x = y |
等于 |
x <> y |
不等于 |
函数值的本机类型是一种自定义函数类型(派生自内部类型 function
),它列出参数名称并指定所有参数类型和返回类型为 any
。 (有关函数类型的详细信息,请转到函数类型。)
调用函数
函数的 function-body 是通过使用 invoke-expression 调用函数值来执行的。 调用函数值意味着将计算函数值的 function-body 并返回值或引发错误。
invoke-expression:
primary-expression(
argument-listopt)
argument-list:
expression-list
每次调用函数值时,都会将一组值指定为 argument-list,称为函数的参数 。
argument-list 用于将固定数量的参数直接指定为表达式列表。 下面的示例定义一个在字段中具有函数值的记录,然后从该记录的另一个字段调用函数:
[
MyFunction = (x, y, z) => x + y + z,
Result1 = MyFunction(1, 2, 3) // 6
]
调用函数时,以下条件适用:
用于计算函数的 function-body 的环境包含与每个参数对应的变量,其名称与参数相同。 每个形参的值对应于从 invoke-expression 的 argument-list 构造的值(如形参中所定义)。
在计算 function-body 之前,计算与函数参数对应的所有表达式。
传播在 expression-list 或 function-body 中计算表达式时引发的错误。
从 argument-list 构造的实参数目必须与函数的形参兼容,否则将引发错误,原因代码为
"Expression.Error"
。 确定兼容性的过程在参数中定义。
参数
parameter-list 中可能存在两种形参:
必需参数指示在调用函数时,必须始终指定与形参相对应的实参。 必须先在 parameter-list 中指定必需形参。 以下示例中的函数定义必需参数
x
和y
:[ MyFunction = (x, y) => x + y, Result1 = MyFunction(1, 1), // 2 Result2 = MyFunction(2, 2) // 4 ]
可选参数指示在调用函数时,可以指定与形参相对应的实参,但不是必需指定。 如果在调用函数时未指定与可选形参对应的实参,则改为使用
null
值。 可选形参必须出现在 parameter-list 中的任何必需形参之后。 以下示例中的函数定义固定参数x
和可选参数y
:[ MyFunction = (x, optional y) => if (y = null) x else x + y, Result1 = MyFunction(1), // 1 Result2 = MyFunction(1, null), // 1 Result3 = MyFunction(2, 2), // 4 ]
调用函数时指定的实参数目必须与形参列表兼容。 函数 F
的一组参数 A
的兼容性计算如下:
让值 N 表示从 argument-list 构造的实参
A
的数目。 例如:MyFunction() // N = 0 MyFunction(1) // N = 1 MyFunction(null) // N = 1 MyFunction(null, 2) // N = 2 MyFunction(1, 2, 3) // N = 3 MyFunction(1, 2, null) // N = 3 MyFunction(1, 2, {3, 4}) // N = 3
让值 Required 表示
F
的固定参数的数目,Optional 表示F
的可选参数的数目 。 例如:() // Required = 0, Optional = 0 (x) // Required = 1, Optional = 0 (optional x) // Required = 0, Optional = 1 (x, optional y) // Required = 1, Optional = 1
如果以下为 true,则参数
A
与函数F
兼容:- (N >= Fixed) and (N <= (Fixed + Optional))
- 实参类型与
F
的相应形参类型兼容
如果函数具有已声明的返回类型,则函数
F
的主体的结果值与F
的返回类型兼容,前提是以下为 true:- 通过使用为函数形参提供的实参计算函数体得到的值的类型与返回类型兼容。
如果函数体产生了与函数的返回类型不兼容的值,则会引发错误,原因代码为
"Expression.Error"
。
递归函数
若要编写递归函数值,必须使用范围运算符 (@
) 在其范围内引用函数。 例如,下面的记录包含一个定义 Factorial
函数的字段和调用该函数的另一个字段:
[
Factorial = (x) =>
if x = 0 then 1 else x * @Factorial(x - 1),
Result = Factorial(3) // 6
]
同样,只要需要访问的每个函数都具有名称,就可以编写相互递归函数。 在下面的示例中,部分 Factorial
函数已重构为第二个 Factorial2
函数。
[
Factorial = (x) => if x = 0 then 1 else Factorial2(x),
Factorial2 = (x) => x * Factorial(x - 1),
Result = Factorial(3) // 6
]
闭包
函数可以将另一个函数作为值返回。 此函数也可以依赖于原始函数的一个或多个参数。 在下面的示例中,与字段 MyFunction
关联的函数返回一个函数,该函数返回指定给它的参数:
[
MyFunction = (x) => () => x,
MyFunction1 = MyFunction(1),
MyFunction2 = MyFunction(2),
Result = MyFunction1() + MyFunction2() // 3
]
每次调用函数时,将返回维护参数值的新函数值,以便在调用时返回参数值。
函数和环境
除了参数外,function-expression 的 function-body 还可以引用函数初始化时环境中存在的变量 。 例如,字段 MyFunction
定义的函数访问封闭记录 A
的字段 C
:
[
A =
[
MyFunction = () => C,
C = 1
],
B = A[MyFunction]() // 1
]
调用 MyFunction
时,它将访问变量 C
的值,即使它是从不包含变量 C
的环境 (B
) 中调用的。
简化声明
each-expression 是使用名为 _
(下划线)的单个形参声明非类型化函数的语法简写形式。
each-expression:
each
each-expression-body
each-expression-body:
function-body
简化的声明通常用于提高高阶函数调用的可读性。
例如,以下声明对在语义上是等效的:
each _ + 1
(_) => _ + 1
each [A]
(_) => _[A]
Table.SelectRows( aTable, each [Weight] > 12 )
Table.SelectRows( aTable, (_) => _[Weight] > 12 )