반응형

루아에서의 스레드(Thread)를 설명 드리겠습니다.

이글은 스레드에 대한 개념이 있으셔야 이해하실 수 있습니다. 앞서 설명드린 스레드에 대하여를 우선 읽어 보시기 바랍니다.

 

루아의 코드는 루아 가상머신이 돌려줍니다. 즉 루아 가상 머신이 스레드를 돌려줘야 합니다.

엄밀히 말하면 C/C++ 로 만든 스레드와는 다릅니다. C/C++ 로 만든 프로그램은 CPU 가 직접(OS 가 직접) 실행하는 코드이지요. 그러나 루아의 코드는 루아 가상머신이 중간에서 돌려주는 역할을 하기때문에, 실질적인 C/C++ 의 스레드를 생각하시면 약간의 오류를 범하실 수 있습니다.

단, 루아의 스레드 역시 하나의 가상머신(이것도 하나의 머신이니까요)에서 돌아주는 스레드 역할을 한다는 것이죠.

루아에서는 스레드를 Coroutine 이라고 합니다.

 

루아의 coroutine은 간단하게 이렇습니다.

 

  1. 루아의 스레드는 함수에서 출발한다.
  2. 루아의 스레드는 함수단위로 만들수 있다.
  3. 루아의 스레드는 coroutine 에 의하여 관리된다.
  4. 루아의 스레드는 c/c++ 에 의하여 만들어지는 스레드와는 다르지만 그 역할을 갖다.

정도로 요약할 수 있습니다.

 

루아원문에 보면

What are coroutines?

 

Coroutines allow us to execute several tasks at once. This is done in a controlled manner by passing control to each routine and waiting until the routine says it has finished. We can reenter the routine to continue at a later time and by doing this repeatedly we achieve multi-tasking.

이라고 나와 있습니다. 즉 한번에 여러가지의 일을 실행할 수 있도록 해주는 것이며, 이러한 루틴들이 서로 실행하며, 또는 대기하면서 기다리며 , 하는 동작을 하게 됩니다.

 

스레드의 생성

루아에서는 스레드를 다음과 같이 생성 합니다.

co = coroutine.create(함수명)

 

즉, 어떠한 함수를 스레드 함수로 만들 수 있습니다. return 되는 값은 thread 인식자라 생각하시면 됩니다.

function foo()
 print("foo",1)
end


co = coroutine.create(foo)

print(co)

위의 코드를 돌려보면

thread: 0137C300

와 같이 결과가 나옵니다. 물론 숫자는 인식자로 각각 다르게 나올 것 입니다.

 

위의 코드는 foo() 라는 함수를 coroutine.create 에 의하여 하나의 스레드함수로 만든 것이고 그 식별자를 co 라는 변수에 담아 놓은 것 입니다.

 

하나의 스레드는 그 상태가 있습니다. 즉 스레드가 어떤 상태이냐 즉, 살아 있나, 죽었나, 돌고 있나, 잠시 멈추어 있나 등의 상태를 가집니다.

 

생성된 스레드의 상태를 알아보는 함수는

coroutine.status(스레드인식자)

 

입니다.

function foo()
 print("foo",1)
end


co = coroutine.create(foo)

print(coroutine.status(co))

 

결과

suspended

위와 같이 coroutine.status() 함수에 의하여 스레드 함수의 상태를 알아 볼 수 있습니다.

스레드 함수는 생성 당시에는 "suspended" 라는 상태에 있습니다. 즉 멈추어 있는 상태 입니다.

 

스레드 함수를 실행하는 방법은

coroutine.resume(스레드인식자)

 

입니다.

function foo()
 print("foo",1)
end


co = coroutine.create(foo)

coroutine.resume(co)

 

결과

foo 1

 

위와 같은 코드로 생성된 thread 함수를 호출하는 것 입니다. 그냥 foo() 라고 함수를 호출하면 스레드로 호출되는 것이 아닙니다.

반드시 coroutine.resume() 로 해당 함수의 스레드 식별자를 이용하여 호출해 주어야 합니다.

위의 결과는 그냥 foo() 함수를 호출하는 것과 별 차이가 없습니다.

 

function foo()
 print("foo",1)

 -- 무엇인가 여기서 다른일을 하고 싶어요
 print("foo",2)
end

foo()

 

결과

foo 1
foo 2

위의 함수를 생각해보죠. foo() 함수가 호출되면 print 함수에 의하여 foo 1 이라는 결과가 나오고 바로 다음의 print 함수에 의하여 foo 2 라는 결과가 나오겠죠. 당연한 결과 입니다.

우리의 목적은 foo 함수가 호출될때 첫번째 print 를 수행하고, 어떤 다른일을 기다리며 , 그 다른일이 끝난후 두번째 print 함수를 수행하고 foo 함수를 종료하고 싶은것 입니다.

 

어떤 함수가 진행을 하다, 다른 프로세스가 돌도록 멈추는 것을

coroutine.yield()

 

라는 것으로 처리 합니다. 즉 프로세스가 위의 라인을 만나면 , 프로세스 진행을 더이상 하지 않고 기다립니다.

물론 위의 함수는 thread 함수가 아닌 일반 함수로 호출하면 오류가 납니다. 그러므로 해당 함수를 thread 로 호출해 줘야 합니다.

 

function foo()
 print("foo",1)
 coroutine.yield()
 print("foo",2)
end

co = coroutine.create(foo)
coroutine.resume(co)

