f(x) = x^2
f (generic function with 1 method)
f(10)
100
A = rand(3,3)
3×3 Array{Float64,2}:
0.501893 0.961247 0.840926
0.899627 0.030949 0.669201
0.634353 0.968113 0.721053
f(A)
3×3 Array{Float64,2}:
1.6501> 1.3263 1.67168
0.903869 1.51358 1.25976
1.64672 1.33779 1.70122
위와 같이, 인자의 타입을 정의하지 않고 함수를 정의한 경우 Julia는 그때그때 들어오는 인자 타입에 따라 동작을 정의한다.
하지만, Julia에서는 명시적으로 입력 타입을 정의할수도 있다.
foo(x::String, y::String) = println("My inputs x and y are both strings!")
foo (generic function with 1 method)
foo("hello", "hi")
My inputs x and y are both strings!
foo(3, 4)
MethodError: no method matching foo(::Int64, ::Int64)
Stacktrace:
[1] top-level scope at In[12]:1
foo(x::Int, y::Int) = println("My inputs x and y are both integers!")
foo (generic function with 2 methods)
foo(3, 4)
My inputs x and y are both integers!
foo("hello", "hi")
My inputs x and y are both strings!
foo 함수의 인자를 int로 명시했는데도, foo 함수는 string을 넣었을때도 동작한다. 왜그럴까?
foo(x::Int, y::Int) 함수를 만들었을 때 우리는 기존의 함수를 덮어쓰거나 교체한 것이 아니라, foo라는 generic function에 새로운 메소드를 추가한것이다.
generic function이란 특정 동작과 연관된 추상적 개념이다.
method는 generic function을 특정 인자 타입에 대해 실제로 구현한 결과물이다.
예를 들어, +라는 generic function은 덧셈을 나타내고, float, int, matrix 등 다양한 타입의 인자를 받는 methods를 가진다.
각 generic function이 가진 method들은 methods 내장함수로 확인할 수 있다.
methods(foo)
2 methods for generic function foo:
- foo(x::Int64, y::Int64) in Main at In[15]:1
- foo(x::String, y::String) in Main at In[14]:1
methods(+)
특정 인자에 대해 foo를 호출할때마다, julia는 입력값의 타입을 추론하고 적절한 메소드에 dispatch한다. -> multiple dispatch
이런 개념덕분에 우리의 코드는 더 범용적이고 유연해질 수 있고, 특한정적인 구현보다는 추상적인 동작에 초점을 맞춰 개발할 수 있다. 또한 julia가 타입에 맞춰 효율적인 메소드를 호출하기 때문에 더 빠르게 실행될 수 있다.
어떤 메소드가 dispatch되었는지 보려면 @which 매크로를 사용한다.
@which foo(3, 4)
foo(x::Int64, y::Int64) in Main at In[15]:1
@which 3.0 + 3.0
+(x::Float64, y::Float64) in Base at float.jl:401
@which 3 + 3
+(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:86
abstract type을 인자로 가진 generic method도 만들 수 있다.
foo(x::Number, y::Number) = println("My inputs x and y are both numbers!")
foo (generic function with 3 methods)
@which foo(3.0, 4.0)
foo(x::Number, y::Number) in Main at In[26]:1
어떤 input이든 받는 duck-typed method인 fallback도 추가할 수 있다.
foo(x, y) = println("I accept inputs of any type!")
foo (generic function with 4 methods)
v = rand(3)
foo(v, v)
I accept inputs of any type!
# 9.1
foo(x::Bool) = println("foo with one boolean!")
foo (generic function with 5 methods)
foo(true)
foo with one boolean1
# 9.2
@which foo(true)
foo(x::Bool) in Main at In[32]:2