Lua 문법 (2)
함수
> function Greet(Name)
>> print(“Hello, “ .. Name .. “.”)
>> end
> Greet(“John”)
Hello, John.
> Greet(“Jane”)
Hello, Jane.
> Greet(“Aloysius”)
Hello, Aloysius.
함수 정의, 함수 호출
 함수 정의는 function 키워드로 시작해서
end 키워드로 마친다.
 함수 정의가 하나의 statement이다.
 매개변수는 ()안에 ,로 구분해서 나열한다.
 함수를 호출할 때 실질 매개변수 개수가
형식 매개변수 개수보다 많으면 남는 것은 버리고,
형식 매개변수 개수보다 적으면 부족한 것은 nil로 보충
(여러 변수에 값을 대입할 때와 동일)
함수 정의, 함수 호출
 함수 정의에서 return 문을 사용하면, 함수를 호출할 때
함수에서 정의된 코드를 실행할 뿐만 아니라 값을
돌려준다. 즉, 함수 호출이 expression이 된다.
> function Average(Num1, Num2)
>> return (Num1 + Num2) / 2
>> end
> Average(0, 10)
> print(Average(0, 10))
5
> print(Average(Average(10, 20), Average(30, 40)))
25
함수에서 값 리턴하기
 return 문은 블록의 마지막 statement이어야 한다.
 블록에는 do 블록, 반복문(for, while, repeat), if문의
선택지와 함께 함수 정의와 청크(chunk)가 있다.
 청크는 한번에 처리되는 코드의 부분이다. 예를 들어,
 루아 파일 (루아 파일을 실행하면 함수로 실행된다)
 루아 인터프리터에 입력했을 때 연속 프람프트(>>)가 아닌 일반
프람프트(>)를 나오게 할 한 줄의 코드
 루아 인터프리터에 입력했을 때 연속 프람프트로 연결된 여러
줄의 코드. 마지막 줄을 입력했을 때 일반 프람프트가 나와야 함.
 그러므로, 디버깅 목적으로 함수 중간에 return을
넣으려면 do return end와 같이 do 블록을 사용한다.
return 문
> function ReturnArgs(Arg1, Arg2, Arg3)
>> return Arg1, Arg2, Arg3
>> end
> print(ReturnArgs(1, 2, 3))
1 2 3
> print(ReturnArgs(ReturnArgs(1, 2, 3)))
1 2 3
> A, B, C = ReturnArgs(“alpha”, “bravo”, “charlie”)
> print(A, B, C)
alpha bravo charlie
여러 개의 값을 리턴하기
 대입문, return 문, 함수의 매개변수에서 값들을 ,로
구분해서 나열하는 것을 value list라고 부릅니다.
 여러 개의 값을 리턴하는 함수 호출을 expression으로
value list에 사용하면,
 함수 호출이 value list의 마지막에 위치한 경우에는
모든 리턴값이 value list에 포함되지만,
 함수 호출이 value list의 처음과 중간에 위치한 경우에는
첫번째 리턴값만 value list에 포함되고 나머지 리턴값은 버려짐.
Value Lists
> print(1, ReturnArgs(“a”, “b”, “c”))
1 a b c
> print(ReturnArgs(1, 2, 3), “a”)
1 a
> print(ReturnArgs(1, 2, 3), ReturnArgs(4, 5, 6),
>> ReturnArgs(7, 8, 9), ReturnArgs(10, 11, 12))
1 4 7 10 11 12
> A, B, C, D = ReturnArgs(1, 2, 3), ReturnArgs(4, 5, 6)
> print(A, B, C, D)
1 4 5 6
Value Lists
 값을 리턴하지 않는 함수 호출을 expression으로 value
list에 사용하면,
 함수 호출이 value list의 마지막에 위치한 경우에는
리턴값이 없으므로 value list에 아무런 값도 추가되지 않지만,
 함수 호출이 value list의 처음과 중간에 위치한 경우에는
리턴값이 없는 것이 nil로 전환되어서 value list에 nil이 추가됨.
 함수 호출이 value list의 마지막에 있거나 단독으로
있을 때에도 함수 호출을 ()로 묶으면 하나의 값으로
강제로 전환됩니다.
Value Lists
> function DoNothing()
>> end
> print(1, DoNothing())
1
> print(DoNothing(), 2, DoNothing())
nil 2
> print(DoNothing(), 2, (DoNothing()))
nil 2 nil
> print(ReturnArgs(1, 2, 3), (ReturnArgs(4, 5, 6)))
1 4
Value Lists
 대입문, 함수 매개변수에서 개수가 일치하지 않아서
발생하는 조정(남는 값을 버리거나 부족한 값을 nil로
보충)은 각 expression에서 발생하는 전환 후에
일어난다.
> A, B = DoNothing(), 1, 2
> print(A, B)
nil 1
> A, B, C = ReturnArgs(1, 2, 3), 4
> print(A, B, C)
1 4 nil
Value Lists
 이미 사용중인 변수명을 다른 변수에 사용할 수 없다.
하지만, 규모가 큰 소프트웨어를 프로그래밍하거나
다른 사람이 만든 라이브러리를 사용할 때, 이 당연한
요구가 문제를 일으킬 수 있다. 내가 지금 선언해서
사용할 변수명이 프로그램의 다른 부분이나 사용하는
라이브러리에서 사용하지 않음을 항상 확인해야 하기
때문이다.
 그래서, 변수의 사용범위를 제한하는 문법이 필요하게