결과

foo 1

위와 같이 foo 함수를 스레드로 만들고 coroutine.resume 으로 해당 스레드 함수를 호출하면 결과가 첫번째 print 함수의 결과만 나옵니다. 그 이유는 resume 에 의하여 해당 foo() 함수가 호출되다가 coroutine.yield() 에서 해당 함수를 돌려주는 프로세스가 멈추게 되는 것이죠.

위의 코드는 coroutine.resume(co) 에 의하여 처음 실행만 되었지, 중간에 yield 된 즉 suspended 된 스레드를 더이상 수행하지 않고 코드가 종료 됩니다. 그러므로 foo() 함수는 suspended 된 후 모든 루아코드가 종료되어 끝나 버린 경우 입니다.

 

function foo()
 print("foo",1)
 coroutine.yield()
 print("foo",2)
end

co = coroutine.create(foo)
print(coroutine.status(co))
coroutine.resume(co)
print(coroutine.status(co))
coroutine.resume(co)
print(coroutine.status(co))

 

결과

suspended  

foo 1            
suspended
foo 2
dead

위와 같이 코드를 바꾸어보죠. 결과를 잘 살펴보시기 바랍니다.

진행 과정은

    <ol dir="ltr" style="list-style-position: initial; list-style-image: initial; margin: 0px 7px 13px 40px; padding: 0px 7px;">
  1. 스레드가 만들어지고 바로 suspended 상태 입니다.
  2. resume 에 의하여 foo 1 이 출력 됩니다.
  3. coroutine.yield() 에 의하여 진행을 더이상하지 않고 멈추어 제어권을 다른곳으로 넘깁니다.
  4. suspended 상태로 있습니다.
  5. resume 에 의하여 앞서 멈춘 thread가 다시 돌아 갑니다. 즉 앞서 정지한 위치부터 프로세스가 돌게되어 foo 2 가 출력됩니다.
  6. thread 함수 즉 foo() 함수가 모두 종료되고 해당 스레드는 죽게 됩니다. (dead 상태)
  7. </ol>

루아에서의 스레드에 대하여 이해가 되셨는지 모르겠습니다.

 

반응형
[루아스크립트/프로그래밍]루아(LUA)Thread 에 대하여



thread 가 무엇인지 잘 모르시는 , 이해가 잘 않되시는분들을 위하여 thread에 대하여 잠깐 적고 넘어가겠습니다.


윈도우즈에서 하나의 프로그램을 실행하면 하나(때에 따라서 하나이상)의 프로세스를 만듭니다. 이 프로세스는 CPU가 아닙니다. 실행 하나라고 보시면 됩니다.

어떤 프로세스가 CPU를 점유하게 되면 다른 프로세스는 멈추게 됩니다.

쉽게 생각하여 무한 루프를 돌리는 코드가 돌고 있다고 생각해보면, 이 무한루프는 컴퓨터의 CPU자원을 혼자 사용하게 됩니다

그러므로 다른 프로세스는 멈추어 있어야 합니다. 컴퓨터가 아무리 성능이 좋아도 하나의 프로세스를 돌리면 다른 프로세스는 순간적으로 멈추어야 합니다.

 

그러면, 여러 프로세스가 동시에 같이 돌아야 하는 상황에서는 어떨까요? 솔직히 이는 불가능 합니다. 동시라는 것은 과학적 물리적 시간에서 정확한 동시여야 하지만, 우리는 컴퓨터 프로그램에서 동시에 라고 하면 해당 컴퓨터를 운영하는 사람이 인지못하는 범위에서 약간의 시간을 주고 조금조금씩 수행하는 것을 의미 합니다.

 

우리가 벽돌로 두개의 탑을 쌓는다고 생각해보죠. 물론 두손으로 동시에 벽돌 하나씩 양손에 쥐고 쌓으면 됩니다. 그러나 컴퓨터는 손이 하나입니다. 즉 손이 하나라고 생각해보죠. 그러면 한번에 벽돌 하나만 집어 두 탑중 한곳에 쌓아야 합니다.

간단하게 하기 위하여 두개의 탑을 1 과 2라고 생각해보조.

한번 벽돌을 집어 탑 1 에 올립니다. 다음번 벽돌을 집어 탑2에 올립니다. 다시 벽돌을 집어 탑 1에 올립니다.

이러한 과정을 계속 하다 보면 탑1 과 탑2 가 동시에 올라가는 것을 경험할 수 있습니다.

어느 탑을 우선시 하느냐에 따라 탑이 동시에 올라가도 그 속도는 차이가 있을 수 있습니다만. 어쨌든 동시에라는 것은 이러한 약간의 시간차를 두면서 거의 동시에 올라가는 것을 의미 합니다.

 

우리가 벽돌을 손으로 집어 탑에 올리는 동작을 컴퓨터에서는 프로세스 라고 합니다. 이러한 프로세스를 두개의 탑에 쌓거나

아니면 두개의 프로세스를 만들어 탑을 쌓는것이 우리가 말하는 thread 입니다.

 

일반적인 프로세스는 한번 프로세스에 들어가면 idle상태(대기상태) 가 될때까지 멈추지 못합니다. 멈춘다면 컴퓨터가 멈추는, 흔히 "프로그램이 죽었다, 다운됬다" 하는 상황이 되는 것 입니다. 살아 있는 프로세스는 계속 도는 것입니다. 설령 그 프로세스가 idle(대기 상태)에 있어도 말입니다.

 

