概要
在学习 Julia 之前,我通过 Mathematica(MMA) 学习的函数式编程。MMA 的一些函数式操作很方便,而 Julia 添加这些功能往往只需要一两行代码。所以想请问:
- Julia 有没有添加这些特性的考虑
- 或者说怎么向 Julia 建议这类想法
- 此外,这类特性是否会带来什么损失?
- 放在 Base 中的函数有什么要求,多了会带来什么影响?
以下是具体建议,包括两方面:
- 给支持函数式编程的函数添加特性
- 添加新函数
添加特性
Julia 中的函数是“一等公民”,也即函数与普通变量平等看待。但有一些函数式编程特性 Julia 没有使用,或者说没有完全使用。
逻辑运算
- Julia 的
!, >, in
等逻辑运算符支持函数式,比如
可以考虑新增的运算f = i -> true !f # 等同于 (args...) -> !(f(args...)) >=(2) # 等同于 i -> (i >= 2)
&&, ||
f && g # 等同于 i -> f(i) && g(i) ## 使用场景 filter(>=(3) && <=(6), 1:100) # 返回 [3, 4, 5, 6] findall(in([1,2,3]) || <(0), data)
柯里化
维基百科:柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数的技术。
比如从 1:10 中筛选大于 3 的数,一般方式为
filter(>(3), 1:10)
当这个操作经常用到时,我们希望将其独立出来
f = filter(>(3))
f(1:10)
f(2:9)
也即,当数据省略时,也能正常输入,只是返回值是算子(函数)。利用多重派发,这个只需要一行代码
filter(f::Function) = data - > filter(f, data)
p.s. 这些只是简单的修改,但可以更好地发挥函数式的编程特性
添加函数
以下函数可以在 MMA 官网教程的这一页找到,其中只有函数 Fold 存在对应的 Julia 版本 foldl
。
-
NestWhile函数,迭代直到判断为否
function nest_while(f::Function, val::T, chk::Function)::T where T while chk(val) val = f(val) end val end
-
NestWhileList 函数,将过程结果一起返回
function nest_while_list(f::Function, val::T, chk::Function)::Vector{T} where T res = [val] while chk(val) val = f(val) push!(res, val) end res end
- 例1:
nest_while_list(i->i//2, 100, >=(1))
返回值为[100, 50, 25, 12, 6, 3, 1, 0]
- 例2:一些 LeetCode 题,比如 172,代码可以简化到一行
trailing_zeroes(n::Int) = sum(nest_while_list(i->i÷5, n÷5, >(1)))
- 例1:
-
Nest 函数,比如
mma_nest(f, val, 3) => f(f(f(val)))
function mma_nest(f::Function, val::T, times::Int)::T where T ## e.g. mma_nest(f, val, 3) | returns f(f(f(val))) for _ in 1:times val = f(val) end val end
-
NestList 函数,将过程结果一起返回
function mma_nest_list(f::Function, val::T, times::Int)::Vector{T} where T ## e.g. mma_nest(f, val, 2) | returns [val, f(val), f(f(val))] res = Vector{T}(undef, times + 1) res[1] = val for i in 2:(times + 1) res[i] = val = f(val) end res end
比如
mma_nest_list(f, val, 2) => [val, f(val), f(f(val))]
还有一些使用频率相对不高的函数,比如 FoldList, FixPointList
。