되었다.
변수의 사용범위(scope)
 함수를 정의할 때 사용하는 매개변수(함수명 옆에 ()
속에 선언하는 변수)를 '형식 매개변수(formal
argument)'라고 부르고,
 함수를 호출할 때 사용하는 매개변수(함수명 옆에 ()
속에 나열하는 expression)를 '실질 매개변수(actual
argument)'라고 부른다.
 형식 매개변수의 사용범위는 함수의 정의 안이다. 즉,
함수 밖에서는 형식 매개변수의 값에 접근할 수 없고,
함수 밖에 동일한 이름의 변수가 있을지라도 서로 다른
변수이므로 서로 간섭하지 않는다.
함수의 형식 매개변수와 실질 매개변수
> -- Prints its one argument:
> function PrintArg(Arg)
>> print(Arg)
>> end
> PrintArg(true)
true
Arg는 형식 매개변수이고, true는 실질 매개변수이다.
함수의 형식 매개변수와 실질 매개변수
 사용범위가 프로그램 코드의 특정 영역에 제한되는
변수를 '지역 변수'라고 부르고, 프로그램 전체에서
접근할 수 있는 변수를 '전역 변수'라고 부른다.
 지역 변수의 예에는 함수의 형식 매개변수와 for
반복문의 반복에 사용되는 변수가 있다.
즉, 형식 매개변수의 사용범위는 함수 정의 안이고,
반복에 사용되는 변수의 사용범위는 for 반복문 안이다.
 지역 변수는 사용범위 안에서 같은 이름을 가진 전역
변수나 더 큰 사용범위의 지역 변수를 가린다(shadow).
지역 변수 (Local Variables)
 local 키워드를
사용해서 지역
변수를 생성할 수
있다.
 지역 변수의
사용범위는 그
변수가 속한 블록
중에서 가장 안쪽
블록이다.
지역 변수
> do
>> local Lcl = “aardvark”
>> -- The first Lcl’s scope starts here.
>> local Lcl = Lcl .. “zebra”
>> -- The second Lcl’s scope starts here.
>> print(Lcl)
>> -- Both scopes end here.
>> end
aardvarkzebra
local 키워드로 생성한 지역 변수의 사용범위는 local 문
다음 줄부터 시작된다.
 블록: do 블록, 반복문의 몸체(repeat문은 until 뒤에
오는 expression 포함), if문의 선택지, 함수, 청크
 청크: 루아 파일, 루아 인터프리터에 일반 프람프트(>)
가 나올 때까지 입력한 코드
> City = “New York”
> local City = “London”
> print(City)
New York
지역 변수의 사용범위는 그 변수가 속한 블록 중에서
가장 안쪽 블록이다.
 statement나 expression이 어떤 변화를 일으킬 때
사이드 이펙트라고 부른다.
 print() 함수 호출이 화면에 무언가를 출력하거나 함수 호출이 전역
변수의 값을 바꾸는 것이 사이드 이펙트의 예이다.
 함수가 값을 리턴하거나 지역 변수를 생성했다 지우는 것은
사이드 이펙트가 아니다. 리턴된 값으로 무언가를 하지 않는 한
리턴값 자체는 변화를 일으키지 않고, 지역 변수도 마찬가지다.
 사이드 이펙트가 있는 함수는 실행 순서에 따라 결과가
달라질 수 있고, 프로그램의 다른 부분에 영향을 미칠 수
있으므로, 필요한 경우가 아니라면 사이드 이펙트가
없는 함수를 만드는 것이 낫다.
사이드 이펙트 (Side Effects)
 함수의 실질 매개변수와 대입문에 사용된 value list의
평가 순서, binary operator가 피연산자를 취하는 순서
등은 보장되지 않는다. 즉, 루아 버전에 따라 달라진다.
 여러 변수 대입문에서 = 오른쪽에 있는 value list의
평가가 대입보다 먼저 이루어지는 것은 보장된다.
 함수의 실질 매개변수로 사용된 함수 호출은 그
매개변수를 사용하는 함수 호출보다 먼저 실행되는
것이 보장된다.
실행 순서
 루아의 불연산자인 and와 or는 수학의 불연산자와
동일한 결과를 주면서도 작동 방식은 조금 다르다.
 and는 첫 번째 피연산자가 false나 nil이면 그 값을
결과로 사용하고, 그 외의 값이면 두 번째 피연산자의
값을 결과로 사용한다.
 or는 첫 번째 피연산자가 false나 nil이 아니면 그 값을
결과로 사용하고, false나 nil이면 두 번째 피연산자의
값을 결과로 사용한다.
 첫 번째 피연산자의 값을 결과로 사용할 때에는
두 번째 피연산자는 아예 들여다보지도 않는다. 즉, 두
번째 피연산자가 함수호출이면 아예 호출하지도 않는다.
Short-Circuit Evaluation
 함수에서 다른 함수를 호출하면 호출된 함수가 실행된
후에 어느 함수의 어디로 돌아와야 되는지 알기 위해서
현재 실행중인 함수들의 정보를 스택에 저장하는데,
이 스택을 call stack이라고 부른다.
 함수가 호출되면 호출된 함수의 정보가 콜 스택의 맨 위