이러한 일반적인 프로세스는 스레드가 아닙니다.

동시에 돌아가는 프로세스가 필요합니다.

어떤 프로세스가 진행, 즉 자신의 고유한 일을 하다가 어느순간 잠시 멈추어 CPU 점유를 풀어주고 다른 프로세스에게 넘겨 주는 작업이 있고, 다시 다른 프로세스가 멈추어 있을때 자신이 멈추었던 것을 이어서 계속 진행할 수 있는 프로세스가 필요합니다.

이러한 프로세스를 스레드(Thread) 또는 스레드 프로세스라고 합니다.

다른말로는 Coroutine 이라고도 합니다.

 

추후 설명하겠지만, 루아에서는 thread라기 보다는 coroutine 이라는 말이 더 적합한 형태 입니다.

루아는 가상 머신입니다. 이러한 가상머신에서 스레드같이 돌도록 하는 것을 coroutine이라는 것으로 처리하고 있습니다.

 

스레드는 다음과 같은 동작을 할수 있는 프로세스 입니다.

 

  1. 스레드의 생성     -- create
  2. 스레드의 실행     -- execute
  3. 스레드의 정지     -- suspend, yielding
  4. 스레드의 재실행  -- resume
  5. 스레드의 종료     -- terminate / dead

일반적인 프로세스는 생성-실행-종료  의 단계만 있습니다. 그러나 스레드 프로세스는 정지 및 재실행 , 즉 suspending, resume 이라는 상태를 더 가지고 있습니다.

이를 이용하여 여러가지의 일을 동시에 처리하는 능력을 부여하는 것이죠.

반응형

[루아스크립트/프로그래밍]루아(LUA)루아의 print 함수에 대하여 print 함수를 잠시 설명하겠습니다.

print 는 루아엔진에서 기본으로 제공하는 함수로 어떠한 값 또는 객체(변수)의 값을 화면에 출력해주는 기능을 수행 합니다.

기본적으로 print 함수는 stdout 으로 출력합니다. 그러나 LuaWin 에서는 이를 redirection 하여 프로그램의 아래에 있는

Consol 부분에 출력하게 되어 있습니다. 흔히 루아 코딩에 있어서 디버깅용, 내용 확인용으로 사용합니다.

 

print(2)     -- 숫자 2를 출력 합니다.

2

 

print("hello")  -- 문자열 "hello" 를 화면에 출력 합니다.

hello

 

print 문은 그 스스로 CR/LF 를 마지막으로 출력 합니다. 그러므로 print 문장은 다음 print 문장에 대하여 cr/lf 로 라인을 갱신하게 됩니다.

 

print 문이 취하는 인수의 갯수는 정해지지 않았습니다.

 

문법은

 

     print ([arg1,arg2,.....])

 

print 문은 입력되는 각 인수를 tab 으로 구분하여 화면에 출력 합니다. 즉

 

print (1,2,3)

 

이라 하면

화면에

 

1[tab]2[tab]3[cr/lf]

 

가 되겠죠.  화면에는    1    2    3    이렇게 나옵니다.

 

인수가 없는 print 문은??

print()

하면 그냥 한줄 생기는 것 입니다.

 

print(1)

print()

print(2)

 

1

[한줄추가]

2

 

이렇게 되겠죠.

 

print문은 흔히 자신의 코드를 확인하거나 디버깅, 검사 하는 용도로 사용합니다.


 

print 는 C/C++ 에서의 printf 함수가 아닙니다. 그러므로 변환 문자형태로 사용할 수 없습니다.

printf 와 같은 것을 찾는다면 루아의 string.format 을 참고하시기 바랍니다.


반응형

루아에서 사용하는 8가지의 type 에 대해서 알아 보겠습니다.

  1. number
  2. string
  3. boolean
  4. table
  5. function
  6. nil
  7. userdata
  8. thread

 

Numbers

루아는 사칙연산(더하기/빼기/곱하기/나누기)에 적용하는 숫자를 제공 합니다.

 

print(2+2)

4

 

print(2-7)

-5

 

print(7*8)

56

 

print(7/8)

0.875

 

주의 할 점은 루아에서의 숫자는 정수가 없습니다. 즉 정수 같지만, 실수로 처리 합니다.

즉 정수든 실수든 모두 그냥 number 입니다.

 

Number 는 = (대입 연산자) 에 의하여 변수에 대입될 수 있습니다.

 

x = 7

print(x)

 

7

 

 

변수는 할당 될때 생성 됩니다.

c/c++ 에서는 변수가 할당될때는 여러 부분에서 있습니다. 컴파일시/로딩시/실행중 등등..

그러나 루아에서는 변수의 선언이 없습니다. 또한 컴파일러가 아니기에

변수는 변수에 어떠한 값이 할당될때 생성이 됩니다.

 

x = 10   -- 이때 변수 x 가 할당 됩니다. 숫자로

x = "aaaa"  -- 이때 변수 x 가 다시 할당 됩니다. 문자열로

 

 

Strings

루아는 문자열(텍스트 형)을 지원 합니다.

 

print("hello")

 

hello

 

문자열 역시 변수에 할당될 수 있습니다.

 

who = "Lua user"

print(who)

 

Lua user

 

 

문자열은 .. 연산자에 의하여 합칠 수 있습니다.

who = "Lua user"

print("hello "..who)

 

hello Lua user

 

