레이블이 수치해석인 게시물을 표시합니다. 모든 게시물 표시
레이블이 수치해석인 게시물을 표시합니다. 모든 게시물 표시

2015년 7월 21일 화요일

싸이랩(scialb)의 인라인(inline)함수와 파일에 저장된 함수

 Scilab함수는 다음과 같이 세 가지 상태가 있다.

➊ 컴파일이 안 된 상태
➋ 컴파일이 된 상태
➌ 프로파일링을 위한 구문이 들어간 상태로 컴파일 된 상태

만약 콘솔에서 함수를 정의한다면 endfunction이 입력되는 순간 컴파일이 된 후 workspace에 함수가 로드된다. 따라서 그 즉시로 호출하여 사용할 수 있다. 파일로 저장된 Scilab함수는 그 자체로는  컴파일이 안된 상태이고 Scilab에서 이 함수를 사용하기 위해서는 반드시 컴파일한 후 workspace에 로드하여야 한다. 프로파일링(profiling)이란 함수를 실행하는데 각각의 줄(line)이 몇 번 실행되었는지 그리고 실행 시간이 얼마나 되는지를 분석하는 것을 말하며 이를 위해서는 추가적인 구문이 필요하다. 함수가 컴파일 된 것인지 아닌지를 확인하려면 type(functoin_name) 함수를 실행해보면 된다. 결과값이 11이면 컴파일이 안된 함수이고 13이면 컴파일된 것이다.

 Scilab에서 함수를 생성하는 방법은 콘솔창에서 직접 입력하는 방법 외에도 다음에 소개하는 deff()함수를 이용하는 방법과 파일(sci파일)에서 로드하는 방법이 이다.

인라인 함수

 함수를 생성하는 방법은 앞 장에서 설명한 바와 같이 콘솔창에서 직접 입력하는 방법 외에 deff()함수를 이용하여 인라인 함수(in-line function)을 생성하는 방법도 있다. 예를 들어서 다음과 같다.


deff() 함수의 첫 번째 인자는 함수 헤더이고 두 번째 인자는 함수의 본체인데 반드시 문자열로 입력해야 한다. 세 번째 인자는 선택 사항인데 함수를 컴파일할 것인지 (‘c’) 아닌지 (‘n’)를 지정하는 문자열이고 생략하면 ‘c’가 기본적으로 선택된다. (‘p’는 프로파일링 기능을 넣은 컴파일 옵션임) 컴파일을 하는 것이 훨씬 더 실행 효율이 높으므로 보통은 기본 옵션으로 생성하면 문제가 없을 것이다.

파일에 저장된 함수

 함수를 파일에서 정의할 수 있으며 Scilab 함수 파일의 확장자는 .sci 으로 정해져 있다.( 다른 확장자도 사용할 수는 있다.) Scilab 의 sci파일에는 여러 개의 함수가 정의될 수 있으며 파일명과 함수명은 크게 상관이 없어서 서로 달라도 된다. 그리고 외부에서 각각의 함수를 개별적으로 호출할 수 있다. (MATALB은 파일명과 첫 함수명이 같아야 하고 첫 번째 함수를 제외하고 다른 함수들은 외부에서 호출할 수는 없다.)

 파일에 저장된 함수를 사용하기 위해서 컴파일/로드 되어야 한다. 이것을 수행하는 함수는 exec()함수이며 이 함수는 스크립트파일을 실행할 때뿐만 아니라 함수를 컴파일하고 로드하는데에도 사용된다. (인라인 함수를 정의하는 deff()함수도 컴파일을 자동으로 수행한다.)

>> exec(‘filename’ [,mode])

만약 함수 파일이 현재 디렉토리에 있다면 파일명만 써도 되는데 그렇지 않다면 파일명을 포함한 전체경로를 첫 번째 인자로 써주어야 한다. 두 번째 인자는 실행모드로서 선택사항이다. 옵션으로 주는 mode값과 해당하는 동작은 다음과 같다.

