GNU Octave 평가(Evaluation)

GNU Octave 평가(Evaluation)

Octave는 "표현식을 평가한다"는 개념이 특히 유연해요. 함수 이름을 문자열로 갖고 호출하거나, 다른 함수의 문맥(context)에서 표현식을 평가할 수 있거든요. 함수를 값처럼 다뤄야 할 때 필요한 기능들이에요.

출처: GNU Octave 평가

본문

이름으로 함수 호출하기: feval

feval 함수는 함수 이름이 담긴 문자열(또는 함수 핸들)로 그 함수를 호출할 수 있게 해줘요. 사용자가 제공한 함수를 호출해야 하는 함수를 작성할 때 특히 유용하죠.

feval ("acos", -1)   # ⇒ 3.1416

feval은 첫 번째 인자로 호출할 함수 이름을 받고, 나머지 인자는 그 함수에 전달돼요. 함수 핸들(예: @exp)과도 함께 쓸 수 있어요. 문자열 안의 함수 이름을 호출하는 유일한 수단이었던 시절도 있었지만, 지금은 함수 핸들이 더 깔끔한 문법을 제공해서 선호돼요.

f = @exp;
feval (f, 1)   # ⇒ 2.7183
f (1)          # ⇒ 2.7183  (같은 결과)

f가 함수 핸들인지, 문자열 함수 이름인지, 인라인 함수인지 미리 알 수 없다면 feval을 쓰면 돼요. 비슷한 함수로 스크립트 파일을 실행하는 run이 있어요. 로드 경로에 없는 스크립트는 run script로 실행할 수 있어요.

뉴턴 방법으로 사용자 함수의 근을 찾는 간단한 예시를 볼게요. feval (fname, x)로 함수를 호출하는 방식이 핵심이에요.

function result = newtroot (fname, x)
  delta = tol = sqrt (eps);
  maxit = 200;
  fx = feval (fname, x);
  for i = 1:maxit
    if (abs (fx) < tol)
      result = x;
      return;
    else
      fx_new = feval (fname, x + delta);
      deriv = (fx_new - fx) / delta;
      x = x - fx / deriv;
      fx = fx_new;
    endif
  endfor
  result = x;
endfunction

다른 문맥에서 평가하기: evalin / assignin

표현식을 평가하기 전에 그 안에 쓰인 변수의 값을 대입해야 하는데, 이 값은 심볼 테이블에 저장돼 있어요. 인터프리터가 새 함수를 시작하면 현재 심볼 테이블을 저장하고, 함수 파라미터와 nargin 같은 미리 정의된 변수로 초기화된 새 테이블을 만들어요. 함수 안의 표현식은 이 새 심볼 테이블을 사용하죠.

가끔은 함수를 호출하면 호출자(caller) 자신의 문맥에서 변수를 수정하도록 만들고 싶을 때가 있어요. 이는 C의 포인터처럼 "이름으로 넘기는(pass-by-name)" 스타일 함수를 가능하게 해요.

  • evalin (context, try)context("caller" 또는 "base")에서 표현식을 평가해요. eval과 같지만 평가 문맥이 다르죠.
  • assignin (context, varname, value) — "base" 또는 "caller" 문맥의 varnamevalue를 대입해요.

예를 들어 함수가 호출자 문맥의 변수 값을 읽어 저장 파일을 만드는 상황을 생각해 볼게요.

function save (file, name1, name2)
  f = open_save_file (file);
  save_var (f, name1, evalin ("caller", name1));
  save_var (f, name2, evalin ("caller", name2));
endfunction

여기서 "caller"는 데이터를 만든 함수이고, name1"x"라는 문자열이라 evalin ("caller", name1)은 곧 x의 값으로 평가돼요. 반대로 loadassignin ("caller", name, val)로 값을 호출자 문맥에 되돌려줘요.

이런 함수는 실제로는 드물게 쓰여요. 대표적인 예가 fail ('code', 'pattern')으로, 코드를 caller 문맥에서 평가하고 그 오류 메시지가 주어진 패턴과 일치하는지 확인해요. save/load 같은 건 C++로 작성되어 모든 변수가 이미 "caller" 문맥에 있어서 evalin이 필요 없어요.

더 알아보기