소개

5장_이터레이터, 시퀀스 그리고 프로듀서

문(Statement) vs 함수(function)
문(Statement)은 하나 이상의 기계어 명령어 (Instruction)로 치환
반복문(Iteration Statement)은 코드 중복을 제거
# Expression println(5) println(4) println(3) println(2) println(1) # Iteration Statement for (x in 5 downTo 1) { println(n) }
JavaScript
복사
함수 선언(function)은 문을 메모리 주소로 치환후 표현식으로 호출함으로써 재사용성을 높힘
# Function Declaration fun sequencePrinter(num: Long) { for (x in 5 downTo 1) { println(n) } }
JavaScript
복사
반복문(Statement)은 순회 가능한(Iterable)한 ‘집합’과 이에 적용할 ‘연산’이 결합됨
for oddSequencePrinter(num: Long) { for (x in 5 down To 1) { if (x % 2 == 1) { println(n) } } }
JavaScript
복사
반복문(Statment)을 표현식(Expression)으로 치환
# Expression fun numberGenerator () { val numbers = listOf(1, 2, 3, 4, 5) val listIterator = numbers.listIterator()) while (listIterator.hasNext()) { val num = listIterator.next() yield num } } (-(1..5)).filter { x -> x % 2 != 0 }.foreach { println(it) }
JavaScript
복사
이터레이터를 사용하는 이유
(반복)문의 표현식 (이터레이터와 시퀀스) 치환
집합과 연산의 분리
집합에 대한 연산을 따로 처리할 수 있음
Composition을 통해 재활용성을 높힐 수 있음
게으른(Lazy) 연산 가능 (Iterator [x], Sequence [o])
(-(1..5)).filter { x -> x % 2 != 0 }.forEach { println(it) }
JavaScript
복사
언어 차원의 이터레이터 지원
public interface Iterator<out T> { public operator fun next(): T public operator fun hasNext(): Boolean } val iterator: Iterator<Any> = iterator { yield(5) yield(4) yield(3) yield(2) yield(1) } iterator.forEach { println(it) }
JavaScript
복사
시퀀스를 사용하는 이유
이터레이터의 한계: Lazy Evaluation을 지원하지 않음
시퀀스(Stream) : 이터레이터 (Iterator) + Lazy Evaluation + Cursor
val iter = iterator { yield(5) yield(4) yield(3) yield(2) yield(1) } while (iter.hasNext()) { println(iter.next()) } val eagor = listOf(5, 4, 3, 2, 1) println("before sum ") val eagorSum: List<Int> = eagor.map { print("$it "); it * it } println(eagorSum.sum()) val lazy = sequence { yield(5) yield(4) yield(3) yield(2) yield(1) }.map { println("$it "); it * it } println("before sum ") val lazySum = lazy.sum() // terminal println(lazySum)
JavaScript
복사
프로듀서 - 컨슈머를 사용하는 이유
순회 중 중단 재개 가능 (suspend / resume)
iterator와 sequence는 중단점 없이 순차 실행
생산과 소비의 주체 및 시점이 달라지므로, lazily consume 가능
Invocation과 Execution의 분리
프로듀서 생산 - 소비
val context = newSingleThreadContext("myThread") val producer = GlobalScope.produce(context) { for (i in 0..9) { send(i) } } producer.consumeEach { println(it) }
JavaScript
복사
채널 메시지 생산 - 소비
val channel = Channel<Int>() val sender = GlobalScope.launch { for (i in 0..9) { channel.send(i) } } while(!channel.isClosedForReceive && !channel.isEmpty) { println(channel.receive()) }
JavaScript
복사
활용예
override fun onCreate(savedInstanceState: Bundle?) { ... GlobalScope.launch { loadMore() } }
JavaScript
복사
override suspend fun loadMore() { val producer = ArticleProducer.producer if (!producer.isClosedForReceive) { val articles = producer.receive() GlobalScope.launch(Dispatchers.Main) { findViewById<ProgressBar>(R.id.progressBar).visibility = View.GONE viewAdapter.add(articles) Log.d("Main", "Currently has ${viewAdapter.itemCount} articles") } } }
JavaScript
복사
interface ArticleLoader { suspend fun loadMore() } class ArticleAdapter(private val loader: ArticleLoader) : RecyclerView.Adapter<ArticleAdapter.ViewHolder>() { private val articles: MutableList<Article> = mutableListOf() private var loading = false class ViewHolder(val layout: LinearLayout, val feed: TextView, val title: TextView, val summary: TextView ) : RecyclerView.ViewHolder(layout) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val layout = LayoutInflater.from(parent.context) .inflate(R.layout.article, parent, false) as LinearLayout val feed = layout.findViewById<TextView>(R.id.feed) val title = layout.findViewById<TextView>(R.id.title) val summary = layout.findViewById<TextView>(R.id.summary) return ViewHolder(layout, feed, title, summary) } override fun getItemCount(): Int { return articles.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val article = articles[position] // request more articles when needed if (!loading && position >= articles.size - 2) { loading = true GlobalScope.launch { loader.loadMore() loading = false } } holder.feed.text = article.feed holder.title.text = article.title holder.summary.text = article.summary } fun add(articles: List<Article>) { this.articles.addAll(articles) notifyDataSetChanged() } }
JavaScript
복사