[표 1] exec()함수의 mode 옵션
mode 값
동작
0
초기값. 세미콜론(;)이 붙지 않은 명령의 결과만 콘솔창에 출력.
-1
아무런 출력도 하지 않음.
1
(마치 직접 콘솔창에 입력하듯이) 한 줄씩 명령과 그 결과를 출력.
2
프롬프트(-->>) 표시 ??? (0번과 거의 유사함)
3
각 명령 줄과 프롬프트 표시. (2번과 거의 유사함)
4
???
7
한 줄씩 실행 후 멈춤. 엔터키 입력으로 실행 (데모를 보일 때 유용함)

exec()함수로 읽어들인 사용자 함수는 workspace에 컴파일되서 올라오므로 바로 사용가능하다.


싸이랩(scialb)의 사용자 함수에서 외부 변수의 접근

 어떤 함수가 실행되면 기본 작업공간과 별도로 그 함수만의 작업공간이 생성되어 함수 내부에서 사용되거나 생성된 객체가 저장되게 된다. 그리고 함수의 실행이 끝나면 그 작업공간은 소멸되므로 함수 내에서 초기화된 변수는 함수의 실행 끝나는 시점에서 소멸된다. 함수 내부에서 사용할 수 있는 외부 변수는 다음과 같은 세 가지 경로로 전달된다.

       ❶ 입력 변수
       ❷ global로 선언된 변수
       ❸ 함수 내부에서 초기화되지 않고 호출된 곳의 workspace에서 정의된 변수

여기서 ❶과 ❷는 MATLAB에서도 동일하지만 ❸의 경우는 그렇지 않다. 함수가 호출되면 호출된 곳의 작업공간과는 별도로 그 함수만의 분리된 작업공간이 생성된다. 편의상 호출된 곳의 작업공간을 기본 작업공간(base workspace, BWS)라고 칭하겠다. 만약 함수 내부에서 어떤 변수를 참조하려고 하면 먼저 자신의 작업공간을 먼저 검색해보고 만약 없다면 기본 작업공간에서 찾아본다. 두 곳 모두에서 없다면 에러가 발생될 것이다.

예를 들어서 다음과 같은 함수가 정의되었다고 가정하자.

function y=fA(x)
         w=(w+1)^2 // (a)
         y=x*w // (b)
endfunction

이렇게 정의한 후

>> fA(1)

이라고 호출하면 에러가 발생된다. 왜냐면 (a)의 우변에서 쓰이는 변수 w는 현재의 workspace에서도 없고 기본 작업 공간에서도 초기화되지 않았기 때문이다.

[그림 1] 작업 공간의 개념도

하지만

>> w=1
>> fA(1)

와 같이 변수 w를 먼저 초기화한다면 이제는 4라는 결과값이 표시될 것이다. 한 가지 정말로 혼동하기 쉬운 것은 (a)의 우변의 변수 w는 기본 작업 공간에서 참조하고 좌변의 w는 이 함수 자신의 작업 공간에 새로운 변수로 초기화된다는 것이다. 따라서 (b)의 변수 w는 이렇게 새롭게 생성된 변수가 참조된다. 기본 작업 공간의 변수 w가 아님에 주의해야 한다. 이 변수 w와 기본 작업 공간의 변수 w는 별개의 것이므로 기본 작업 공간에서는 여전히 1값을 유지한다. 이렇게 기본 작업 공간에서 이미 초기화되어 있는 변수를 함수 내부에서 다시 초기화한 경우 “shadowed” 되었다고 하며 이 경우 기본 작업 공간의 변수를 접근할 수는 없게 된다.

[그림 2] 함수의 작업공간과 기본 작업공간은 분리되어 있다.

 만약 기본 작업 공간에서 변수 w를 global로 지정한 후 초기화했다면 어떤 일이 발생할까.

>> global w, w=1
>> fA(1)
>> disp(w)

이 경우에도 변수 w는 여전히 1값을 유지하며 global로 지정했더라도 함수 내부에서 그 값을 변경할 수는 없다. 즉, 앞의 경우와 동일하게 동작한다는 것이다. 그렇다면 함수 내부에서 여전히 기본 작업공간의 변수를 접근할 수 있는데 global로 지정해주어야 하는 필요가 있을까하는 의문이 생긴다. 일견 아무런 차이점이 없어 보이기 때문이다. 함수 내부에서 상위 workspace의 변수값을 변경해주려면 함수의 내부에서도 global로 지정해 주어야 한다.

