본문 바로가기
Programming/TclTk

TCL 프로시져와 스코프

by Hunveloper 2022. 9. 5.
728x90

TCL 프로시져와 스코프

프로시져와 스코프

  • 프로시져는 자주 사용되는 일련의 명령들을 묶어 쉽게 사용할 수 있게 함
  • 각 프로시져는 변수에 대한 새로운 스코프를 가짐
  • 변수의 스코프란 그 변수가 정의되는 명령의 영역을 의미

proc 명령

  • TCL 프로시져를 정의하는 명령
proc name params body
  • name은 프로시져의 이름, 대소문자를 구분하며 어떤 문자도 포함할 수 있다
  • params는 파라미터 이름의 리스트
  • 정의된 프로시져는 다른 TCL 명령과 똑같은 방식으로 사용
  • 특정 값을 돌려주기 위해 return 명령을 사용할 수 있음
  • default로는 마지막 명령의 결과 값이 반환됨
  • 프로시져의 파라미터 이름 리스트는 기본 값을 가질 수 있음
proc p2 {a {b 7} {c -2} } {
    expr $a*100+$b*10+$c
}
puts [p2 3 5 7]
#357
puts [p2 3 5]
#348
  • 프로시져 p2는 1개, 2개 또는 3개의 인자로 불릴 수 있음
    • 1개 호출 시 a=value1, b=7, c=-2d
    • 2개 호출 시 a=value1, b=value2, c=-2
    • 3개 호출 시 a=value1, b=value2, c=value3
  • 프로시져는 args 키워드를 사용해 임의 개수의 파라미터를 사용하게 할 수 있음
proc argtest {a {b foo} args} {
    foreach param {a b args} {
        puts stdout "\t$param = [set $param]"
    }
}
puts [argtest 1 2 3 4]
#a = 1
#b = 2
#args = 3 4

rename

  • rename 명령은 명령어의 이름을 변경
  • rename은 기존 프로시져에 기능을 추가하기 위해 사용
rename foo foo.orig
  • foo 프로시져를 새로 정의할 때 foo.orig 프로시져를 호출할 수 있게 됨
  • rename의 두 번째 인자로 빈 스트링을 줌으로써 명령어를 사용할 수 없게 만들 수도 있음
rename exec { }

스코프

  • 변수 이름과 프로시져 이름은 다른 이름 공간을 사용하므로 같은 이름을 사용할 수 있음
  • 프로시져 내에서 정의된 변수는 그 프로시져 안에서만 사용가능
  • 프로시져가 리턴되면 그 변수도 소멸됨
  • 프로시져 외부에서 정의된 변수는 upvar나 global을 이용해야 프로시져 내에서 사용가능
  • 프로시져 외부의 이름과 일치하는 변수가 재정의되면 외부 변수는 값 변경에 영향을 받지 않음
set a 5
set b -8
proc p1 {a} {
    set b 42
    if {$a < 0} {
        return $b
    } else {
        return $a

    }
}

puts [p1 $b]
#42
puts [p1 [expr $a*2]]
#10

global 명령

  • 프로시져 외부에서 정의된 변수를 프로시져 내부에서 사용하기 위해서 global 키워드를 이용
global varName1 varName2 ...
  • global 명령은 외부 변수를 사용하고자 하는 프로시져의 내부에 포함
  • 하나의 프로시져 내부에서만 유효하기에 여러 개의 프로시져에 외부 변수를 사용할 경우에는
    모든 프로시져에서 global 명령을 사용해야 함
proc randomInit { seed } {
      global rand
      set rand(ia) 9301;# Multiplier
      set rand(ic) 49297;# Constant
      set rand(im) 233280;# Divisor
      set rand(seed) $seed;# Last result
}
proc random {} {
      global rand
      set rand(seed) \
          [expr ($rand(seed)*$rand(ia) +\
              $rand(ic)) % $rand(im)]
      return [expr $rand(seed)/double($rand(im))]
}
proc randomRange { range } {
      expr int([random]*$range)
}