스택 프레임에 쌓이고, 함수가 실행을 마치고 리턴하면
콜 스택의 맨 위 스택 프레임을 비우게 된다.
 그러므로, 호출된 함수에서 다른 함수를 호출하는 일이
반복되면 콜스택은 점점 높아지게 되고, 호출된 함수가
실행을 마치고 리턴하면 콜스택은 점점 낮아지게 된다.
콜 스택 (Call Stack)
 자기 자신을 호출하는 함수를 재귀 함수라고 부른다.
 함수에서 정의된 지역 변수는 콜 스택의 스택 프레임에
생성되므로, 재귀 함수가 자신을 호출할 때마다
생성되는 지역 변수는 각각 다른 스택 프레임에
생성되어서 겹치지 않는다.
 재귀 함수는 자신을 호출하는 recursive case와 함께
순환이 끝나는 base case를 설정해서 영원히 순환하는
것을 방지한다.
재귀 함수 (Recursive Function)
> -- Returns the factorial of N:
> function Fact(N)
>> local Ret
>> if N == 0 then
>> -- Base case:
>> Ret = 1
>> else
>> -- Recursive case:
>> Ret = N * Fact(N - 1)
>> end
>> return Ret
>> end
>
> for N = 0, 5 do
>> print(N .. “! is “ .. Fact(N))
>> end
0! is 1
1! is 1
2! is 2
3! is 6
4! is 24
5! is 120
 함수 호출이 너무 깊어져서 콜 스택이 주어진 메모리를
모두 소진하면 스택 오버플로우 에러가 발생한다.
 스택 오버플로우가 발생하는 주된 이유는
재귀 함수에서 base case에 걸리지 않고 무한 호출이
일어날 때이다. 예를 들어, 이전 슬라이드에 정의한
Fact() 함수에 매개변수를 음수로 해서 호출하면 스택
오버플로우가 발생한다.
> Fact(-1)
스택 오버플로우 (Stack Overflow)
 return 문의 expression이 함수 호출이면,
호출된 함수가 리턴한 값을 그대로 리턴하면 된다.
즉, 호출된 함수가 리턴한 후에 따로 할 일이 없게 된다.
따로 할 일이 없으므로 스택 프레임에 저장된 정보가
지워진다고 해도 문제가 발생하지 않는다.
 그래서, return 문의 expression이 함수 호출이면,
그 호출은 콜 스텍에 새로운 스택 프레임을 추가하지
않고 자신을 호출한 함수의 스택 프레임을 재사용한다.
즉, 콜 스택이 커지지 않는다.
 재귀 함수에 테일 콜을 사용하면 tail recursive라고 한다.
테일 콜 (Tail Calls)
> function ForeverTail()
>> return ForeverTail()
>> end
> ForeverTail() -- 스택 오버플로우 없이 영원히 멈추지
않는다.
> function ForeverNotTail()
>> ForeverNotTail() -- Is this a tail call?
>> end
> ForeverNotTail() -- 스택 오버플로우가 발생한다.
-- 이 함수는 ForeverNotTail()과 end 사이에 리턴값의
리스트를 0으로 조정할 일이 남아있다.
 Fun() -- return 문이 아니다.
 return Fun() + 1 -- Fun() 리턴값에 1을 더해야 한다.
 return X and Fun()
 return (Fun()) -- ()가 Fun() 리턴값을 하나의 값으로
조정해야 한다.
 return Fun(), Fun()
 return 문에 함수 호출 하나만 expression으로 사용된
경우만 테일 콜이다.
테일 콜이 아닌 경우들
-- Returns the factorial of N (tail-recursively). 형식 매개변수가 2개이지만,
-- 호출할 때에는 Fact(5)처럼 1개의 실질 매개변수만 주고 호출해야 한다.
function Fact(N, Acc)
-- Initialize the accumulator to 1:
Acc = Acc or 1
if N == 0 then
-- Base case:
return Acc
else
-- Recursive case:
return Fact(N - 1, N * Acc)
end
end
tail recursive로 만든 팩토리얼 함수
 루아에서 함수는 다른 종류의 값들(숫자, 문자열, 불값,
nil)과 동일하게 취급 가능한 값이다.
 함수의 매개변수로 넘길 수 있다.
> print(type(print))
function
 다른 변수에 대입할 수 있다.
> RealPrint = print
> function FakePrint(Val)
>> RealPrint("Inside FakePrint:", Val)
>> end
> print = FakePrint
> print("Hello")
Inside FakePrint: Hello
값으로서 함수, 변수로서 함수명
 ==, ~= 연산자의 피연산자로 사용해서 비교할 수 있다.
> RealPrint = print
> print(print == RealPrint)
true
 함수의 리턴값으로 함수를 리턴할 수 있다.
> function MakeDoNothing()
>> return function() end -- function expression
>> end
 함수를 print 함수로 출력하거나
tostring 함수로 문자열로 바꿔보면,
function이라는 단어와 콜론과 함께 16진수 숫자가 나온다.
이 16진수 숫자는 메모리에서 이 함수의 주소이다.
> print(print)
function: 00000000006D8C70
 함수가 값이고, 함수명이 변수라고 한다면,
함수 정의는 일종의 대입문이라고 할 수 있다.
함수 정의
function name(formal arguments)
statements
end -- function statement
name = function(formal arguments)
statements
end -- function expression
대입문 = 오른쪽에
function으로 시작해서
end로 끝나는 부분은
function expression으로,
새로 생성된 함수를
값으로 가지는
expression이다.
 function expression은 다른 종류의 expression이