function y=fB(x)
         global w
         w=(w+1)^2
         y=x*w
endfunction

이제

>> fB(1)
>> disp(w)

를 실행하면 변수 w는 4로 바뀐다. 즉, 상위 workspace의 변수 w가 fB()함수 내부에서 바뀐 것이다. 이와 같이 외부 변수의 값을 바꿀 필요가 있다면 외부에서뿐만 아니라 내부에서도 그 변수를 global로 지정해 주어야 한다.

[그림 3] 글로벌 변수의 동작 개념도

 함수의 결과값을 기본 작업 공간으로 내보내는 데에도 다음과 같은 세 가지 방법이 있다.

       ❶ 출력 변수로 반환하는 방법
       ❷ global로 지정된 변수를 이용하는 방법
       ❸ resume 혹은 return 명령의 인수를 이용하는 방법

❶ 은 함수의 정의부에서 선언한 출력변수를 통해서 기본 작업 공간으로 값을 내보내는 것이고 ❷는 직전 절에서 기술한 바와 같이 함수의 내부에서 global로 지정한 변수를 통해서 기초 작업 공간에서 접근할 수 있는 전역 공간에 변수값을 내보내는 방법이다.

 함수 내부에서 return 이나 resume을 만나면 그 즉시 함수가 종료되는데 보통은 단문으로 사용되어서 함수를 강제로 종료시키는 기능을 수행한다. 하지만 다음과 같은 문법도 가능하다.

[x1, x2, … xn] = return(a1, a2, … an)
[x1, x2, … xn] = resume(a1, a2, … an)
 
이러한 명령들은 함수 내부의 변수들 a1, a2, … , an 을 호출한 곳의 작업 공간에 변수 x1, x2, …, xn 으로 내보내는 기능을 수행한다. 예를 들면 다음과 같다.

function foo(a)
           a=a+1
           b=resume(a) //(*)
           c=52 // (**)
endfunction

 

이 예제에서 (**)줄은 실행되지 않고 (*)줄에서 함수는 종료된다. 또한 기본 작업공간에 변수 b를 생성시켜서 함수 내부의 a변수 값을 넘겨주게 된다. 따라서

>> foo(42)

라고 함수를 실행시키면 workspace에 변수 b가 43값으로 생성된다.

그렇지만 Scilab 매뉴얼(도움말)에 의하면 이러한 용법은 쓸데없이 프로그램의 복잡도만 증가시키므로 출력 변수를 이용하는 것이 바람직하다고 기술되어 있다. 따라서 가급적 ❶번 방법을 주로 사용하고 필요시 ❷번 방밥을 사용하도록 하는 것이 좋다. 또한, resume명령은 pause명령에 의해서 실행이 멈춘 함수를 다시 시작시키는 데에도 사용되는 명령이다.


싸이랩(scialb)의 사용자 함수 개요

 Scilab에서 함수의 정의는 다음과 같이 키워드 function 으로 시작하고 endfunction으로 끝난다.

function [y1, y2, … ] = function_name(x1, x2, …)
      …
      ...
endfunction

출력변수(들)은 대괄호 [ ] 로 묶지만 만약 출력 변수가 하나라면 대괄호는 생략이 가능하다. 입력변수는 괄호 ( )로 묶는데 만약 입력 변수가 없다면 정의에서는 괄호를 생략할 수 있다. 하지만 함수를 호출할 때는 입력 변수가 없다고 하더라도 괄호를 생략하지 못 한다. 함수의 몸체는 Scilab의 문장들(statements)로 작성된다.

 간단한 예로 입력변수를 단순하게 세 제곱하여 반환하는 함수는 다음과 같이 입력할 수 있다.

>> function y=pow3(x)
>> y=x^3
>> endfunction

이렇게 입력하면 pow3()라는 함수가 내부적으로 컴파일 된 후 작업 공간에 올라오며 이 후에 호출할 수 있다. 예를 들어서

>> a=pow(4)

 

라고 호출하면 변수 a에 64가 저장될 것이다.

 또 다른 예로서 숫자 x가 소수(prime number)이면 %t를 아니면 %f 를 반환하는 IsPrime()이라는 함수는 다음과 같다.

