在网易云课堂上直接搜索:Julia教程 ,就可以找到,教程的全名是:Julia教程 从入门到进阶
这是国内第一个免费的完整的Julia视频教程,非常适合Julia的入门。有兴趣的朋友可以去学习一下。
教程链接:
Julia教程
函数和方法
函数
函数定义
function f1(x,y)
x + y # 可以不带return,当然也可以写成return x + y
end
>>f (generic function with 1 method)
f(1,2)
>>3
函数的最后一行是不需要加return的,return一般用于在函数中间返回时使用。因为Julia的代码都是表达式(在后面的元编程一节中会讲到),表达式是有返回值的,要么是nothing
,要么是别的,因此函数最后一行默认就是返回值,无需再加return
;如果一个函数不想有返回值,那再最后一行写个nothing
即可。
可以指定函数输出的类型
function g(x, y)::Int8
return x * y
end
typeof(g(2,3))
>>Int8
符号也可以当做函数使用
+(1,2,3)
>>6
f2 = +
f2(1,2,3)
>>6
给函数添加说明
"simple add function"
function funcAdd(x, y)
x + y
end
使用@doc funcAdd
可以看到函数前的注释,如果是在REPL中定义的函数,则可以在help
模式下查看函数使用说明
匿名函数
map(x->x*2 + 1, [1,2,3,4])
>>4-element Array{Int64,1}:
3
5
7
9
多返回值
function f3(x,y)
x+y, x-y
end
f3(3,4)
>>(7,-1) #返回的是一个tuple
minVal(x,y) = (x < y) ? x : y;
可变参数
function f4(x...)
r1 = length(x)
r2 = x[r1]
return r1,r2
end
println(f4(4,6,9))
>>(3,9)
println(f(11,15,(18,20)))
>>(3, (18,20))
默认参数 关键字参数
function f5(x,y=1)
return x + y
end
f5(3)
f5(5,6)
f5(x=7,y=8)
function f6(x;y,z=3)
return x + y + z
end
f6(3,y=5)
f6(4,y=3,z=8)
参数类型
Julia虽然属于动态语言,但函数定义时可指定其参数类型
function f7(x::Int8)
x + 3
end
f7(Int8(10))
函数的其他使用方法
map
function funcAbs(x)
x >=0 ? x : -x
end
map(funcAbs, -3:3)
reduce
fucntion add(x, y)
x + y
end
reduce(add, 1:10)
>>55
reduce(+, 1:10)
>>55
|>
操作符,对于运算顺序非常直观
"abcde" |> uppercase
[i for i in 1:5] |> join
当然,Julia中函数还有更简单灵活的定义方式
f8(x::Int64,y::Int64) = 2*x + y
f8(4,3)
>>11
看到这里,是不是更加喜欢Julia了!它就是这么简洁明了。
方法
函数和方法的区别
同样的函数,可以有不同的方法,比如加法函数,可以实现整数加法,浮点数加法和复数加法等,他们都是实现加法功能,即他们是同一个函数,但他们的实现方法不一样,可以理解位C++中的重载。
在REPL上可以看到我们定义的函数有几种方法。Julia会选择更加专用的那一个方法。
function f9(x::Int64, y::Int64)
x + y
end
>>f9 (generic function with 1 method)
表示该函数只有一个方法
如果我们再定义一个f9,参数类型位Float64
function f9(x::Float64,y::Float64)
x + y
end
>>f9 (generic function with 2 methods)
这时当我们执行f(2,3)
编译器会自动选择更加专用的方法来执行,也就是第一个方法。
我们也可以直接输入+
看到加号(也是一个函数)的方法数
+
>>+ (generic function with 170 methods)
可以使用methods(f9)
来看f9的具体方法
但如果两个方法都不更加专用,就会出现分歧。
g(x::Int64, y) = 2x + y
g(x, y::Int64) = x + 2y
g(2,3.0)
>>7.0
g(2.0,3)
>>8.0
但如果是g(2,3)
,那两个方法没有更加专用的,这时就会报error,解决方法就是定义一个更加专用的方法:g(x::Int64, y::Int64)
。
参数方法
我们先定义一个简单的方法,对于任何输入,结果都是false
f10(x,y) = false
f10(1,2)
>>false
再增加一个方法
f10(x::T, y::T) where {T} = true
f10(1,2)
>>true
f10(1,2.1)
>>false
第二个方法的意思是当两个参数的类型相同时,返回true
当然,这种方式还可以针对矩阵进行操作
f11(v::Vector{T}, x::T) where {T} = [v..., x]
f11([1,2,3],4)
>>4-element Array{Int64,1}:
1
2
3
4
f11([1,2,3],4.2)
>>ERROR: MethodError
还可以对子类型参数进行约束
f12(x::T, y::T) where {T<:Number} = true
多重分派
分派就是指根据变量的类型选择相应的方法,单分派指的是指根据第一个参数类型去选择方法。
下面我们举一个Python中的例子,Python因为在函数定义时是不知道参数类型的,所以一般没有单分派;但Python中提供了单分派的修饰符,可以实现单分派的功能。
from functools import singledispatch
@singledispatch
def func(arg, verbose=False):
print('initial...\n')
@func.register(int)
def _(arg, verbose=False):
print(arg)
func(1)
>>1
func(2.3)
>>initial...
可以看出,函数func()的结果只跟第一个参数的类型有关,跟后面的参数没有关系,这就是单分派。
使用函数的所有参数,而非只用第一个,来决定调用哪个方法被称为多重分派。多重分派对于数学代码来说特别有用,人工地将运算视为对于其中一个参数的属于程度比其他所有的参数都强的这个概念对于数学代码是几乎没有意义的:x + y 中的加法运算对 x 的属于程度比对 y 更强?一个数学运算符的实现普遍基于它所有的参数的类型。即使跳出数学运算,多重分派是对于结构和组织程序来说也是一个强大而方便的范式。
优化方法的使用
- 只根据一个参数分派
Julia是多重分派的模式,那如果我们在定义方法的时候想只根据第一个参数分派怎么办?我们可以采用“名字级联”的方式,在内部做好分派。例如我们可以采用下面的方式:
f(x::A, y) = _fA(x, y)
f(x::B, y) = _fB(x, y)
这样对于函数f()来说,只根据第一个参数就可以进行分派了。
微信公众号:Quant_Times