Flux in Julia/Learning Julia (Intro_to_Julia)

10. Multiple dispatch (한글)

딥스탯 2018. 10. 2. 22:31
10_Multiple_dispatch(한글)

Multiple dispatch

참고문헌

https://github.com/JuliaComputing/JuliaBoxTutorials/tree/master/introductory-tutorials/intro-to-julia (github : JuliaComputing/JuliaBoxTutorials/introductory-tutorials/intro-to-julia/)

Topics:

  1. 친근한 예제로 시작하기
  2. Input의 type 특정지어주기
  3. 연습문제

함께보기

여기서 우리는 Julia의 주요 특징 중 하나인 multiple dispatch에 대해 살펴볼거다.

Multiple dispatch 는 소프트웨어를 일반적이고 빠르게 만들어준다!

친근한 예제로 시작하기

Multiple dispatch를 이해하기 위해서, 이전에 본 적 있는 예제로 시작할거다.

함수를 선언할 때, 아래와 같이 input의 type에 대한 아무런 정보를 주지 않고 만들 수 있다.

In [1]:
f(x) = x^2
Out[1]:
f (generic function with 1 method)

그러면 Julia는아래와 같이 input이 그럴듯한지 아닌지 스스로 판단하게 된다.

In [2]:
f(10)
Out[2]:
100
In [3]:
f([1, 2, 3])
MethodError: no method matching ^(::Array{Int64,1}, ::Int64)
Closest candidates are:
  ^(!Matched::Float16, ::Integer) at math.jl:782
  ^(!Matched::Missing, ::Integer) at missing.jl:120
  ^(!Matched::Missing, ::Number) at missing.jl:93
  ...

Stacktrace:
 [1] macro expansion at ./none:0 [inlined]
 [2] literal_pow at ./none:0 [inlined]
 [3] f(::Array{Int64,1}) at ./In[1]:1
 [4] top-level scope at In[3]:1

Input의 type 특정지어주기

그러나 Julia에게 어떤 input의 type을 허용하는지 명시적으로 알려줄 수 있다.

예를 들어, String을 input으로 사용하는 foo 함수를 작성해 보자.

In [4]:
foo(x::String, y::String) = println("x랑 y 둘 다 string이다!")
Out[4]:
foo (generic function with 1 method)

보이다시피, input 다음에 콜론을 2개 써서 xy의 type을 String으로 제한시켰다.

이제 fooString에만 작동하고 다른 input에는 작동하지 않는다.

In [5]:
foo("hello", "hi!")
x랑 y 둘 다 string이다!
In [6]:
foo(3, 4)
MethodError: no method matching foo(::Int64, ::Int64)

Stacktrace:
 [1] top-level scope at In[6]:1

foo 함수가 integer(Int)에도 작동하게 하기 위해서, ::Int 를 사용해서 다음과 같이 foo를 정의할 수 있다.

In [7]:
foo(x::Int, y::Int) = println("x랑 y 둘 다 integer다!")
Out[7]:
foo (generic function with 2 methods)
In [8]:
foo(3, 4)
x랑 y 둘 다 integer다!

이제 foo는 integer에도 작동한다. 그러나, 여전히 xyString일 때도 작동함을 아래를 통해 볼 수 있다.

In [9]:
foo("hello", "hi!")
x랑 y 둘 다 string이다!

이게 multiple dispatch의 핵심이다. 아래와 같이 선언했을 때,

foo(x::Int, y::Int) = println("My inputs x and y are both integers!")

아래 코드는 위의 코드를 덮어쓰기 하거나 대체하지 않는다.

foo(y::String, y::String)

대신에, foo라고 불리는 generic functionmethod를 추가로 더했을 뿐이다.

generic function은 특정 연산에 대한 추상적인 개념이다.

예를 들어, generic function + 은 덧셈에 대한 개념이다.

method특정 argument type에 대한 generic function의 구현이다.

예를 들어, +에는 실수, 정수, 행렬 등등을 수용하는 여러 method가 있다.