.. 연산자 역시 연산에 사용됩니다.

 

message = "hello " .. who

print(message)

 

hello Lua user

루아에서는 문자열에 + 연산자를 사용할 수 없습니다.

message = "hello " + who   -- 불가능 합니다.

 

Boolean

boolean 은 true 또는 false 를 값을 가질 수 있습니다. 즉 true 아니면 false 여야 합니다.

b = true
print(b)

 

true

 

b = not true

print(b)

 

false

 

not 연산자는 true 를 false 로, 반대로 false를 true 로 바꾸는 연산자 입니다.

 

boolean 은 논리적인 연산 (논리 연산자) 에 의하여 만들어 질 수 있습니다. 즉 논리 연산자의 결과는 boolean 입니다.

 

print(1==0)

 

false

 

print(1==1)

 

true

Tables

루아는 일반적인 목적의 집합형 데이터를 제공하며 이를 table 이라 합니다. c/c++ 의 struct (구조체) 라 생각하시면 되는데

실제적으로는 이보다 더욱 유연합니다.

 

집합형 데이터는 흔히 list, set,array,associative array 등에 사용합니다.

루아에서의 테이블은 이렇게 집합형 데이터를 처리할 수 있는 단 하나의 타입 입니다.

 

테이블의 생성

테이블은 {} 에 의하여 생성할 수 있습니다.

 

t = {}   -- 비어있는 하나의 테이블을 만듭니다.

 

테이블 자체를 print 하면 그 type 과 주소가 나옵니다.

t = {}
print(t)

 

table: 01429420
* 참고: table: 뒤의 숫자는 다를 수 있습니다.

테이블은 생성 당시 object 들을 설정하여 생성할 수 있습니다.

 

t = { value = 123, text = "hello"}

print(t.value)

print(t.text)

123
hello

테이블은 또다른 테이블을 포함할 수 있습니다.

y = { const={ name="Pi", value=3.1415927 }, const2={ name="light speed", value=3e8 } }
print(y.const.name)
print(y.const2.value)

 

Pi
300000000

 

Functions

루아에서 함수는 number 또는 string 과 같이 변수에 할당되어 집니다. 함수는 function 이라는 키워드에 의하여 생성 됩니다.

function foo() print("hello") end     -- 함수 선언

foo() -- 함수 호출

print(foo)

 

결과

hello
function: 014308F8

함수의 선언은

function 함수명([인수1,인수2,...])

-- 함수 내부

end

함수의 호출은 함수명에 () 를 붙여 호출 합니다.

함수명([인수1,인수2,...])

 

함수도 변수입니다. 그러므로 다음이 가능 합니다.

 

function foo() print("hello") end     -- 함수 선언

myfoo = foo

myfoo() -- 함수 호출

 

결과

hello

nil values

nil 은 참으로 특이한 값 입니다. c/c++ 을 아시는 분들이 혼동하기 쉽습니다. 결론적으로 nil 은 아무것도 아닙니다.

nil 은 c/c++ 의 null  이 아닙니다. c/c++ 의 null 은 0 이죠.. 그러나 루아의 nil 은 0 이 아닙니다. 그냥 nil 입니다.

 

x = 2.5
print(x)
x = nil
print(x)


결과

2.5
nil

nil 은 비교 연산에 사용할 수 있습니다.

 

print(x == nil)
x = 7
print(x == nil)
print(x)

 

결과

true
false
7

할당되지 않은 변수는 nil 입니다.

 

Userdata

userdata 는 기본 루아만으로 사용될수 없습니다. 이는 루아엔진 자체가 다른 어플리케이션에 내제되어(embeded)

C/C++ 과 연동하여 작성될때 사용 됩니다.  추후 설명을 하도록 하겠습니다.

 

Thread

Thread 역시 추후 설명 도록 하겠습니다.

 


반응형

Linked List, 참으로 필요한 것이죠.

그러나 lua 코딩을 하다보면 별 필요는 없습니다.

그 이유는 루아를 사용하여 그렇게 큰 데이터 리스트를 만들 이유가 많지 않고 또한 table 자체를 사용하면

제한 없이 정보를 보관할 수 있기 때문입니다.

 

하지만, 이론은 알고 넣어가야죠..

 

루아에서 Linked List 의 구현은 매우 쉽습니다. 그 이유는 루아의 table 이 dynamic entity (동적 요소??) 를 다루고 있기 때문이죠. 배열과 같이 정적인 메모리를 잡아서 사용하는 것이 아니라는 것이죠.

 

흔히 linked list 는 next 와 value 가 있습니다. next 는 해당 요소의 뒤에 따라오는 요소이고 value는 해당 요소에 저장하는 값이 되겠죠.

 

일단 list 는 root 가 있어야 합니다.

 

list = nil

 

이제 리스트의 가장 앞에 값 v 를 추가하는 것을 생각해 봅시다.

 

list = {next=list, value = v}

 

위와 같이 하면 list.next 가 있을 것이고, list.value 가 있을 것입니다.

list.next 는 추가되기 전의 가장 마지막의 위치가 되겠죠.

 

이렇게 계속 넣다보면 리스트의 구조를 가지게 됩니다. 마지막에 들어간 것이 물론 root 가 되어지는 것이니,

reverse list 라고나 할까요?

 

이제 이러한 list 를 순회해 봅시다. 여기서 순회란 리스트의 모든 요소를 한번 훓어서 본다 라고 생각하시면 됩니다.

