Tcl의 프로시져

admin의 아바타

프로시져(procedure)는 인자를 지정할 수 있는 함수입니다. 또한 자주 사용되는 커맨드를 조합한 함수라고도 할 수 있습니다. 프로시져는 Tcl 커맨드와 동일한 동작을 하기 때문에, Tcl 커맨드와 마찬가지로 동일하게 사용하실 수 있습니다.

프로시져 이름 규칙은 아래와 같습니다.

  • 영 대/소문자를 구분합니다.
  • 어떤 문자라도 사용할 수 있습니다.
  • 변수명과 같은 이름도 가능합니다.


프로시져의 인자와 리턴

아래는 프로시져의 예입니다. max 프로시져는, 인자로 주어진 두 값중 큰 값을 리턴합니다.

proc max {a b} {
        if {$a > $b} {
                return $a
        } else {
                return $b
        }
}

max 10 20
=> 20
max 20 10
=> 20

인자는 리스트 형식으로 열거합니다. return 커맨드는, 프로시져로부터 값을 돌려줄때 사용합니다. 아래의 예는 주어진 3개의 평균값을 리턴합니다.

proc average {a b c} {
        set sum [expr $a + $b + $c]
        expr $sum / 3
}

average 10 20 30
=> 20

set 커맨드로 작성한 변수 sum은, 프로시져 밖에서 참조할수 없는 지역변수 입니다. 여기서는 return 커맨드를 사용하지 않았지만, 프로시져 안에서 마지막으로 실행한 커맨드의 결과는 프로시져의 리턴값이 되므로, return은 생략 가능합니다.

인자가 없는 프로시져

아래의 예는 프로시져에 인자가 없는 경우입니다.

proc getdate {} {
        set sec [clock seconds]
        clock format $sec -format "%Y/%m/%d"
}

getdate
=> 2008/04/06

글로벌(global) 변수의 참조

프로시져 안에서 글로벌 변수를 참조하고자 한다면, global 선언을 할 필요가 있습니다. 아래의 예는, Tcl 버전을 체크하여 국제화에 대응된 버전인지 아닌지를 검사합니다.

proc isI18N {} {
        global tcl_version
        expr $tcl_version > 8.0
}

isI18N
=> 1

Tcl8.0 이상에서는, global 커맨드를 사용하지 않아도 스코프 연산자(::)를 사용하여 글로벌 변수를 참조할 수 있습니다.

proc isI18N {} {
        expr $::tcl_version > 8.0
}

isI18N
=> 1

가변 인자

프로시져에 가변개의 인자를 주는 것도 가능합니다. 마지막 인자에 args를 사용하면 리스트의 형태로 취급할 수 있습니다. 아래의 예는 주어진 가변개의 인자 값의 합을 계산합니다.

proc sum {args} {
        set sum 0
        foreach num $args {
                incr sum $num
        }
        return $sum
}

sum 1 2 3
=> 6
sum 1 2 3 4
=> 10

디폴트(default) 인자

인자에 기본 값을 지정한 것으로, 프로시져를 호출시 가장자리로부터 인자를 생략할 수 있습니다. 아래의 예는 주어진 두개의 인자값의 합을 계산합니다. 두번째 인자를 생략하면 1로 지정됩니다.

proc add {a {b 1}} {
        expr $a + $b
}

add 10
=> 11
add 10 2
=> 12

인자의 참조

인자에 값을 건네주는 대신, 변수명을 건네줄 수 있다면 요긴하게 사용될 수 있을것입니다. 예를 들면, 배열 변수는 값으로 건네줄수 없으므로, 배열 변수명으로 건네주어 값을 참조할 수 있습니다. 아래의 예는, 배열 변수 ary의 이름을 건네주고 있습니다.

set ary(이름) 민인학
set ary() 31
set ary(성별)
proc getname {a} {
        upvar $a var
        return $var(이름)
}

getname ary
=> 민인학

upvar는 변수명의 참조(별명)를 작성합니다.

또, 인자에 변수명을 건내줌으로, 변수의 값이 직접 변경됩니다. 아래의 예에서는, 첫번째 인자로 주었던 변수의 값이 변경됨을 확인할 수 있습니다.

proc add {a {b 1}} {
        upvar $a var
        set var [expr $var + 1]
}

set a 1
add a
=> 2
set a
=> 2

global과 upvar 그리고 uplevel

출처: http://terzeron.net/wp/?p=244

프로시져 내부로 제어가 넘어가면 기본적으로는 외부나 상위의 프로시져의 변수를 사용할 수 없습니다. 매번 매개변수를 사용할 수는 없는 노릇이므로 global이나 upvar를 사용하여 상위레벨의 변수에 접근하게 됩니다. global 명령은 외부의 변수들을 전역변수로 지정하여 현재 프로시져 내부에서 지역변수처럼 자연스럽게 사용할 수 있게 합니다. upvar는 Tcl의 참조에 의한 호출 (call-by-reference)을 지원하기 위해 사용되는 명령으로 상위레벨의 프로시져에서 사용되는 변수를 새로운 이름으로 지정해서 지역변수로 사용하게 합니다.

set virtual_average 30.0
proc average parameters {
        global virtual_average
        set sum 0.0
        foreach i $parameters {
                set sum [expr $sum + $i]
        }
        return [expr $virtual_average + $sum / [llength $parameters]]
}

이 예제는 평균을 구하는 위의 예제에 가평균을 사용하기 위해 약간 손을 본 프러시져입니다. virtual_average가 외부에서 정의되었기 때문에 proc average에서는 사용할 수가 없어서 global 명령으로 virtual_average를 지역변수화한 것입니다. 다음의 예는 upvar를 이용해서 새로운 지역변수로 상위레벨의 변수를 참조하는 것을 보이는 것입니다.

proc print array_name {
        upvar $array_name arr
        foreach item [lsort $arr] {
                puts $item
        }
}

upvar는 정수값을 이용해서 참조의 대상이 되는 변수를 어느 레벨까지 찾을 것인가를 결정할 수 있는데, #0을 쓰면 무한대로 거슬러 상위로 올라갈 수 있습니다. 다음의 예는 바로 상위의 프로시져의 array_name이나 혹은 그 프로시져를 호출한 더 상위의 프로시져에서 사용된 array_name을 arr로 참조하겠다는 의미입니다. 그 다음 줄은 상위로 계속 거슬러 올라가면서 array_name이라는 변수를 찾겠다는 의미입니다.

upvar 2 $array_name arr
upvar #0 $array_name arr

upvar와 비슷한 명령으로 uplevel이라는 명령이 제공되는데, 이 명령은 eval과 upvar를 합쳐놓은 기능을 합니다. 다음 명령이 프러시져 내부에서 사용되면 이 프러시져를 호출한 프러시져의 x변수를 참조해다가 43을 대입하는 것을 실행할 수 있게 합니다.

uplevel 1 {set x 43}