function y=isPrime(x)
         for n=2:(x-1)
                   if modulo(x,n)==0 then
                             y=%f
                             return
                  end
         end
         y=%t
endfunction

여기서 함수 내부의 return 명령을 만나면 그 즉시 함수의 실행이 종료되고 호출된 곳으로 되돌아가게 된다. 알고리듬은 2와 x-1 사이의 모든 수로 나누어 보고 한 번이라도 그 나머지가 0이라면 %f를 반환하는 아주 간단한 방법을 사용했다.

 Scilab의 함수는 변수처럼 취급된다. 예를 들어서 입력의 3제곱을 반환하는 다음과 같은  함수를 고려하자.

>> function y=foo(x), y=x^3, endfunction

전술한 바와 같이 이렇게 함수를 정의하면 작업 공간에 이 함수가 올라오게 된다. 그 다음 일반적인 변수에 이 함수를 대입할 수 있다.

>> foo2 = foo

이 명령을 보면 좌변의 새로운 변수에 함수명을 대입했다.  이렇게 되면 함수 본체가 복사되어 새로운 변수 foo2에 대입된다. 이 말은 이후에 원래의 함수 foo를 삭제/변형하더라도 foo2 함수에는 영향을 미치지 않는다는 말이다.

이렇게 함수를 변수처럼 취급할 수 있다는 것은 여러가지 이점이 있다. 함수 자체가 다른 함수의 입력 변수, 혹은 출력 변수가 될 수 있으며 구조체나 리스트의 필드가 될 수도 있다.

 만약 정의된 함수를 삭제하고 싶다면 변수를 삭제할 때와 같이 clear 함수를 이용하면 된다.

>> clear foo
>> clear(‘foo’)

이렇게 원래의 함수 foo()가 삭제되더라도 foo2()함수는 여전히 메모리에 남아있게 된다.

싸이랩(scialb)의 그래프창 생성/소멸/지정

 이전 포스트에서 plot()명령을 내리면 창이 자동으로 생성됨을 확인할 수 있다. 여기에서는 사용자가 임의로 여러 개의 그래프 창을 선택(생성)하고, 내용을 지우고, 창을 닫는 기능을 설명하도록 하겠다.

 그래프 창은 각각 고유의 아이디를 가지고 있다. 현재 열린 여러 개의 그래프 창들 중에서 특정한 아이디를 갖는 그래프 창을 선택하려면 scf() 함수를 호출하면 된다. (Set Current Figure)

>> scf(n) // n번 그래프 창 선택 (없으면 생성)
>> h = scf(n) // n번 그래프창 선택(없다면 생성) (h : 핸들)
>> scf() // (직전 아이디+1)을 가지는 새로운 그래프창 생성.
              (초기에는 0번이 생성됨)

이 함수를 사용하면 그래프 창이 선택되고 만약 지정한 아이디를 가지는 그래프창이 없다면 생성된다. 이후의 plot 명령은 이렇게 선택된 창에 그래프가 그려지게 된다. 그래프 창의 캡션바에 보면 이 아이디가 표시된 것을 확인할 수 있다.


이 아이디는 현재 그래프 창을 선택하거나 핸들(handle)을 얻거나(gcf 함수 이용) 창을 소멸시키는 등의 일을 할 수 있는 그래프 창의 고유 번호이다. 아이디로부터 핸들을 얻기 위해서는 다음과 같이 gcf()함수를 이용한다.

>> h=gcf() // 현재 선택되어 있는 그래프 창의 핸들을 얻는다.
>> h=gcf(n) // 아이디가 n인 그래프 창의 핸들을 얻는다.

아이디와 핸들은 구분해야 하는데 아이디는 여러 개의 그래프 창을 열었을 때 각각을 구분하기 위한 번호인 반면  핸들은 그래프 창의 각종 속성을 제어할 수 있는 내부 변수값으로서 자동으로 생성되어 부여된다. 여러 개의 그래프창들이 열려 있을 때 특정 아이디를 가지는 그래프창을 선택할 때도 이 함수를 사용하면 된다.

 현재 그래프 창에 그려진 내용을 지우려면 clf()를 이용하면 된다. 용례는 다음과 같다.