methods를 이용해서 foo에 얼마나 많은 method가 있는지 볼 수 있다.

In [10]:
methods(foo)
Out[10]:
2 methods for generic function foo:
  • foo(x::Int64, y::Int64) in Main at In[7]:1
  • foo(x::String, y::String) in Main at In[4]:1

참고 : 더하는 데는 얼마나 많은 방법이 있을까?

In [11]:
methods(+)
Out[11]:
163 methods for generic function +:

그래서 이제 foo는 integer와 string에 대해서 사용할 수 있다. 특정 input에 foo를 사용하면, Julia는 어떤 타입의 input인지 추론하고, 적절한 method를 빠르게 전달한다. 이것이 multiple dispatch다.

Multiple dispatch는 우리 코드를 일반적이고 빠르게 만든다. 우리 코드는 일반적이고 유연해질 수 있는데, 왜냐하면 특정 구현(specific implementations)으로 코드를 쓰는 것이 아니라 덧셈과 곱셈 같은 추상 연산(abstract operations)으로 코드를 쓸 수 있기 때문이다. 동시에, 빨리 실행되는데, 그 이유는 관련된 타입에 있어서 효율적인 method를 불러낼 수 있기 때문이다.

generic function을 불러낼 때, 어떤 method가 전달되는지 보기 위해서는, @which macro를 사용한다.

In [12]:
@which foo(3, 4)
Out[12]:
foo(x::Int64, y::Int64) in Main at In[7]:1

더하기에도 @which를 사용해보자.

In [13]:
@which 3.0 + 3.0
Out[13]:
+(x::Float64, y::Float64) in Base at float.jl:395

함수 foo에 다른 method를 더 적용시킬 수 있다. IntFloat64 등등 숫자로 생각되는 모든 object를 포함하는 추상적인 typenumber를 사용하는 method를 추가하자.

In [14]:
foo(x::Number, y::Number) = println("x랑 y 둘 다 number이다!")
Out[14]:
foo (generic function with 3 methods)

This method for foo will work on, for example, floating point numbers:

In [15]:
foo(3.0, 4.0)
x랑 y 둘 다 number이다!

또한 이전에 한 적이 있는, any type을 input으로 받는 duck-typed method를 foo에 추가할 수 있다.

In [16]:
foo(x, y) = println("나는 any type을 input으로 넣어도 실행된다!")
Out[16]:
foo (generic function with 4 methods)

지금까지 foo에 적용한 method를 감안할 때,이 method는 non-numbers를 foo의 input으로 넣을 때 마다 적용될 것이다.

In [17]:
v = rand(3)
foo(v, v)
나는 any type을 input으로 넣어도 실행된다!

연습문제

10.1

함수 foo를 확장하자. Bool type 하나를 input으로 넣을 때, "foo with one boolean!" 이라는 문구가 출력되는 method를 추가하자.

In [18]:
foo(x::Bool) = println("boolean 한 개로 실행됐다!")
Out[18]:
foo (generic function with 5 methods)
In [19]:
methods(foo)
Out[19]:
5 methods for generic function foo:
  • foo(x::Bool) in Main at In[18]:1
  • foo(x::Int64, y::Int64) in Main at In[7]:1
  • foo(x::String, y::String) in Main at In[4]:1
  • foo(x::Number, y::Number) in Main at In[14]:1
  • foo(x, y) in Main at In[16]:1

10.2

아래 코드를 실행할 때, 위에서 적용한 method가 전달되고 있는지 확인하라.

foo(true)
In [20]:
foo(true)
boolean 한 개로 실행됐다!
In [21]:
@which foo(true)
Out[21]:
foo(x::Bool) in Main at In[18]:1
In [22]:
@assert foo(true) == "boolean 한 개로 실행됐다!"
boolean 한 개로 실행됐다!
AssertionError: foo(true) == "boolean 한 개로 실행됐다!"

Stacktrace:
 [1] top-level scope at In[22]:1