이 페이지의 선택한 이전 버전과 현재 버전 사이의 차이점을 보여줍니다.
coroutine [2012/01/21 23:41] han 항목 표제 오류; 정확한 표제로 |
coroutine [2012/01/22 12:26] (현재) kroisse constructor와의 혼동을 피하기 위해 "생성자" -> "발생자"로 수정 |
||
---|---|---|---|
줄 5: | 줄 5: | ||
===== 파이선 ===== | ===== 파이선 ===== | ||
- | 파이선 구현 중 스택리스 파이선을 사용하거나 그린릿(greenlet)을 쓰면 코루틴을 이용할 수 있습니다. 하지만 파이선에서 일반적으로 사용되는 코루틴은 생성자(generator)라고 불리는 구조입니다. | + | 파이선 구현 중 스택리스 파이선을 사용하거나 그린릿(greenlet)을 쓰면 코루틴을 이용할 수 있습니다. 하지만 파이선에서 일반적으로 사용되는 코루틴은 발생자(generator)라고 불리는 구조입니다. |
<code python> | <code python> | ||
줄 14: | 줄 14: | ||
</code> | </code> | ||
- | 보통 함수는 return 문을 이용해 하나의 값을 반환하면서 실행이 종료됩니다. 하지만 생성자는 yield 문을 써서 “여러 번 반환”합니다. 이렇게 여러 번 반환되는 값들은 일반적인 함수의 반환값을 얻을 때와는 다른 접근으로 받아 냅니다. | + | 보통 함수는 return 문을 이용해 하나의 값을 반환하면서 실행이 종료됩니다. 하지만 발생자는 yield 문을 써서 “여러 번 반환”합니다. 이렇게 여러 번 반환되는 값들은 일반적인 함수의 반환값을 얻을 때와는 다른 접근으로 받아 냅니다. |
<code python> | <code python> | ||
줄 21: | 줄 21: | ||
</code> | </code> | ||
- | 코루틴이 할 수 있는 모든 일을 생성자가 다 할 수 있는 것은 아닙니다. 하지만 일반적으로 코루틴을 사용하는 용도는 대개 생성자로 충족되고, 생성자는 쓰기 간편하므로 인기가 있습니다. | + | 코루틴이 할 수 있는 모든 일을 발생자가 다 할 수 있는 것은 아닙니다. 하지만 일반적으로 코루틴을 사용하는 용도는 대개 발생자로 충족되고, 발생자는 쓰기 간편하므로 인기가 있습니다. |
- | 파이선에는 리스트, 순서쌍, 사전 등 여러 개의 값을 집어 넣을 수 있는 내장 자료 구조가 많이 있으므로, 값 여러 개를 반환할 필요가 있다면 그냥 그런 자료 구조에 값 여러 개를 집어 넣어서 반환하면 되지 않겠느냐는 의문이 들 수도 있습니다. 시퀀스에 비해 생성자가 갖는 장점은 예를 들면 다음과 같은 것입니다. | + | 파이선에는 리스트, 순서쌍, 사전 등 여러 개의 값을 집어 넣을 수 있는 내장 자료 구조가 많이 있으므로, 값 여러 개를 반환할 필요가 있다면 그냥 그런 자료 구조에 값 여러 개를 집어 넣어서 반환하면 되지 않겠느냐는 의문이 들 수도 있습니다. 시퀀스에 비해 발생자가 갖는 장점은 예를 들면 다음과 같은 것입니다. |
<code python> | <code python> | ||
줄 33: | 줄 33: | ||
</code> | </code> | ||
- | 이 생성자는 0에서 bound까지의 숫자를 하나씩 뱉어 낼 것입니다. 물론 비슷한 코드를 다음과 같이 작성할 수도 있습니다. | + | 이 발생자는 0에서 bound까지의 숫자를 하나씩 뱉어 낼 것입니다. 물론 비슷한 코드를 다음과 같이 작성할 수도 있습니다. |
<code python> | <code python> | ||
줄 45: | 줄 45: | ||
</code> | </code> | ||
- | 그리고 이렇게 해서 만들어진 생성자나 시퀀스를 처음 다섯 항만 출력하는 코드를 짜면, 똑같은 형태가 될 것입니다. | + | 그리고 이렇게 해서 만들어진 발생자나 시퀀스를 처음 다섯 항만 출력하는 코드를 짜면, 똑같은 형태가 될 것입니다. |
<code python> | <code python> | ||
줄 67: | 줄 67: | ||
</code> | </code> | ||
- | 이 생성자는 0에서 시작하여 양의 정수를 하나씩 무한히 뱉어 낼 것입니다. 함수로 만들었다면 호출하는 즉시 무한 루프에 빠지게 되지만, 생성자라면 위의 다섯 항만 출력하는 예제에서 보듯 값을 필요한 만큼만 뽑아 쓴 뒤 버려도 됩니다. | + | 이 발생자는 0에서 시작하여 양의 정수를 하나씩 무한히 뱉어 낼 것입니다. 함수로 만들었다면 호출하는 즉시 무한 루프에 빠지게 되지만, 발생자라면 위의 다섯 항만 출력하는 예제에서 보듯 값을 필요한 만큼만 뽑아 쓴 뒤 버려도 됩니다. |
+ | |||
+ | ===== 해스켈 ===== | ||
+ | |||
+ | 언어 설계의 관점에서 해스켈의 코루틴은 살펴볼 가치가 충분합니다. 해스켈의 코루틴 상황은 대략 이렇습니다. | ||
+ | |||
+ | - 내장된 코루틴은 없습니다. | ||
+ | - 하지만 Haskell98((C로 치면 ANSI C에 해당하는 표준 명세입니다. 모든 해스켈 구현이 대체로 완벽하게 지원합니다.))만으로 [[http://random.axman6.com/blog/?p=231|코루틴을 쉽게 구현]]할 수 있습니다. 언어 차원에서 따로 지원해서 발생자를 구현한 파이선과는 대조적입니다. | ||
+ | - 코루틴보다 일반화된 접근인 컨티뉴에이션도 마찬가지입니다. 내장된 컨티뉴에이션은 없지만 Haskell98로 [[http://www.haskell.org/haskellwiki/All_About_Monads#The_Continuation_monad|쉽게 구현]]할 수 있고, 그래서 누군가 구현해 놓았고, 그것을 ''import'' 해서 써도 됩니다. | ||
+ | - 그러나 이것들은 별로 안 쓰입니다. | ||
+ | |||
+ | 먼저 왜 코루틴이 유용한데도 별로 쓰이지 않는지 알아봅시다. 이것은 해스켈의 제때 계산(lazy evaluation)이라는 특성 때문이 가장 큽니다. 해스켈은 순함수형 언어고 부작용(side-effect)이 없는 언어이므로 하나의 식별자에 연결된 값은 처음부터 끝까지 변하지 않는다는 보장, 즉 참조 투명성(referential transparency)이 있습니다. 즉, 값은 언제 계산해도 결과가 같습니다. 그렇다면 쓰지도 않을 값을 미리 다 계산해 놓을 필요가 없고, 꼭 필요할 때만 계산해도 됩니다. 따라서 해스켈에서는 무한 수열도 쉽게 만들 수 있습니다. ''[0 ..]''이라는 짧은 코드로 0부터 시작하여 양의 정수를 하나씩 출력하는 무한 수열을 만들 수 있습니다. 이 무한 수열의 처음 다섯 항을 구하기 위해 다음과 같은 코드를 작성하면, | ||
+ | |||
+ | <code haskell> | ||
+ | take 5 [0 ..] | ||
+ | </code> | ||
+ | |||
+ | 무한 수열을 끝까지 계산한 뒤 처음 다섯 항을 취하려는 어리석은 시도를 하지 않고, 처음 다섯 항만 계산이 되는 대로 즉시 내놓습니다. 그 이후의 항들은 아직 “꼭 필요했던” 적이 없으므로 계산되지 않은 채로 남습니다. | ||
+ | |||
+ | 더 복잡한 경우에도 마찬가지입니다. | ||
+ | |||
+ | <code haskell> | ||
+ | infinite :: [Int] | ||
+ | infinite = first : next | ||
+ | where | ||
+ | first = ... | ||
+ | next = ... | ||
+ | </code> | ||
+ | |||
+ | 이 리스트의 첫 항에 ''head infinite'' 코드로 접근할 경우 ''next'' 부분은 전혀 계산되지 않습니다. ''next'' 부분에 어떤 복잡하고 어렵고 비싼 계산이 들어 있어도 상관이 없습니다. 이것은 다음과 같은 파이선 코드가 | ||
+ | |||
+ | <code python> | ||
+ | def infinite(): | ||
+ | yield 1 | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | ''...''의 부분에 어떤 복잡한 계산, 심지어 무한 루프를 갖고 있더라도 이 발생자로부터 첫 항만 요구하는 코드를 쓸 경우 아무 문제 없이 값을 받아낼 수 있는 것과 마찬가지입니다. | ||
+ | |||
+ | 파이선이 시퀀스로 해결할 수 없어서 코루틴을 도입했던 과제를, 해스켈은 언어의 특성 덕분에 시퀀스로 해결이 가능해서 코루틴의 필요성을 느끼지 못하는 경우입니다. | ||
+ | |||
+ | 하지만 해스켈에서도 코루틴과 비슷한 개념이 요구되는 경우가 없는 것은 아닙니다. (계속) |