>> clf() // 현재 그래프의 모든 내용을 지운다.
>> clf(h) // 핸들이 h인 그래프창의 모든 내용을 지운다.
>> clf(n, ‘reset’) // 아이디가 n인 그래프 창의 모든 내용을 지우고 속성도 초기화시킨다.

두 번째 인수로 ‘clear’ 나 ‘reset’을 줄 수 있는데 ‘clear’는 단순히 내용만을 지우는 것이고 ‘reset’은 내용을 지운 후 변경된 속성까지 초기화시키는 것이다.

 그래프창을 닫으려면 close()나 xdel()함수를 이용하면 된다. 아이디로 닫으려면 xdel()함수를 이용하고 핸들로 닫으려면 close()함수를 이용하면 된다.

>> xdel() // 현재 선택된 그래프창을 닫는다.
>> xdel(n) // 아이디가 n인 그래프창을 닫는다.
>> close() // 현재 선택된 그래프창을 닫는다.
>> close(h) // 핸들이 h인 그래프창을 닫는다.


싸이랩(scialb)의 2차원 그래프와 plot()함수

2차원 그래프

 Scilab에서 2차원 그래프를 그려주는 함수들 중 자주 사용되는 것들을 다음 도표에 정리하였다.

[표 1] Scilab의 2차원 그래프 함수
함수
기능
plot()
x-y 그래프를 그려준다.
polarplot ()
극좌표 그래프를 그려준다.
bar(), barh()
막대그래프
histplot()
히스토그램
pie()
파이차트

가장 많이 쓰이는 함수는 plot()함수이다.

plot() 함수

 plot()함수는 가장 기본적인 2차원 x-y 그래프를 만들어주는 함수로서 사용법이 MATLAB의 그것과 매우 유사하다. 첫 번째 예로 sin(x)의 그래프를 0과 2*%pi 범위에서 그려보도록 하겠다. 먼저 x벡터를 linspace()함수를 이용하여 다음과 같이 생성한다.

>> x = linspace(0, 2*%pi)

그 다음 y 데이터를 다음과 같이 생성한다.

>> y = sin(x)

그럼 다음 plot()함수를 이용하면 된다.

>> plot(x, y)

plot 함수의 내부적인 작동은 x-y 쌍의 점들을 직선으로 이어주는 것이다. 이 예에서는 100개의 (x,y) 좌표점을 서로 직선으로 연결하였으며 점들이 조밀하므로 마치 곡선처럼 보이는 것이다. 주의할 점은 x가 행벡터라면 y도 행벡터여야하고 x가 열벡터라면 y도 열벡터여야 한다는 점이다.

 다른 방법으로는 x좌표만 생성한 후 두 번째 인수로 함수명을 적어주는 것이다.

>> plot(x, sin )

 

그러면 자동으로 x벡터에 대해서 sin(x)를 구한 다음 그래프를 생성해 준다. (이 방법은 별로 권장되지 않는다.)

 만약 x는 행벡터이고 y가 행렬이라면 y행렬의 각각의 행에 대해서 그래프를 그려준다. 이 때 x의 열수와 y의 열수는 같아야 한다. 예를 들어서

>> Y = [sin(x); cos(x)]

명령은 Y 행렬을 만들어 주는데 첫 행은 sin(x)함수 값들이, 두 번째 행은 cos(x)함수 값들이 대입된다. 이제

>> plot(x, Y)

라고 하면 다음과 같이 x-Y(1,:) 과 x-Y(2, :) 에 대한 그래프를 하나의 창에 도시한다. 이 명령은 다음과 동일하다.

>> plot(x, sin(x), x, cos(x))
 
아래 결과를 보면 두 개의 그래프를 한 개의 그래프창에 도시하고 자동으로 색상이 다르게 지정되었음을 확인할 수 있다.


 함수 plot()의 세 번째 옵션으로 선의 색과 모양을 지정해 줄 수 있다. 아래 표에 자세한 옵션을 정리하였다. (MATLAB의 옵션과 매우 흡사하다.)

[표 2] plot()함수에서 선의 모양 지정자
기호
선 색
모양
기호
모양
마커
기호

r
red
-
직선(solid line)
+
Plus sign
g
green
--
파선(dashed line)
o
Circle
b
blue
:
점선(dotted line)
*
Asterisk
c
cyan
-.
일점쇄선 (dash-dotted line)
.
Point
m
magenta


