apply — 익명 함수 적용하기
apply — 익명 함수 적용하기
이름 없는 함수, 즉 익명 함수를 즉석에서 정의해 그 자리에서 호출하고 싶을 때가 있어요. apply가 바로 그런 용도의 명령입니다. proc으로 프로시저를 만들면 이름을 지어야 하는데, 이름이 필요 없는 일회성 함수라면 apply가 더 알맞아요.
본문
apply func ?arg1 arg2 ...?
apply 명령은 함수 func를 인자 *arg1 arg2 ...*에 적용하고 그 결과를 리턴합니다.
함수 func는 두 요소 리스트 {args body} 또는 세 요소 리스트 {args body namespace} 형태예요(마치 list 명령으로 만든 것처럼). 첫 번째 요소 args는 func의 형식 인자를 지정합니다. 형식 인자 args의 명세는 proc 명령과 공유되며, 해당 매뉴얼 페이지에 자세히 설명돼 있어요.
body의 내용은 형식 인자에 해당하는 지역 변수들이 실제 인자 *arg1 arg2 ...*의 값으로 채워진 뒤 Tcl 인터프리터에 의해 실행됩니다. body가 실행되는 동안 변수 이름은 보통 지역 변수를 가리키며, 이 지역 변수들은 참조될 때 자동으로 생성되고 apply가 리턴할 때 삭제돼요. 함수의 인자마다 지역 변수 하나가 자동으로 만들어집니다. 전역 변수는 global 명령이나 upvar 명령으로만 접근할 수 있어요. 네임스페이스 변수는 variable 명령이나 upvar 명령으로만 접근할 수 있습니다.
apply의 호출은 Tcl 평가 스택(즉 uplevel을 통해 접근하는 프레임 스택)에 호출 프레임을 추가합니다. body의 실행은 이 호출 프레임 안에서, namespace로 지정된 네임스페이스에서 진행돼요. namespace가 지정되지 않았다면 전역 네임스페이스에서 실행됩니다. namespace가 주어지면 그 이름이 "::"로 시작하지 않더라도 전역 네임스페이스에 상대적으로 해석돼요.
apply의 의미는 다음과 같이도 표현할 수 있습니다:
proc apply {fun args} {
set len [llength $fun]
if {($len < 2) || ($len > 3)} {
error "can't interpret \"$fun\" as anonymous function"
}
lassign $fun argList body ns
set name ::$ns::[getGloballyUniqueName]
set body0 {
rename [lindex [info level 0] 0] {}
}
proc $name $argList ${body0}$body
set code [catch {uplevel 1 $name $args} res opt]
return -options $opt $res
}
예시
목록의 각 요소에 변환을 적용하는 간단한 일반 명령을 만들어 봅니다.
proc map {lambda list} {
set result {}
foreach item $list {
lappend result [apply $lambda $item]
}
return $result
}
map {x {return [string length $x]:$x}} {a bb ccc dddd}
→ 1:a 2:bb 3:ccc 4:dddd
map {x {expr {$x**2 + 3*$x - 2}}} {-4 -3 -2 -1 0 1 2 3 4}
→ 2 -2 -4 -4 -2 2 8 16 26
apply는 trace 명령에서 쓸 콜백을 정의할 때도 유용해요.
set vbl "123abc"
trace add variable vbl write {apply {{v1 v2 op} {
upvar 1 $v1 v
puts "updated variable to \"$v\""
}}}
set vbl 123
set vbl abc
더 알아보기
- proc, uplevel — 프로시저 정의와 호출 프레임
- 키워드: anonymous function, argument, lambda, procedure