사용되는 곳은 어디든 사용할 수 있다.
 function expression은 평가될 때마다 새로운 함수를
만들어낸다. 즉, 동일한 function expression이라고
하더라도 실행될 때마다 그 값은 새로운 함수이다.
> function MakeDoNothing()
>> return function() end
>> end
> print(MakeDoNothing() == MakeDoNothing())
false
Function Expression
 함수에 꼭 이름을 붙일 필요는 없다. 이름을 붙이지 않고
function expression으로 사용할 수 있고, anonymous
function이라고 부른다.
 anonymous function을 호출할 때에는 다음과 같이
function expression을 괄호로 묶어서 호출한다.
> (function(A, B)
>> print(A + B)
>> end)(2, 3)
5
Anonymous Function
 함수를 지역 변수에 대입할 수 있다.
 다음과 같이 function expression으로 할 수도 있고,
> do
>> local LclAverage = function(Num1, Num2)
>> return (Num1 + Num2) / 2
>> end
>> print(LclAverage(10, 20))
>> end
15
> print(LclAverage)
nil
지역 함수 (Local Functions)
 다음과 같이 function statement로 할 수도 있다.
> do
>> local function LclAverage(Num1, Num2)
>> return (Num1 + Num2) / 2
>> end
>> print(LclAverage(10, 20))
>> end
15
> print(LclAverage)
nil
 다음과 같이 지역 변수 선언과 함수 정의를 분리할 수도 있다.
> do
>> local LclAverage
>> function LclAverage(Num1, Num2)
>> return (Num1 + Num2) / 2
>> end
>> print(LclAverage(10, 20))
>> end
15
> print(LclAverage)
nil
 위의 예처럼 function statement로 함수를 정의할 때도
함수명이 지역 변수이면 지역 함수로 생성한다.
local F = function()
code that does something or other
F() -- 지역 변수 F는 여기서 보이지 않으므로,
-- 재귀 호출이 되지 못한다.
more code that does something or other
end
 local 키워드로 선언한 지역 변수의 사용 범위는
local statement 다음 statement부터이므로,
local statement 안에 포함된 F()는 사용 범위 밖에 있다.
재귀 함수를 지역 함수로 만들 때 주의할 점
 아래와 같이 선언문과 대입문을 분리하거나,
local F
F = function()
something or other
F() -- 지역 변수 F의 사용 범위 안이므로, 재귀 호출이 된다.
more something or other
end
 local function statement를 사용하면 이러한 분리를
자동으로 처리해준다.
local function F()
something or other
F() -- 지역 변수 F의 사용 범위 안이므로, 재귀 호출이 된다.
more something or other
end
 함수를 호출할 때 실질 매개변수가 문자열 하나밖에
없으면, 괄호를 생략하고 호출할 수 있다.
> print "with a space"
with a space
> print"or without"
or without
함수 호출할 때 괄호 생략
 function expression 설명에서 말했듯이,
동일한 function expression이더라도 실행될 때마다
새로운 함수를 생성한다. 왜냐하면, 함수 정의에
업밸류가 포함되어 있다면 실행될 때마다 다르게
작동하는 함수가 생성될 수 있기 때문이다.
 업밸류의 다른 이름은 'external local variable'이다.
 업밸류를 포함하는 함수를 클로져라고 부른다.
 업밸류의 예는 다음 슬라이드의 예제 참조
업밸류와 클로져 (Upvalues and Closures)
> -- Returns a function that tests whether a number is
> -- less than N:
> function MakeLessThan(N)
>> return function(X)
>> return X < N
>> end
>> end
>
> LessThanFive = MakeLessThan(5)
> LessThanTen = MakeLessThan(10)
> print(LessThanFive(5))
false
> print(LessThanTen(5))
true
 다수의 클로져가 한 개의 업밸류를 공유할 수 있다.
 공유되는 업밸류는 private state로 작용한다.
즉, 클로져가 호출될 때 변경할 수 있고 다음 호출될
때까지 유지되는 데이터라는 측면에서 상태(state)라고
부를 수 있고, 오직 업밸류를 공유하는 클로져들에서만
접근가능하다는 측면에서 전용(private)이라고 부를 수
있다.
 공유되는 업밸류의 예는 다음 슬라이드의 예제 참조
private state로서 업밸류
> -- Returns two functions: a function that gets N’s value,
> -- and a function that increments N by its argument.
> function MakeGetAndInc(N)
>> -- Returns N:
>> local function Get()
>> return N
>> end
>> -- Increments N by M:
>> local function Inc(M)
>> N = N + M
>> end
>> return Get, Inc
>> end
>
> -- Make two pairs of get and increment functions, one
> -- pair initialized to 0 and the other initialized to 100:
> GetA, IncA = MakeGetAndInc(0)
> GetB, IncB = MakeGetAndInc(100)