iterator 에 대해서 살펴본적이 있습니다. 그와 유사한 개념 입니다.

 

    local l = list
    while l do
      print(l.value)
      l = l.next
    end

 

이들을 합쳐서 다음과 같이 테스트 해 봅니다.

 

list = nil

list = {next=list, value=10}
list = {next=list, value=20}

 

local l = list
   while l do
      print(l.value)
      l = l.next
   end

 

 

결과

20
10

앞서 말했듯이 뒤에 추가된것이 먼저 나오게 되었습니다. reverse 된 리스트라 생각하시면 됩니다.

이러한 응용에 의하여 순방향 리스트도 쉽게 구현이 가능합니다.

반응형

루아의 테이블의 활용은 막힘이 없습니다.

앞서 우리는 테이블을 이용하여 배열을 만들어 보는 것을 알아 보았습니다.

이제 2차원 배열을 알아보도록 하겠습니다.

 

    mt = {}          -- create the matrix
    for i=1,N do
      mt[i] = {}     -- create a new row
      for j=1,M do
        mt[i][j] = 0
      end
    end

위와 같이 할 경우, 물론 N 과 M 은 그 값이 미리 선언이 되어 있어야 겠지요, M*N 의 2차원 배열이 만들어 집니다.

이는 테이블의 각 요소를 테이블로 정하는 방법으로 구현한 것 입니다.

즉 mt 에 들어가는 것은 다른 배열의 row 로 사용할 또 다른 배열인 것 입니다.

 

위와 같은 경우 꼭 집어서 M*N 행이 아닐 수 있습니다. 희소행렬(Sparse matrix)의 형태가 되겠죠.

M*N 행렬이면  1열의 크기와 2열의 크기가 같아야 되지만 위의 경우 각 열이 또다른 table 이기 때문이고, table은 그 특성상

각각의 크기를 가질 수 있기 때문에 희소행렬로 보아야 할 것 입니다.

 

다음과 같은 것을 살펴봅시다.

 

    mt = {}          -- create the matrix
    for i=1,N do
      for j=1,M do
        mt[i*M + j] = 0
      end
    end


이는 전체적으로는 테이블이 하나만 생성 됩니다. 즉 1차원적인 테이블을 i , j 행렬의 계산에 의하여 2차원화 된 형상 입니다.

앞의 것 보다 사용하기는 다소 불편할지는 몰라도 M*N 행렬을 정확히 처리함은 의심의 여지가 없습니다.

 

아무튼 루아의 table을 이용한 다차원 행렬은 sparse matrix 에 적합한 구조를 제공하고 있습니다.

반응형

루아에서 배열 (Array) 은 어떻게 사용하는지를 알아 보겠습니다.

 

이미 lua table 의 사용법을 아시는 분은 이미 알고 있는 내용 입니다.

 

    a = {}    -- new array
    for i=1, 1000 do
      a[i] = 0
    end

위의 코드는 a 라는 것은 table 로 만들 어지게 됩니다 (여기서는 array 라고 여김)

a 라는 테이블에 키를 1 부터 1000 까지 바꾸면서 0의 값을 설정하는 코드 입니다.

 

table 은 대괄호 [ ] 에 의하여 key 인덱싱 될 수 있습니다. 여기서 key 인덱싱은 특정한 메모리 위치는 아닙니다.

즉 c 에서의 배열과 다릅니다.

 

그러므로 다음과 같은것이 가능 합니다.

 

a = {}

a[-1] = 20

 

print(a[-1])

 

즉 [] 안에 들어가는 것은 배열적인 인덱스가 아니라 그냥 어떤 데이터를 보관하는 장소에 대한 키 값으로 사용되는 것 입니다.

 

 

    -- creates an array with indices from -5 to 5
    a = {}
    for i=-5, 5 do
      a[i] = 0
    end

배열은 다음과 같이 초기화 할 수 도 있습니다.

 루아스크립트/루아/웹/웹프로그래밍/Lua

squares = {1, 4, 9, 16, 25, 36, 49, 64, 81}

이는

 

square[1] = 1

square[2] = 4

square[3] = 9

square[4] = 16

square[5] = 25

square[6] = 36

square[7] = 49

square[8] = 64

square[9] = 81

 

과 같은 뜻 입니다. 즉 인덱스가 1 부터 순차적으로 1씩 증가하면서 보관되게 되는 것 입니다.


반응형

for 문에 사용할 iterator 함수를 사용자가 직접 만들 수 있습니다.

 

다음의 내용은 lua-users wiki 에 있는 사용자 iterator 함수의 pseudo-code 입니다.

 

-- Equivalent to "for var1, ···, varn in explist do block end"
do
  local iterator, state, var1 = explist
  local var2, ... , varn
  while true do
    var1, ..., varn = iterator(state, var1)
    if var1 == nil then break end
      -- for block code
  end
end

 

좀 어려우니 예제를 살펴봅시다.

 루아스크립트,web,프로그래밍,웹프로그래밍,웹

function square(state,n) if n<state then n=n+1 return n,n*n end end
for i,n in square,5,0 do print(i,n) end

결과

1 1
2 4
3 9
4 16
5 25

위의 코드를 보면 square 라는 iterator 용으로 사용할 함수를 하나 만들었습니다.

이 함수를 for 문에 적용하여 iterator 역할을 수행하도록 만든 것 입니다.

 

