-
[Lua] 반복자 (Iterator)와 일반 for문Lua 2025. 2. 19. 16:35
for문에 대해서는 이전 글 ( [Lua] 제어 구문 (if, while, repeat, for, break, goto...) )에서 간단하게 살펴본 바 있습니다.
루아의 for문은 일반 for문과 수치 for문으로 구분됩니다.
일반 for문은 반복자가 사용되는 대표적인 문법 중 하나입니다.
이전 글에서는 간단하게 기본 라이브러리를 이용한 사용법을 살펴보았습니다.
이번 글에서는 일반 for문과, 거기서 사용되는 반복자에 대해 조금 더 상세히 살펴보도록 하겠습니다.
0. 반복자란
특정 콜렉션의 원소를 순회할 수 있게 구성한 코드를 반복자라 합니다.
반복자는 자신이 참조하는 콜렉션, 다음으로 반환할 원소의 위치 등에 대한 정보를 가지고 있는 것이 일반적입니다.
이러한 요구사항을 만족시키기 위해 클로저를 주로 사용합니다.
function itr_fact(c) local i = 0 return function() i = i + 1; return c[i] end end foo = {6,2,3,1,6,2,3} itr = itr_fact(foo)
위 코드의 경우, 함수 itr_fact는 반복자를 만드는 팩토리 함수입니다.
해당 함수를 통해 만들어진 클로저 itr이 반복자가 됩니다.
1. 일반 for문에서 반복자의 사용
더보기이전 글에서 살펴본 pairs함수도 반복자입니다.
pairs함수는 현재 위치 및 현재 인덱스를 반환하는 반복자라고 볼 수 있습니다.
0번 문단의 예제에 있는 반복자를 반복문에서 사용할 경우 다음과 같이 사용할 수 있습니다.
itr = itr_fact(foo) while true do local elm = itr() if elm == nil then break end print(elm) end
for i in itr_fact(foo) do print(i) end
각각 while문과 for문에서 반복자를 사용하여 리스트를 순회한 예제입니다.
구조적으로 for문이 더 간결한 것을 볼 수 있습니다.
2. 일반 for문 살펴보기
더보기일반 for문의 일반형은 다음과 같습니다.
for {var_list} in {exp_list} do -- Work end
var_list는 변수들을 쉼표로 구분하여 나열합니다.
exp_list는 표현식을 쉼표로 구분하여 나열합니다.
일반 for문은 실행 될 때, 우선 표현식을 계산합니다.
표현식의 결과는 순서대로 반복자 함수, 불변 상태, 제어 변수 초기값이 됩니다.
초과되는 값은 버려지며, 모자란 값은 nil이 됩니다.
계산 이후, 반복문은 표현식이 반환한 반복자 함수를 통해 불변 상태와 제어 변수를 업데이트하여 반복문을 수행합니다.
각각의 결과값은 다음과 같은 역할을 수행합니다.
- 반복자 함수 : 컬렉션의 순회 방식을 결정하는 함수입니다. 불변 상태와 제어 변수를 인자로 호출됩니다.
- 불변 상태 : 반복문이 실행되는 동안 고정되는 값 입니다. 주로 순회할 콜렉션이 인자로 전달됩니다.
- 제어 변수 : 반복문의 진행 여부를 정하는 제어 변수입니다. 반복자 함수에 의해 업데이트됩니다.
예제를 살펴보도록 하겠습니다.
for v1, v2 ... vN in exp_list do -- Code end
do local itr_fuc, collection, state = exp_list while true do local v1, v2 ... vN = itr_func(collection, state) state = v1 if state == nil then break end -- Code end end
위 for문의 코드는 아래의 while문 코드와 같이 해석될 수 있습니다.
일반 for문은 반복자 함수를 통해 제어 변수가 nil이 될 때 까지 반복됩니다.
구체적인 예시입니다.
foo = {6,2,3,1,6,2,3} function func(arr, i) i = i + 1 local val = arr[i] if val then return i, val end end function my_itr(arr) return func, arr, 0 end -- Case 1 for i, v in func, foo, 0 do print(i, v) end -- Case 2 for i, v in my_itr(foo) do print(i, v) end
반복자 함수, 불변 상태, 제어 변수를 생각하며 코드를 읽어주세요.
Case1과 2는 서로 동일한 코드입니다.
차이점은 반복자 함수, 불변 상태, 제어 변수의 계산 방식입니다.
1은 직접적으로 전달했고, 2는 추가적인 함수를 거쳐 전달했습니다.
3. 반복자와 클로저
더보기이전 문단에서 일반 for문을 사용하기 위해 클로저를 생성한 것을 볼 수 있었습니다.
function itr_fact(c) local i = 0 return function() i = i + 1; return c[i] end end foo = {6,2,3,1,6,2,3} for i in itr_fact(foo) do print(i) end
위와 같은 코드 구성을 사용할 경우, 반복문에 팩토리 함수가 전달되고, 해당 팩토리 함수를 통해 반복자인 클로저를 생성하게 됩니다.
이런 구성의 반복문이 늘어날수록, 반복문의 실행 비용에 클로저의 생성 비용이 추가되게 됩니다.
클로저의 생성 비용은 보통 반복문 내부의 실행 비용에 비하면 크지 않은 수준입니다.
하지만 이런 추가 비용마저 최적화 해야 하는 상황일 경우 아래와 같은 방식을 사용해 볼 수 있습니다.
function itr(arr, i) i = i + 1 local val = arr[i] if val then return i, val end end for i, v in itr, foo, 0 do print(i, v) end
위 코드는 이전 문단의 일반 for문의 구성을 설명할 때 사용한 코드입니다.
자세히 살펴보면 for문에서 추가적인 클로저 생성이 없음을 볼 수 있습니다 (이미 생성된 itr을 사용합니다.)
반복자와 일반 for문에 대해 알아보았습니다.
이번 글에서는 일반 for문에서 사용하는 반복자에 대해 집중적으로 기술했습니다.
본문의 반복자는 다음 원소를 전달하는 역할을 수행합니다.
하지만 반복자에 인자를 추가하여 반복자 내부에서 반복을 수행하게 만들 수도 있습니다.
또한 본문의 반복문, 반복자는 배열 기반, 테이블 기반 데이터를 사용하고 있습니다.
반복자 클로저를 다르게 구현하면 다른 데이터 (파일 IO를 통해 받아온 데이터 등)에도 사용이 가능합니다.
이번 글이 도움이 되셨기를 바랍니다.
감사합니다.
'Lua' 카테고리의 다른 글
[Lua] Lua Build for Windows (0) 2025.02.28 [Lua] 코루틴 (Coroutine) (0) 2025.02.25 [Lua] 함수 (2) (0) 2025.02.17 [Lua] 함수 (1) (0) 2025.02.13 [Lua] 제어 구문 (if, while, repeat, for, break, goto...) (0) 2025.02.05