Lua 문법 -함수

  • 1.
  • 2.
    > function Greet(Name) >>print(“Hello, “ .. Name .. “.”) >> end > Greet(“John”) Hello, John. > Greet(“Jane”) Hello, Jane. > Greet(“Aloysius”) Hello, Aloysius. 함수 정의, 함수 호출
  • 3.
     함수 정의는function 키워드로 시작해서 end 키워드로 마친다.  함수 정의가 하나의 statement이다.  매개변수는 ()안에 ,로 구분해서 나열한다.  함수를 호출할 때 실질 매개변수 개수가 형식 매개변수 개수보다 많으면 남는 것은 버리고, 형식 매개변수 개수보다 적으면 부족한 것은 nil로 보충 (여러 변수에 값을 대입할 때와 동일) 함수 정의, 함수 호출
  • 4.
     함수 정의에서return 문을 사용하면, 함수를 호출할 때 함수에서 정의된 코드를 실행할 뿐만 아니라 값을 돌려준다. 즉, 함수 호출이 expression이 된다. > function Average(Num1, Num2) >> return (Num1 + Num2) / 2 >> end > Average(0, 10) > print(Average(0, 10)) 5 > print(Average(Average(10, 20), Average(30, 40))) 25 함수에서 값 리턴하기
  • 5.
     return 문은블록의 마지막 statement이어야 한다.  블록에는 do 블록, 반복문(for, while, repeat), if문의 선택지와 함께 함수 정의와 청크(chunk)가 있다.  청크는 한번에 처리되는 코드의 부분이다. 예를 들어,  루아 파일 (루아 파일을 실행하면 함수로 실행된다)  루아 인터프리터에 입력했을 때 연속 프람프트(>>)가 아닌 일반 프람프트(>)를 나오게 할 한 줄의 코드  루아 인터프리터에 입력했을 때 연속 프람프트로 연결된 여러 줄의 코드. 마지막 줄을 입력했을 때 일반 프람프트가 나와야 함.  그러므로, 디버깅 목적으로 함수 중간에 return을 넣으려면 do return end와 같이 do 블록을 사용한다. return 문
  • 6.
    > function ReturnArgs(Arg1,Arg2, Arg3) >> return Arg1, Arg2, Arg3 >> end > print(ReturnArgs(1, 2, 3)) 1 2 3 > print(ReturnArgs(ReturnArgs(1, 2, 3))) 1 2 3 > A, B, C = ReturnArgs(“alpha”, “bravo”, “charlie”) > print(A, B, C) alpha bravo charlie 여러 개의 값을 리턴하기
  • 7.
     대입문, return문, 함수의 매개변수에서 값들을 ,로 구분해서 나열하는 것을 value list라고 부릅니다.  여러 개의 값을 리턴하는 함수 호출을 expression으로 value list에 사용하면,  함수 호출이 value list의 마지막에 위치한 경우에는 모든 리턴값이 value list에 포함되지만,  함수 호출이 value list의 처음과 중간에 위치한 경우에는 첫번째 리턴값만 value list에 포함되고 나머지 리턴값은 버려짐. Value Lists
  • 8.
    > print(1, ReturnArgs(“a”,“b”, “c”)) 1 a b c > print(ReturnArgs(1, 2, 3), “a”) 1 a > print(ReturnArgs(1, 2, 3), ReturnArgs(4, 5, 6), >> ReturnArgs(7, 8, 9), ReturnArgs(10, 11, 12)) 1 4 7 10 11 12 > A, B, C, D = ReturnArgs(1, 2, 3), ReturnArgs(4, 5, 6) > print(A, B, C, D) 1 4 5 6 Value Lists
  • 9.
     값을 리턴하지않는 함수 호출을 expression으로 value list에 사용하면,  함수 호출이 value list의 마지막에 위치한 경우에는 리턴값이 없으므로 value list에 아무런 값도 추가되지 않지만,  함수 호출이 value list의 처음과 중간에 위치한 경우에는 리턴값이 없는 것이 nil로 전환되어서 value list에 nil이 추가됨.  함수 호출이 value list의 마지막에 있거나 단독으로 있을 때에도 함수 호출을 ()로 묶으면 하나의 값으로 강제로 전환됩니다. Value Lists
  • 10.
    > function DoNothing() >>end > print(1, DoNothing()) 1 > print(DoNothing(), 2, DoNothing()) nil 2 > print(DoNothing(), 2, (DoNothing())) nil 2 nil > print(ReturnArgs(1, 2, 3), (ReturnArgs(4, 5, 6))) 1 4 Value Lists
  • 11.
     대입문, 함수매개변수에서 개수가 일치하지 않아서 발생하는 조정(남는 값을 버리거나 부족한 값을 nil로 보충)은 각 expression에서 발생하는 전환 후에 일어난다. > A, B = DoNothing(), 1, 2 > print(A, B) nil 1 > A, B, C = ReturnArgs(1, 2, 3), 4 > print(A, B, C) 1 4 nil Value Lists
  • 12.
     이미 사용중인변수명을 다른 변수에 사용할 수 없다. 하지만, 규모가 큰 소프트웨어를 프로그래밍하거나 다른 사람이 만든 라이브러리를 사용할 때, 이 당연한 요구가 문제를 일으킬 수 있다. 내가 지금 선언해서 사용할 변수명이 프로그램의 다른 부분이나 사용하는 라이브러리에서 사용하지 않음을 항상 확인해야 하기 때문이다.  그래서, 변수의 사용범위를 제한하는 문법이 필요하게 되었다. 변수의 사용범위(scope)
  • 13.
     함수를 정의할때 사용하는 매개변수(함수명 옆에 () 속에 선언하는 변수)를 '형식 매개변수(formal argument)'라고 부르고,  함수를 호출할 때 사용하는 매개변수(함수명 옆에 () 속에 나열하는 expression)를 '실질 매개변수(actual argument)'라고 부른다.  형식 매개변수의 사용범위는 함수의 정의 안이다. 즉, 함수 밖에서는 형식 매개변수의 값에 접근할 수 없고, 함수 밖에 동일한 이름의 변수가 있을지라도 서로 다른 변수이므로 서로 간섭하지 않는다. 함수의 형식 매개변수와 실질 매개변수
  • 14.
    > -- Printsits one argument: > function PrintArg(Arg) >> print(Arg) >> end > PrintArg(true) true Arg는 형식 매개변수이고, true는 실질 매개변수이다. 함수의 형식 매개변수와 실질 매개변수
  • 15.
     사용범위가 프로그램코드의 특정 영역에 제한되는 변수를 '지역 변수'라고 부르고, 프로그램 전체에서 접근할 수 있는 변수를 '전역 변수'라고 부른다.  지역 변수의 예에는 함수의 형식 매개변수와 for 반복문의 반복에 사용되는 변수가 있다. 즉, 형식 매개변수의 사용범위는 함수 정의 안이고, 반복에 사용되는 변수의 사용범위는 for 반복문 안이다.  지역 변수는 사용범위 안에서 같은 이름을 가진 전역 변수나 더 큰 사용범위의 지역 변수를 가린다(shadow). 지역 변수 (Local Variables)
  • 16.
     local 키워드를 사용해서지역 변수를 생성할 수 있다.  지역 변수의 사용범위는 그 변수가 속한 블록 중에서 가장 안쪽 블록이다. 지역 변수
  • 17.
    > do >> localLcl = “aardvark” >> -- The first Lcl’s scope starts here. >> local Lcl = Lcl .. “zebra” >> -- The second Lcl’s scope starts here. >> print(Lcl) >> -- Both scopes end here. >> end aardvarkzebra local 키워드로 생성한 지역 변수의 사용범위는 local 문 다음 줄부터 시작된다.
  • 18.
     블록: do블록, 반복문의 몸체(repeat문은 until 뒤에 오는 expression 포함), if문의 선택지, 함수, 청크  청크: 루아 파일, 루아 인터프리터에 일반 프람프트(>) 가 나올 때까지 입력한 코드 > City = “New York” > local City = “London” > print(City) New York 지역 변수의 사용범위는 그 변수가 속한 블록 중에서 가장 안쪽 블록이다.
  • 19.
     statement나 expression이어떤 변화를 일으킬 때 사이드 이펙트라고 부른다.  print() 함수 호출이 화면에 무언가를 출력하거나 함수 호출이 전역 변수의 값을 바꾸는 것이 사이드 이펙트의 예이다.  함수가 값을 리턴하거나 지역 변수를 생성했다 지우는 것은 사이드 이펙트가 아니다. 리턴된 값으로 무언가를 하지 않는 한 리턴값 자체는 변화를 일으키지 않고, 지역 변수도 마찬가지다.  사이드 이펙트가 있는 함수는 실행 순서에 따라 결과가 달라질 수 있고, 프로그램의 다른 부분에 영향을 미칠 수 있으므로, 필요한 경우가 아니라면 사이드 이펙트가 없는 함수를 만드는 것이 낫다. 사이드 이펙트 (Side Effects)
  • 20.
     함수의 실질매개변수와 대입문에 사용된 value list의 평가 순서, binary operator가 피연산자를 취하는 순서 등은 보장되지 않는다. 즉, 루아 버전에 따라 달라진다.  여러 변수 대입문에서 = 오른쪽에 있는 value list의 평가가 대입보다 먼저 이루어지는 것은 보장된다.  함수의 실질 매개변수로 사용된 함수 호출은 그 매개변수를 사용하는 함수 호출보다 먼저 실행되는 것이 보장된다. 실행 순서
  • 21.
     루아의 불연산자인and와 or는 수학의 불연산자와 동일한 결과를 주면서도 작동 방식은 조금 다르다.  and는 첫 번째 피연산자가 false나 nil이면 그 값을 결과로 사용하고, 그 외의 값이면 두 번째 피연산자의 값을 결과로 사용한다.  or는 첫 번째 피연산자가 false나 nil이 아니면 그 값을 결과로 사용하고, false나 nil이면 두 번째 피연산자의 값을 결과로 사용한다.  첫 번째 피연산자의 값을 결과로 사용할 때에는 두 번째 피연산자는 아예 들여다보지도 않는다. 즉, 두 번째 피연산자가 함수호출이면 아예 호출하지도 않는다. Short-Circuit Evaluation
  • 22.
     함수에서 다른함수를 호출하면 호출된 함수가 실행된 후에 어느 함수의 어디로 돌아와야 되는지 알기 위해서 현재 실행중인 함수들의 정보를 스택에 저장하는데, 이 스택을 call stack이라고 부른다.  함수가 호출되면 호출된 함수의 정보가 콜 스택의 맨 위 스택 프레임에 쌓이고, 함수가 실행을 마치고 리턴하면 콜 스택의 맨 위 스택 프레임을 비우게 된다.  그러므로, 호출된 함수에서 다른 함수를 호출하는 일이 반복되면 콜스택은 점점 높아지게 되고, 호출된 함수가 실행을 마치고 리턴하면 콜스택은 점점 낮아지게 된다. 콜 스택 (Call Stack)
  • 23.
     자기 자신을호출하는 함수를 재귀 함수라고 부른다.  함수에서 정의된 지역 변수는 콜 스택의 스택 프레임에 생성되므로, 재귀 함수가 자신을 호출할 때마다 생성되는 지역 변수는 각각 다른 스택 프레임에 생성되어서 겹치지 않는다.  재귀 함수는 자신을 호출하는 recursive case와 함께 순환이 끝나는 base case를 설정해서 영원히 순환하는 것을 방지한다. 재귀 함수 (Recursive Function)
  • 24.
    > -- Returnsthe factorial of N: > function Fact(N) >> local Ret >> if N == 0 then >> -- Base case: >> Ret = 1 >> else >> -- Recursive case: >> Ret = N * Fact(N - 1) >> end >> return Ret >> end > > for N = 0, 5 do >> print(N .. “! is “ .. Fact(N)) >> end 0! is 1 1! is 1 2! is 2 3! is 6 4! is 24 5! is 120
  • 25.
     함수 호출이너무 깊어져서 콜 스택이 주어진 메모리를 모두 소진하면 스택 오버플로우 에러가 발생한다.  스택 오버플로우가 발생하는 주된 이유는 재귀 함수에서 base case에 걸리지 않고 무한 호출이 일어날 때이다. 예를 들어, 이전 슬라이드에 정의한 Fact() 함수에 매개변수를 음수로 해서 호출하면 스택 오버플로우가 발생한다. > Fact(-1) 스택 오버플로우 (Stack Overflow)
  • 26.
     return 문의expression이 함수 호출이면, 호출된 함수가 리턴한 값을 그대로 리턴하면 된다. 즉, 호출된 함수가 리턴한 후에 따로 할 일이 없게 된다. 따로 할 일이 없으므로 스택 프레임에 저장된 정보가 지워진다고 해도 문제가 발생하지 않는다.  그래서, return 문의 expression이 함수 호출이면, 그 호출은 콜 스텍에 새로운 스택 프레임을 추가하지 않고 자신을 호출한 함수의 스택 프레임을 재사용한다. 즉, 콜 스택이 커지지 않는다.  재귀 함수에 테일 콜을 사용하면 tail recursive라고 한다. 테일 콜 (Tail Calls)
  • 27.
    > function ForeverTail() >>return ForeverTail() >> end > ForeverTail() -- 스택 오버플로우 없이 영원히 멈추지 않는다. > function ForeverNotTail() >> ForeverNotTail() -- Is this a tail call? >> end > ForeverNotTail() -- 스택 오버플로우가 발생한다. -- 이 함수는 ForeverNotTail()과 end 사이에 리턴값의 리스트를 0으로 조정할 일이 남아있다.
  • 28.
     Fun() --return 문이 아니다.  return Fun() + 1 -- Fun() 리턴값에 1을 더해야 한다.  return X and Fun()  return (Fun()) -- ()가 Fun() 리턴값을 하나의 값으로 조정해야 한다.  return Fun(), Fun()  return 문에 함수 호출 하나만 expression으로 사용된 경우만 테일 콜이다. 테일 콜이 아닌 경우들
  • 29.
    -- Returns thefactorial of N (tail-recursively). 형식 매개변수가 2개이지만, -- 호출할 때에는 Fact(5)처럼 1개의 실질 매개변수만 주고 호출해야 한다. function Fact(N, Acc) -- Initialize the accumulator to 1: Acc = Acc or 1 if N == 0 then -- Base case: return Acc else -- Recursive case: return Fact(N - 1, N * Acc) end end tail recursive로 만든 팩토리얼 함수
  • 30.
     루아에서 함수는다른 종류의 값들(숫자, 문자열, 불값, nil)과 동일하게 취급 가능한 값이다.  함수의 매개변수로 넘길 수 있다. > print(type(print)) function  다른 변수에 대입할 수 있다. > RealPrint = print > function FakePrint(Val) >> RealPrint("Inside FakePrint:", Val) >> end > print = FakePrint > print("Hello") Inside FakePrint: Hello 값으로서 함수, 변수로서 함수명
  • 31.
     ==, ~=연산자의 피연산자로 사용해서 비교할 수 있다. > RealPrint = print > print(print == RealPrint) true  함수의 리턴값으로 함수를 리턴할 수 있다. > function MakeDoNothing() >> return function() end -- function expression >> end  함수를 print 함수로 출력하거나 tostring 함수로 문자열로 바꿔보면, function이라는 단어와 콜론과 함께 16진수 숫자가 나온다. 이 16진수 숫자는 메모리에서 이 함수의 주소이다. > print(print) function: 00000000006D8C70
  • 32.
     함수가 값이고,함수명이 변수라고 한다면, 함수 정의는 일종의 대입문이라고 할 수 있다. 함수 정의 function name(formal arguments) statements end -- function statement name = function(formal arguments) statements end -- function expression 대입문 = 오른쪽에 function으로 시작해서 end로 끝나는 부분은 function expression으로, 새로 생성된 함수를 값으로 가지는 expression이다.
  • 33.
     function expression은다른 종류의 expression이 사용되는 곳은 어디든 사용할 수 있다.  function expression은 평가될 때마다 새로운 함수를 만들어낸다. 즉, 동일한 function expression이라고 하더라도 실행될 때마다 그 값은 새로운 함수이다. > function MakeDoNothing() >> return function() end >> end > print(MakeDoNothing() == MakeDoNothing()) false Function Expression
  • 34.
     함수에 꼭이름을 붙일 필요는 없다. 이름을 붙이지 않고 function expression으로 사용할 수 있고, anonymous function이라고 부른다.  anonymous function을 호출할 때에는 다음과 같이 function expression을 괄호로 묶어서 호출한다. > (function(A, B) >> print(A + B) >> end)(2, 3) 5 Anonymous Function
  • 35.
     함수를 지역변수에 대입할 수 있다.  다음과 같이 function expression으로 할 수도 있고, > do >> local LclAverage = function(Num1, Num2) >> return (Num1 + Num2) / 2 >> end >> print(LclAverage(10, 20)) >> end 15 > print(LclAverage) nil 지역 함수 (Local Functions)
  • 36.
     다음과 같이function statement로 할 수도 있다. > do >> local function LclAverage(Num1, Num2) >> return (Num1 + Num2) / 2 >> end >> print(LclAverage(10, 20)) >> end 15 > print(LclAverage) nil
  • 37.
     다음과 같이지역 변수 선언과 함수 정의를 분리할 수도 있다. > do >> local LclAverage >> function LclAverage(Num1, Num2) >> return (Num1 + Num2) / 2 >> end >> print(LclAverage(10, 20)) >> end 15 > print(LclAverage) nil  위의 예처럼 function statement로 함수를 정의할 때도 함수명이 지역 변수이면 지역 함수로 생성한다.
  • 38.
    local F =function() code that does something or other F() -- 지역 변수 F는 여기서 보이지 않으므로, -- 재귀 호출이 되지 못한다. more code that does something or other end  local 키워드로 선언한 지역 변수의 사용 범위는 local statement 다음 statement부터이므로, local statement 안에 포함된 F()는 사용 범위 밖에 있다. 재귀 함수를 지역 함수로 만들 때 주의할 점
  • 39.
     아래와 같이선언문과 대입문을 분리하거나, local F F = function() something or other F() -- 지역 변수 F의 사용 범위 안이므로, 재귀 호출이 된다. more something or other end  local function statement를 사용하면 이러한 분리를 자동으로 처리해준다. local function F() something or other F() -- 지역 변수 F의 사용 범위 안이므로, 재귀 호출이 된다. more something or other end
  • 40.
     함수를 호출할때 실질 매개변수가 문자열 하나밖에 없으면, 괄호를 생략하고 호출할 수 있다. > print "with a space" with a space > print"or without" or without 함수 호출할 때 괄호 생략
  • 41.
     function expression설명에서 말했듯이, 동일한 function expression이더라도 실행될 때마다 새로운 함수를 생성한다. 왜냐하면, 함수 정의에 업밸류가 포함되어 있다면 실행될 때마다 다르게 작동하는 함수가 생성될 수 있기 때문이다.  업밸류의 다른 이름은 'external local variable'이다.  업밸류를 포함하는 함수를 클로져라고 부른다.  업밸류의 예는 다음 슬라이드의 예제 참조 업밸류와 클로져 (Upvalues and Closures)
  • 42.
    > -- Returnsa function that tests whether a number is > -- less than N: > function MakeLessThan(N) >> return function(X) >> return X < N >> end >> end > > LessThanFive = MakeLessThan(5) > LessThanTen = MakeLessThan(10) > print(LessThanFive(5)) false > print(LessThanTen(5)) true
  • 43.
     다수의 클로져가한 개의 업밸류를 공유할 수 있다.  공유되는 업밸류는 private state로 작용한다. 즉, 클로져가 호출될 때 변경할 수 있고 다음 호출될 때까지 유지되는 데이터라는 측면에서 상태(state)라고 부를 수 있고, 오직 업밸류를 공유하는 클로져들에서만 접근가능하다는 측면에서 전용(private)이라고 부를 수 있다.  공유되는 업밸류의 예는 다음 슬라이드의 예제 참조 private state로서 업밸류
  • 44.
    > -- Returnstwo functions: a function that gets N’s value, > -- and a function that increments N by its argument. > function MakeGetAndInc(N) >> -- Returns N: >> local function Get() >> return N >> end >> -- Increments N by M: >> local function Inc(M) >> N = N + M >> end >> return Get, Inc >> end > > -- Make two pairs of get and increment functions, one > -- pair initialized to 0 and the other initialized to 100: > GetA, IncA = MakeGetAndInc(0) > GetB, IncB = MakeGetAndInc(100)

Editor's Notes

  • #21 -- Prints a message with Val, then returns Val: function PrintReturn(Val) print(“Returning: “ .. tostring(Val)) return Val end 아래 코드의 여러 변수 대입문에서 = 오른쪽에 있는 PrintReturn() 함수 호출이 모두 실행된 후에 대입이 실행된다. > A, B = 1, 10 > A, B = PrintReturn(B + 1), PrintReturn(A + 1) Returning: 11 Returning: 2 > print(A, B) 11 2 함수의 실질 매개변수에서 호출되는 함수 호출이 먼저 실행된다. > print(PrintReturn(PrintReturn(1) + 1)) Returning: 1 Returning: 2 2
  • #23 -- A demonstration of functions calling functions. function A() print(“ About to enter B”) B() print(“ Just exited B”) end function B() print(“ About to enter C”) C() print(“ Just exited C”) end function C() print(“ Inside C”) end print(“About to enter A”) A() print(“Just exited A”)