square 함수는 state 와 n 이라는 인수를 받아서 n 이 state 보다 작을때는 n=n+1 을 해주고 return 으로

n 과 n*n 을 돌려줍니다. 2개의 값을 돌려주며, 루아에서는 함수가 한개 이상(다수의) return을 할 수 있다는 것을

알기 바랍니다. 어쨌든 위의 경우 n 과 n*n 즉 n의 제곱을 돌려주고 있습니다.

 

이 함수를 for 문의 iterator로 사용하였습니다.

 

in square,5,0 이라는 부분이 iterator 부분을 구성합니다.

 

즉 square 함수를 state = 5, n = 0 으로 초기화 하여 지속적으로 호출해 줍니다.

단 명심해야 할 것은 square 함수 자체가 지속적으로 호출되는 것이 아닙니다.

다시 말해 square 함수가 호출되면 지속적으로 함수에 인수가 전달되어야 겠죠. 그러나 이렇게 지속적으로 인수가 전달되는

것이 아닙니다. 인수는 처음에만 전달되고, 그 상태가 지속적으로 그 후에도 남게 됩니다.

 

잘보시면 square 함수의 인수에 n 이라는 변수가 있습니다. 인수로 사용한 변수이지요.

또한 for 문에 보면 i,n 에  역시 n 이라는 변수가 사용됩니다.

 

이 두게 모두 local 변수이므로 서로 다른 변수 인것을 알아야 합니다.

 

위의 코드를 분석하면 , for 문에 들어가 square 함수가 호출되어 state 및 n 에 각각 5 와 0 이 할당되어지고

square 함수의 내부계산에 의하여 return 되는 두개의 값(n,n*n) 이 각각 for 문의 i,n 에 전달되게 되어

for 문의 블럭인 print(i,n) 에서 i 와 n 즉  n,n*n 이 출력되어지고 다시 square 내부 계산이 이루어지며

조건에 맞을때 다시 n,n*n 이 return 되어 집니다.

 

조건이 만족하지 못하면 square 문은 아무것도 return 하지 않고 함수 자체가

종결이 되며 for 문의 i,n 은 nil 의 값을 받게 되어서 결국 for 문이 종료되게 되어 있습니다.

 

좀 복잡하지만, 또한 논리적으로 이해하기 힘들 수 있습니다만, 이것이 루아의 특징이라고 생각하시기 바랍니다.

 

function squares(nbvals) return square,nbvals,0 end  -- iterator,state,initial value


for i,n in squares(5) do print(i,n) end
1       1
2       4
3       9
4       16
5       25

위와 같이 squares 라는 함수를 다시 하나 더 만들어 pairs 또는 ipairs 와 같이 일반적인 iterator 함수와 같은 역할을

수행하는 함수를 만들어 사용할 수 도 있습니다.

반응형

루아는 특별히 지정하지 않는 변수는 global 변수로 지정됩니다.

그러나 함수의 인수로 사용하는 변수는 해당 함수에서만 사용되는 local 변수 입니다.

 

function f(a)

 print(a)

end


f(10)
print(a)

 

그런 이유에서 위의 코드를 실행시켜 보면

 루아스크립트,웹,프로그래밍,웹프로그래밍,web,lua

10
nil

이 나옵니다.

 

이와 마찬가지로 for 문과 같은 제어문에 사용하는 변수도 해당 for loop 에서만 사용할 수 있는 local 변수가 됩니다.

 

for i=0,5 do print(i) end

print(i)

 

결과

0
1
2
3
4
5
nil

 

위와 같은 경우에 for 문의 i는 for 문 안에서만 유효하며

for 문 밖에서는 다른 변수가 됩니다.

 

반응형

Iterator 에 대하여 알아봅시다.

 

C 의 STL 을 아시는 분은 iterator 가 무엇인지를 이해하실 것 입니다.

모르시는 분들을 위하여 잠시 여기에 소개하고자 합니다.

 

Iterator 란?

배열,리스트,큐,스텍 등등 어떠한 데이터 요소들을 다수 넣어 놓고 프로그램에서 이를 접근 운영하는 경우가 많습니다.

이러한 것을 container (컨테이너) 라고 합니다. 즉 데이터들을 담아 놓는 공간이라 생각하시면 됩니다.

 

이러한 컨테이너에 다수의 데이터들을 넣어 보관하다가 , 이 데이터들을 하나하나 순차적으로 접근할 필요가 있을때가 있습니다.

즉 데이터들을 하나하나 찾아다니며 돌아다니는 녀석이 바로 iterator (연속방문자? 정도로 해석) 라 합니다.

iterator 의 형태는 컨테이너의 자료구조에 의하여 정해집니다. 자료구조는 리스트의 구조와 트리의 구조가 있겠죠.

 

어찌됬든 이러한 방문자를 이용하여 루아의 for 문을 완성할 수 있습니다.

for 문은 일반적으로 어디서부터 어디까지 어떻게 증가하여 정해진 프로그램 블럭을 수행한다.. 라는 의미로 사용되죠.

 

여기서 iterator 를 이용하면 어디서부터 어디까지라기 보다는 해당 컨테이너(루아의 테이블)에 보관되어 있는 데이터들을 하나하나 방문하면서 for 루프를 돌리는 형태로 수행될 수 있습니다.

 

iterator를 이용하는 for loop 의 문법은 다음과 같습니다.

 

for var {, var} in explist do block end

 

