文章目录
Julia作为科学计算语言,对函数式编程提供了良好的支持,不仅支持匿名函数,还支持管道操作,并且为函数复合提供了运算符,十分强大且便利。
可变参数
有一些函数可以传入任意个数的参数,例如最大值函数
python
> max(1,2,3)
3
这种机制在Julia中由arg...
实现,比如下面定义一个Max
函数
python
function Max(arg...)
M = -Inf
for a in arg
if a > M
M = a
end
end
return M
end
Max(3,1,4,5) # 返回5
其逻辑是,把3,1,4,5
映射为arg...
,则arg
自动变成[3,1,4,5]
,从而可以被迭代。
反过来说,...
可以理解为把一个数组展开,效果如下
python
Max([3,1,4,5]) # 报错!!!
Max([3,1,4,5]...) # 返回5,等价于Max(3,1,4,5)
匿名函数
目前我们讲了两种定义函数的方法,一种类似于f(x)=x^2
,另一种则用function...end
来实现,这两种写法均把一段映射和一个名字等同起来了。如果在创建函数时,并不声明函数的名字,那么就相当于创建了一个匿名函数,比如
python
function (x)
x^2
end
这个就是匿名函数。当然,这个匿名函数毫无意义,因为没有名字就没法调用,但如果写成如下形式,就可以调用了。
python
f = function (x)
x^2
end
f(5) # 25
在这个过程中,函数名f
和函数的实际内容x->x^2
被分离开了,相当于把函数当作一个变量赋值给了f
,这就是函数式编程的核心思想:函数也是一种变量。
匿名函数还有一种更为简单的写法,这种写法一般也叫做lambda表达式
python
f = x->x^2
f(5) # 25
事情到了这个地步,量变也就引起了质变,匿名函数支持一种更加玄幻的写法,即不通过函数名,直接调用
python
(x->x^2)(5) # 返回25
函数式
函数既然可以被赋值,那么也自然可以作为参数在另一个函数中传递,比如下面写一个函数生成器,用以生成N次方函数
python
function gen(N)
x->x^N
end
其输入是一个整数,输出为一个函数,下面测试一下
python
e5 = gen(5)
e5(3) # 返回243,即3的5次方
此外,Julia提供了Lisp家族的书写风格,即可以把所有的二元运算改写成函数的形式,示例如下
python
+(1,2,3) # 6
*(2, +(3, 5)) # 16, 即2*(3+5)
函数复合
Julia还提供了Ocaml家族的链式风格,用以完成函数复合,比如下面的表达式用于求1,2,3,4,5的均方根
python
[1:5;] |> (x->x.^2) |> (x->sum(x)/length(x)) |> sqrt
其中经过三层管道
- 对每个元素求平方
- 取平均值,由于Base中不提供平均值函数,所以用sum和length自己做了一个
- 开根号
管道起到的作用其实是函数的复合,Julia为函数复合提供了运算符∘
,在命令行中可输入\circ
然后按下tab键即可,有了这个就可以将上面的三个函数合在一起了,但需要注意,其书写顺序和管道操作|>
正好相反,对于f∘g∘h(x)
来说,其运算顺序为f(g(h(x)))
,而对应的管道计算应该表示为x|> h |> g |> f
。
python
std = sqrt ∘ (x->sum(x)/length(x)) ∘ (x->x.^2)
std([1,2,3,4,5]) # 3.3166247903554