puts [randomInit [pid]]
puts [random]
puts [random]
puts [randomRange 100]

전역 상태 유지를 위해 배열 사용

  • TCL 배열은 인덱스 값에 아무런 제한이 없기 때문에 유연하게 사용 가능
  • 배열 이름에 global 명령을 사용하면 배열을 구성하는 모든 원소가 전역 변수가 됨
proc ObjInit { o x y } {
      global obj
      set obj($o, x) $x
      set obj($o, y) $y
      set obj($o, dist) [expr sqrt($x * $x + $y * $y)]
}
proc ObjMove { o dx dy } {
      global obj
      if ![info exists obj($o, x)] {
          error "Object $o not initialized"
      }
      incr obj($o, x) $dx
      incr obj($o, y) $dy
      set obj($o, dist) [expr sqrt($obj($o, x) * $obj($o, x) + \
          $obj($o, y) * $obj($o, y))]
}

upvar명령을 이용한 “Call by Name” 구현

  • 프로시져에게 변수의 값이 아닌 이름을 넘겨 주고 싶을때 사용
upvar ?level? varName localVar
  • level 인자는 옵션으로 기본값은 1, TCL 호출 스택의 한 단계 위를 의미
  • level 인자에 숫자를 주면 그 숫자만큼 호출 스택의 단계를 넘어간 스코프를 의미
  • level 인자를 #number의 형태로 주면 절대값을 가지는 특정 스코프를 의미
  • #0은 global 스코프를 의미하므로 global foo는 다음 명령과 동일한 의미
upvar #0 foo foo
  • 상위 단계 스택의 변수는 일반 변수, 또는 배열 변수의 원소값, 또는 배열의 이름일 수 있음
  • 변수가 배열의 이름인 경우는 지역 변수가 배열로 취급
  • 변수의 이름을 인자로 주었을 때 그 값을 출력하는 프로시져
proc PrintByName {varName} {
    upvar $varName var
    puts stdout "$varName = $var"
}
set AAA "1234"
puts [PrintByName AAA]
#AAA = 1234
  • upvar 명령은 incr 명령을 개선하기 위해 사용할 수 있음
  • incr 명령은 변수가 존재하지 않는 경우에 에러를 발생
  • 변수가 존재하지 않는 경우 변수를 새로 생성하는 incr 프로시져
proc incr {varName {amount 1}} {
    upvar $varName var
    if [info exists var] {
        set var [expr $var + $amount]
    } else {
        set var $amount
    }
    return $var
}

puts [incr aa]
puts [incr aa]
puts [incr aa]

#1
#2
#3
  • upvar 명령을 사용한 스택 구현
proc Push { stack value } {
    upvar $stack S
    if ![info exists S(top)] {
        set S(top) 0
    }
    set S($S(top)) $value
    incr S(top)
}

proc Pop { stack } {
    upvar $stack S
    if ![info exists S(top)] {
        return {}
    }
    if {$S(top)==0} {
        return {}
    } else {
        incr S(top) -1
        set x $S($S(top))
        unset S($S(top))
        return $x
    }
}

puts [Push st 111]
puts [Push st 222]
puts [Push st 333]
puts [Push st 444]
puts "from here will be pop"
puts [Pop st]
puts [Pop st]
puts [Pop st]

#1
#2
#3
#4
#from here will be pop
#444
#333
#222

Global vs upvar

  • global은 전역변수를 바로 이용해서 이름을 동일하게 사용하는 방법
  • upvar는 call by reference 형태로 새로운 변수 이름에 연결해서 사용가능
728x90
728x90

'Programming > TclTk' 카테고리의 다른 글

TCL UNIX에서 작업하기  (0) 2022.09.06
TCL Eval  (0) 2022.09.05
TCL 순서제어 명령  (0) 2022.09.02
TCL 자료 구조  (0) 2022.09.02
TCL 스트링과 패턴 매칭  (0) 2022.09.02

댓글