여기서 explist 는 루프에 들어가기 전에 평가하는 함수 입니다. 이를 iterator function 이라 합니다.

 

다음의 함수들이 루아 테이블에서 iterator function 으로 사용되어 집니다.

 

pairs(table)

 

pairs() iterator 함수는 주어진 테이블의 key-value 쌍을 모두 처리 합니다.

인덱스에 의한 순차적인 테이블 뿐만 아니라 그렇지 않은 모든 key-value 쌍을 처리 하는 것 입니다.

 

for key,value in pairs(t) do print(key,value) end
3       10
1       3
4       17
2       7
pi      3.14159
banana  yellow

 

 

위와 같이 table t 에 있는 모든 key-value 쌍을 처리하며 , key 에 의한 순차적인 순서(order) 를 지키지 않습니다.

 루아스크립트,프로그래밍,웹,웹프로그래밍

 

ipairs(table)

 

이 함수는 pairs 함수와는 다르게 순차적인 key order 를 처리 합니다. 즉 순차적이지 않은 key 에 대해서는 처리하지 않습니다.

 

for index,value in ipairs(t) do print(index,value) end
1       3
2       7
3       10
4       17

 

위에서와 같이 table t 에 있는 key-value 쌍중에 순차적인 인덱스로 이루어진 데이터들에 대해서만 순차적으로 처리하게 됩니다.

 

순차적이면서 순차적이지 않은 모든 key-value에 대하여 처리하고자 한다면 다음과 같이 사용하면 됩니다.

 

for key,value in next,t,nil do print(key,value) end


1       3
2       7
3       10
4       17
pi      3.14159
banana  yellow

이러한 형태는 next 함수를 사용하는 것 입니다. 위의 문장은 next 함수로 t 를 처리하며 nil 이 될때까지 진행 합니다.


반응형

루아에서의 for 문은 크게 두가지의 형태로 생각해 볼수 있습니다.

  1. 인덱스 증감에 의한 데이터 접근 (Numeric progression)  -- numeric for 문이라 함
  2. Iterators (반복자) 에의한 데이터 접근 (Iterators)            -- generic for 문이라 함

Numeric Progression

 

Numeric progression 에 의한 접근은 다음과 같은 문법을 갖습니다.

 

for variable = from_exp , to_exp [, step_exp] do block end

이러한 형태에서는

variable 이 for 문을 들어가기전에 from_exp 에 의하여 초기화 되며, to_exp 가 될때까지 진행 하며

block 문이 수행 됩니다.

각 block 문이 수행된 후 step_exp 만큼 variable 은 중감하게 되어 to_exp 까지 도달하게 됩니다.

만약 step_exp 가 생략될 경우 1로 간주 하게 되어 1씩 증가하게 되는 것 입니다.

 

for i = 1,3 do print(i) end

 

결과

1
2
3

 

for i = 3,1 do print(i) end

 

와 같이 하면 for문은 돌지 않습니다.

c 에서 생각한다면 무한루프의 소지가 있습니다만, 루아에서는 그렇지 않습니다.

 

for i = 3,1,-1 do print(i) end

 

결과

3
2
1

 

 

for i=1,0,-0.25 do print(i) end

 

결과

1
0.75
0.5
0.25
0

 

와 같이 증감을 실수를 사용할 수 도 있습니다.

 

위의 예들에서 순차적 증가값으로 i 라는 변수를 사용하게 됩니다. 이 변수는 local 로 변수입니다.

for 문에 사용된 증감 변수는 local 이라 지정하지 않아도 자동적으로 local 변수로 처리 됩니다.

 

for i=1,0,-0.25 do print(i) end

print(i)

 

를 수행하면

1
0.75
0.5
0.25
0
nil

와 같이 i 값이 nil 이 됩니다. 즉 for 문의 i 와 print(i) 의 i 가 다르다는 것이죠.

 

 

for i = e1,e2,e3 do myFunction(i) end

와 같이 변수를 사용할 수도 있습니다.

반응형

좀 의아해 할 수 있습니다만, 다음의 예를 살펴보죠.

 

t = { [function(x) print(x) end] = "foo" }
for key,value in pairs(t) do key(value) end


 

위에서 보면 t 라는 테이블은 function(x) print(x) end 라는 함수 즉  x 를 인수로 받아 이를 consol 에 출력하는 함수 입니다.

이 함수 자체가 키가 됩니다. 그러면서 이 키에 설정된 값이 "foo" 입니다.

 

그러면 for 문에 의해서

 

일단

 

key,value in pairs(t)

 

에 의하여 테이블 t 에 있는 key 와 value 를 가져오게 되겠죠.

여기서 key 는 결국 앞어서 말한 함수가 됩니다. 그리고 value 는 "foo" 라는 값이 되겠죠.

 

do key(value)

 

에 의하여 결국 key 라는 함수 , 즉 앞에 키로 넣은 함수 에 value 를 인수로 호출한 것이 됩니다.

 

function(value) 이렇게 되겠죠.

 

결국 value 가 화면에 찍히게 됩니다.

 

과연 이런것을 어디에 사용할 까요?

 

다수의 값이 있고 그 값을 처리하는 함수가 각각 따로 있다고 생각해보죠.

이들을 하나의 테이블에 보관하여 관리할 수가 있겠죠.

 

이런정도의 용도,, 약간의 OOP 적인 성격을 갖는 용도로 활용할 수 있지 않을까 합니다.

반응형

