为了更好的能够采用相同的概念产生不同的行为,函数就需要在不同的位置进行定义,根据不同的类和常数,产生不同的行为。函数(function)的一个可能的行为(behavior),称为一个方法(method)。选择函数执行哪种方法称为派发,Julia可以通过 函数声明个数以及声明类型来执行派发。 通过函数所有声明来选择派发,而不是通过第一个声明,称为多重派发。
方法冲突
g(x::Float64, y) = x^2+y # y 没有指定类
g(x,y::Float64)=x+y #x没有指定类
g(3.0,2.0) # MethodError: g(::Float64, ::Float64) is ambiguous. Candidates:
g(x, y::Float64) in Main at In[3]:1
g(x::Float64, y) in Main at In[1]:1
Possible fix, define
g(::Float64, ::Float64)
g(x::Float64, y::Float64) = x-y
g(3.0,2.0) # 1.0
参数化方法
和类一样,方法也可以参数化
same_type(x::T,y::T) where {T} = True
same_type(x,y) = False
same_type(3,0.5) #False
刚方法通过参数化类可以实现判断两个变量属不属于相同的类。
myappend(v::Vector{T}, y::T) where{T} = [v..., y]
myappend([2,3,4],3) # 2,3,4,3
可以实现同种类型变量数组的追加。其中 … 代表任意长度数组。
比如,想实现不同长度数组和变量之间的求和,可以通过定义
add(x...) = reduce(+, x)
add([3,2,1]..., 1) # 7
其中reduce也是一个特别实用的函数,它提供了对某一集合itr,在初始条件init下实行运算op的功能,reduce(op, itr; dims=: ; [init])
reduce(+,[2,3,4]) #9
reduce(+, [2,3,4]; init=10) #19
a=reshape(Vector(1:16), (4,4))
reduce(max, a; dims=2) # 15,16
跑题了
通过参数化可以了解很多内部函数实现的思路,比如
mytypeof(a::T) where {T} = T
mytypeof(32) # Int64
可以实现typeof功能。
当然,在类型声明里也可以给出子类型限制条件,比如
same_type_num(x::T, y::T) where{T<:Number} = true
same_type_num(x::Number, y::Number) = false
same_type_num(3,2) #true
same_type_numeric(Int32(1), Int64(2)) #false
方法重新定义 (基本没看懂)
当重新定义一个方法或者增加一个方法时,为了稳定编译和提高效率,这些更改并非立即生效的。
function tryE()
@eval newfun2()=3
newfun2()
end
tryE # MethodError: no method matching newfun2()
直到第二次执行才会正确,如果想立即生效,可以通过
function tryeval2()
@eval newfun2() = 2
Base.invokelatest(newfun2)
end
另外一个例子
f1(x) = "Origin method"
g1(x) = f1(x)
t = @async f1(wait()); yield();
f1(x::Int)="A new method"
f1(x::Type{Int}) = "defination for Type{int}"
f1(1) "A new method"
g(1) "A new method"
fetch(schedule(t, 1)) #Origin method
t = @async f(wait()); yield();
fetch(schedule(t, 1)) #" A new method)
其中fetch函数是抓取 宏async 将Task中的表达式打包并放入机器列表(scheduler)里。其中Task 是执行指定函数命令,如
a() = sum(I for I in 1:100)
b = Task(a);
这里面b就是一个尚未运行的可执行的Task。
迭代派发
为了能够派发一个多层参数化的命令,最好的办法是将每一层放在独立的函数里进行派发。例如,派发单元类的数组,经常会出现混乱的状况。通常的做法是首先将它派发给存储类(container type)然后在循环进基于元素类(eltype)的方法里。 多数情况下,算法会自动执行这种分级操作,但某些情况可能需要自己手动执行这些规则。例如求解矩阵加法的逻辑
+(a::Matrix, b::Matrix) = map(+, a, b) #通过map算法进行派发
+(a,b) = +(promote(a,b)...) #将不同类型的元素进行统一
下面还有很多,看不大懂。。。 先这么多吧。