就像我们在先前所展示的那样,当一个函数需要另一个函数作为其第一个参数值的时候,do
代码块就能够派上用场了:
这里调用的函数Base.MPFR.setprecision
的签名是这样的:
setprecision(f::Function, [T=BigFloat,] precision::Integer)
它的第一个参数是Function
类型的,即代表函数的数据类型。
为了完整的演示,我们需要先稍微改造一下之前定义过的函数map1
。如下所示:
现在,我们可以像下面这样调用改造后的map1
函数:
julia> map1(e->e*10, [1,2,3,4])
4-element Array{Int64,1}:
10
30
40
julia>
或者,在调用它的时候携带一个代码块:
我们这次一共向 REPL 环境输入了三行代码。第一行代码包括针对map1
函数的调用表达式map1([1,2,3,4])
、关键字do
以及标识符x
。实际上,后两者与第二行的表达式x*10
和第三行的关键字end
共同组成了一个do
代码块。
请注意,虽然map1
函数的必选参数有两个,但我们只在调用表达式中传给了它一个参数值。你应该也看出来了,被传入的这个参数值是给该函数的第二个参数的。那么,第一个参数值在哪里呢?
不过,这有一个限制,那就是:do
代码块代表的匿名函数只能有一个参数。当然了,我们可以通过一些手段突破这个限制。为了加以说明,我们再来定义一个map1
方法:
[f((e, extra)) for e in vec]
end
map1 (generic function with 2 methods)
这个map1
方法有三个参数。参数extra
代表了额外的附加值。另外,这个方法传给f
的参数值是元组(e, extra)
,而不是之前的单一变量e
。相应的,我们调用该方法时所携带的do
代码块也需要有所变化:
可以看到,我们在这里的调用表达式中向这个map1
方法传入了第二个参数值[1,2,3,4]
和第三个参数值1
,而第一个参数值仍然由后面的do
代码块代表。但不同的是,在do
关键字右边的是由一个圆括号包裹的两个标识符。你也可以把这一小段代码看成一个元组。因为它表示的只是一个参数,而不是两个。这也正是这个map1
方法在调用f
时传入(e, extra)
而非e
和extra
的原因。
do
代码块的意义在于,当我们需要临时定义一个函数并把它作为第一个参数值传入另一个函数的时候,使用do
代码块会让代码变得非常的清晰。因为它看起来就是(事实上也是)一个独立的代码块。我们可以在这样的代码块中写入各种复杂的逻辑,而丝毫不会对前面的调用表达式以及周边的代码造成视觉上的干扰。如果在这种情况下不使用do
代码块,那么就很可能会降低相关代码的可读性,甚至会间接地导致一些代码编写方面的错误。由此看来,代码块在特定的场景下是很有用处的。