x
Cross
y
yellow


's'
Square
k
black


'd'
Diamond
w
white


^
Upward-pointing triangle




v
Downward-pointing triangle




>
Right-pointing triangle




<
Left-pointing triangle




'p'
Five-pointed star (pentagram)





No marker (default)

이 세 가지 옵션을 조합하여서 다양한 선의 모양을 나타낼 수 있다.  예를 들어서 파란색 파선으로 그리고 싶다면 ‘b--’ 라고 세 번째 옵션으로 주면 된다.

>> plot(t, sin(t), ‘b--’)


실선을 유지하고 녹색원으로 마킹을 하려면 ‘g-o’라고 입력하면 된다.

>> plot(t, cos(t), ‘g-o’)


 그래프 축에 격자선(grid)을 추가하려면 xgrid(n)함수를 실행시키면 된다. n은 자연수로서 격자선의 색을 지정한다. 예를 들면 다음과 같다.

>> plot(t, cos(t), ‘r:’)
>> xgrid(2)
 

이 그림을 보면 xgrid(2) 명령에 의해서 파란색 격자선이 x축과 y축에 생겼음을 알 수 있다.

 그래프의 축의 범위를 지정하려면 mtlb_axis()라는 함수를 이용하면 된다.(Matlab의 axis라는 함수와 유사하게 만들어진 함수임) 예를 들어서 위 그래프에서 x축의 범위를 정확히 [0, 2pi] 범위로 맞추고 y축은 [-2, 2]로 넓히고 싶다면 다음과 같이하면 된다.

>> mtlb_axis([0 2*%pi -2 2])

또한 이 그림에 제목을 붙이려면 title() 함수를 이용한다.

>> title(‘sin and cos’)

그러면 그림의 상단에 그럼의 제목(title)이 생성된다. x축에 제목을 붙이는 함수는 xlable()이고 y축은 ylabel()함수를 사용한다.

>> xlabel(‘x-axis’)
>> ylabel(‘y-axis’)

타이틀과 x축, y축을 한꺼번에 지정하고 싶다면 xtitle()함수를 이용하면 된다.

>> xtitle(‘sin and cos’, ‘x-axis’, ‘y-axis’)

입력 인수는 차례대로 제목, x축 라벨, y축 라벨로 표시할 문자열이다. 만약 각각의 라벨을 사각형으로 둘러쌀려면 마지막 옵션으로 1을 주면 된다.

>> xtitle(‘sin and cos’, ‘x-axis’, ‘y-axis’, 1)

 

재미있는 것은 title(), xaxis(), yaxis, xtitle() 명령은 LaTeX 명령어를 사용할 수 있어서 미려한 수식을 출력할 수 있다. 예를 들면 다음과 같다.

>> title(‘$\frac{sin(x)}{x}$’)

 

 여러 개의 그래프를 하나의 그래프 창에 도시하려면 subplot(r,c,n)함수를 사용하면 되는데 현재 그래프 창을 r행 c열로 분할한 다음 그 중 n번을 선택한다. 예를 들면 다음과 같다

>> subplot(2,1,1) // ❶
>> plot(t,sin(t),'b:+')
>> subplot(2,1,2) // ❷
>> plot(t,cos(t),'k-.')

위의 ❶에서 subplot(2,1,1)이라고 입력했는데 이것은 현재 그래프 창을 2x1 으로 분할한 다음 그 중 1번째 자식창을 선택한다. 따라서 그 다음에 오는 plot()함수는 그 1번 자식창에 그려진다.  ❷에서는 subplot(2,1,2) 라고 했는데 2번째 자식창을 선택하는 것이다. 아래 결과를 보면 이 명령의 동작이 이해가 갈 것이다.


subplot(2,1,1) 은 subplot(211)이라고 숫자들 길 그냥 붙여서 쓸 수도 있다. 예를 들어서 subplot(221)이라면 2x2로 나누고 그 중 첫 번째 자식창을 선택하라는 것이다. 이와 같이 subplot()함수를 이용하면 하나의 그래프 창에 여러개의 그래프를 분할하여 도시할 수 있다.