복잡하게 생각하지 말고 다음과 같은 예를 들어 보겠습니다.

 

 t = { apple=5 }
 a = "apple"
 b = "apple"
 print(t.apple, t[a], t[b])

 

결과는

5 5 5

위에서와 같이

t 라는 테이블은 apple라는 키에 5 가 설정된 테이블입니다.

 

이에 대한 접근은

 

t.apple

 

로 접근할 수 있으며,

 

또한 문자열 "apple" 이라는 것으로도 접근할 수 있다는 것 입니다.

 

이러한 기능이 별 필요 없거나 단순하다고 생각할 수 있습니다만,

 

이렇게 생각해보면 매우 유용합니다.

 

c/c++ 같은 고급 언어에서는 변수와 변수와 같은 이름의 문자열은 서로 연결할 수가 없습니다.

 

즉 c 에서

int apple;

string s = "apple";

 

이라는 것이 있을때

s 와 apple 사이의 관계는 특별히 매칭함수를 만들어 주지 않는이상 연결이 어렵습니다.

 

그러나 루아에서는 이렇게 특정 변수를 데이터인 문자열로 접근할 수 있도록 하는 기능을 제공하는 것 입니다.

반응형

루아 테이블에서의 저장 영역은 키-값 (key-value)의 쌍으로 이루어 집니다.

루아는 일반적인 배열(array) 과 dictionary 간의 차이점을 갖지 않습니다.

즉 배열과 dictionary 간의 차이를 루아 테이블에서는 볼수 없다는 것이죠.

 

모든 루아의 테이블들은 실질적으로는 dictionary 와 같이 동작 합니다. 일반적으로 배열에서의 키는 정수이어야 한다는것과도

다르죠.

 

> t = { 3,6,9 }                 -- is the same as...

> t = { [1]=3, [2]=6, [3]=9 } -- is the same as...
> t = {} t[1]=3 t[2]=6 t[3]=9

이러한 의미에서 위의 3줄은 모두 같은 내용 입니다.

 

테이블의 키는 일종의 레퍼런스(reference) 입니다.

a = { [{1,2,3}]="yadda" }              -- construct a table where the element has a table key
for k,v in pairs(a) do print(k,v) end  -- display the table contents: key-value pairs
print( a[{1,2,3}])                           -- display the element value

 

위와 같은 예제서 결과는

table: 014D9150 yadda
nil

입니다.

k,v in pairs(a) 에서

k 는 테이블 a에 있는 키, v 는 해당 키로 지정된 값 입니다.

또한 a 의 키는 {1,2,3} 이라는 테이블입니다. [] 를 쒸운것은 이 테이블 자체의 주소를 키로 사용한다는 것이죠.

그러므로 k 는 table 이라는 값이 나오는 것이고 그 주소가 나오는 것 입니다.

역시 v 는 해당 키에 대한 값 "yadda" 이죠.

 

위의 값을 다음과 같이 풀어보죠.

tablekey = {1,2,3}                     -- create a table with a variable referencing it
a = { [tablekey]="yadda" }             -- construct a table using the table as a key
for k,v in pairs(a) do print(k,v) end  -- display the table elements
print(a[tablekey])                          -- retrieve a value from the table
print(tablekey)              -- the table value is the same as the key above

 

결과는

table: 014D89D0 yadda
yadda
table: 014D89D0

 

위와 같이 보시면 이해가 좀 쉬우실 겁니다.

 

반응형

Mixed table constructor , 즉 혼합형 테이블 이라 할 수 있습니다.

말로는 이해가 어렵습니다.

 

우리는 table을 이용해서 dictionary 를 구성할 수 있음을 알았습니다. dictionary 에는 키가 있고 그 키에 저장되는 값이 있습니다.

 

table.key = 값

일반적으로 dictionary 는 키의 자료형태, 즉 데이터 타입이 같은 형태(정수면 정수, 문자열이면 문자열 등등)를 사용합니다.

그러나 루아의 table은 이러한 키의 형태가 같지 않아도 됩니다.

또한 순차적이지 않아도 됩니다.

 

다음의 예를 살펴보독 하죠.

 

t = { 2,4,6, language="Lua", version="5.1" }
for k,v in pairs(t) do print(k,v) end

for i,v in ipairs(t) do print(i,v) end

결과는

1 2
2 4
3 6
language Lua
version 5.1


1 2
2 4
3 6

 

위에서
앞의 for 문에 의해서는 정수로 인덱스(순차적)된 내용과 language, version 으로 명명된 키의 값들도 나옵니다.

그러나 뒤의 for 문에 의해서는 정수로 인덱스된 순차적인 것들만 나옵니다.

이는 pairs() 와 ipairs 의 차이에 의해서 발생하는 결과 입니다. 이 두가지의 차이는 추후 다시 설명하기로 하고

일단 여기서는 하나의 table에 서로 다른 형태의 순차적이지 않은 값들을 동시에 사용할 수 있다는 것 입니다.

c 나 c++ 프로그램을 하다보면, 가끔 위의 형태를 만들기 위하여 노력할 때가 있습니다.

즉 어떠한 데이터 집단을 만들고 그 데이터 집단의 이름과 특징들을 적어놓을 공간을 만들기 위하여 구조체나 class 의

구조화된 형태를 다소 복잡하게 만들어 사용하게 됩니다.

 

이런 경우 루아의 table은 쉽게 그 형태를 구성하도록 해 줍니다.

+ Recent posts