函数
函数
函数是一个值,该值表示一组参数值到单个值的映射。通过提供一组输入值(参数值)来调用函数,并生成单个输出值(返回值)。
编写函数
函数使用function-expression来编写。
函数由声明部分、函数主体(或函数体)组成。声明部分又有参数列表、返回值类型组成。
function-expression:
(
parameter-listopt )
function-return-typeopt =>
function-body
function-body:
expression
parameter-list:
fixed-parameter-list
fixed-parameter-list ,
optional-parameter-list
optional-parameter-list
fixed-parameter-list:
parameter
parameter ,
fixed-parameter-list
parameter:
parameter-name parameter-typeopt
parameter-name:
identifier
parameter-type:
assertion
function-return-type:
assertion
assertion:
as
nullable-primitve-type
optional-parameter-list:
optional-parameter
optional-parameter ,
optional-parameter-list
optional-parameter:
optional
parameter
nullable-primitve-type:
nullable
opt primitive-type
对于下面定义的函数,x
和y
是需要输入的参数,调用函数时会将计算表达式x + y
,并将生成的值返回。
(x, y) => x + y
函数表达式(function-expression)生成的结果是一个函数值,而非函数主体(function-body)的内容。
函数值的内部类型是function
类型,它表示一个指定any
类型的形参和返回值类型为any
的函数类型。
调用函数
函数主体(function-body)通过调用表达式(invoke-expression)来执行。调用函数会计算函数主体(function-body)并返回值或引发错误。
invoke-expression:
primary-expression (
argument-listopt )
argument-list:
expression-list
函数内定义的参数叫形式参数,简称形参。实际调用的参数叫实际参数,简称实参。
形参位于function-expression中的parameter-list,实参位于invoke-expression中的argument-list。
// x是形参,a是实参
let
a = 1
fx = (x) => x + 1
in
fx(a)
调函定义时:
- 传播计算expression-list或function-expression表达式时引发的错误。
- 用于计算函数的函数主体(function-body)的环境包含与每个参数对应的变量,其名称与参数相同。每个参数的值对应于从调用表达式(invoke-expression)的参数列表(argument-list)构造的值。
- 从参数列表(argument-list)构建的参数数量必须与函数的形参(parameter-list中定义的内容)兼容,否则会引发错误。
- 在计算函数主体之前,所有的对应于参数列表(argument-list)的表达式都会被计算。
参数
parameter-list中存在两种参数:必需参数和可选参数。可选参数只能定义于必需参数之后。可选参数名称前使用optional
修饰。
- 必需参数。必需参数在调用时必需传入实参,否则会引发错误。
- 可选参数。可选参数可以选择是否输入,不输入可选参数则会使用
null
作为形参的值。
调用函数时,会对比实参和形参的类型,如果不兼容则会报错。
递归函数
若要定义递归函数,需要使用作用域运算符(@
)在其作用域内引用函数。
let
fx = (x) => if x = 1 then 1 else x * @fx(x - 1),
r = fx(5)
in
r
提示
递归函数是指调用自己的函数。在M中,正常情况下正在定义的变量是无法访问的,因此@
的作用就是使你能够访问正在定义的变量。
如果对不是正在定义的变量使用该操作符,则和不加没有区别。
递归通常是有限制的,不管是递归函数还是其它递归结构,如果处理不慎很容易出现栈溢出错误。
闭包
函数可以将其他函数作为返回值返回。
特定情况下,某些函数需要随时设置新的参数来实现特定的功能,因此使用函数内定义函数来实现预先设置参数。
let
fx = (m) =>
(n) => // 定义新的函数
m + n, // 新的函数可以访问外部函数的形参
r = fx(6)(100)
in
r
标准库中的Splitter.*
、Combiner.*
函数都是此类函数。
函数和环境
除了参数以外,函数主体(function-body)还会引入函数初始化时环境中存在的变量。
let
fx1 = [
m = 6,
fx2 = (n) => m + n
],
r = fx1[fx2](100)
in
r
简化声明
each表达式(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 )
each
可以直接使用(_)=>
进行替代。当形参名为下划线(_
)时,可以使用隐式目标字段访问。
Table.SelectRows( aTable, each [Weight] > 12 )
Table.SelectRows( aTable, (_) => _[Weight] > 12 )
Table.SelectRows( aTable, (_) => [Weight] > 12 )