레이블이 function인 게시물을 표시합니다. 모든 게시물 표시
레이블이 function인 게시물을 표시합니다. 모든 게시물 표시

2015년 7월 16일 목요일

MATLAB의 함수 m파일의 입출력 변수 개수

 함수에 대해서 설명한 이전 포스트에서 함수 정의부에 명시된 입력 변수의 개수보다 적은 입력변수를 함수에 넘겨주는 경우 못받은 변수는 생성이 되지 않으므로 이를 따로 처리해야 한다고 하였다. 그렇다면 함수 내부에서 입출력 변수의 개수를 어떻게 알아낼 수 있을까? 이 작업을 위해서 nargin변수와 nargout변수가 마련되어 있다. 이 변수들은 모두 정수값을 가지며, nargin변수는 호출된 함수 내부에서 그 함수로 인가된 입력 변수의 개수값을 가지고 nargout 변수는 출력 변수의 개수를 가진다. 이들 변수값을 이용하여 여러 가지 입출력 방법에 대해서 대처를 할 수 있다. 앞에서 예를 든 mimo()함수를 다음과 같이 수정해 보자.

1 : function [y1,y2]=mimo2(x1,x2,x3)
2 : % function [y1,y2]=mimo(x1,x2,x3)
3 : % y1은 입력변수들의 합
4 : % y2는 입력변수들의 곱
5 : if (nargin==1)
6 :     y1=x1;
7 :     y2=x1;
8 : elseif (nargin==2)
9 :     y1=x1+x2;
10:     y2=x1*x2;
11: elseif (nargin==3)
12:     y1=x1+x2+x3;
13:     y2=x1*x2*x3;
14: else
15:    y1 = 0;
16:    y2=0;
17: end

이 수정된 함수에서 5번 줄과 8번 줄 그리고 11번 줄에서 nargin변수가 사용이 되었다. nargin==1이 참이라면, 즉 입력변수가 하나라면 6번 줄과 7번 줄을 수행시킨다. nargin==2이 참이라면, 즉 입력변수가 두개라면 9번 줄과 10번 줄을 수행시킨다. nargin==3이 참이라면, 즉 입력변수가 세 개라면 12번 줄과 13번 줄을 수행시킨다. 마지막으로 모든 경우가 거짓이라면, 즉 입력 변수가 없다면 15, 16번 줄을 수행시킬 것이다. 입력변수가 4개 이상이라도 else 문에 걸려서 15,16번 줄이 수행된다.

>> [x,y]=mimo2()
x = 0
y = 0
>> [x,y]=mimo2(11)
x =  11
y =  11
>> [x,y]=mimo2(11,22)
x =  33
y =  242
>> [x,y]=mimo2(11,22,33)
x =  66
y =  7986
>> [x,y]=mimo2(11,22,33,44)
x = 0
y = 0

연습삼아서 이 mimo2()함수를 switch-case문을 이용하여 수정해 보기 바란다.

MATLAB의 함수 m파일

 스크립트 m파일과 구별되는 다른 형태의 파일로서 함수(function) m파일이 있다. 지금까지 자주 사용해왔던 sin(), cos(), exp()같은 함수를 생각해 보면 사용자는 입력값을 이들 함수에 넘겨주고, 예를 들어 sin(pi)명령은 pi라는 값을 sin()함수에 넘겨주고 그 결과값 0을 얻게 된다. 내부적으로 어떠한 계산 과정이 있는 지는 일반 사용자는 알 필요가 없고 단지 입력에 대한 결과값을 얻을 뿐이다. 이와 같이 미리 정의된 함수 외에 사용자만의 함수를 작성할 수도 있다.

 함수 m파일은 입력 값과 출력 값이 존재하며 입력 변수를 받아서 특정한 기능이나 계산 등을 수행한 다음 그 결과 값을 반환하는 기능을 한다. 스크립트 m파일의 경우 생성된 변수들이 작업 공간에 위치하는 것과는 대조적으로 함수 m파일은 함수 내에서 사용된 변수들이 작업 공간과는 별도의 메모리 공간을 사용하며 함수의 수행이 끝나면 그 메모리 공간도 사라진다. 단지 출력 값만을 그 함수가 호출된 곳으로 반환시켜줄 뿐이다.
다음의 예제를 입력하여 보자.

function Y = flipud(X)
   [m,n] = size(X);
   Y = X(m:-1:1,:);

그리고 이 파일을 ‘flipud.m'으로 저장하자. 이 함수는 행렬 x를 받아서 행들의 순서를 역순으로 바꾼 후 출력하는 기능을 하는 함수이다. 이제 명령창에서 다음과 같이 명령을 내려보자.

>>A=rand(3,2) 󰎠
A =
0.0187 0.9737
0.3107 0.5865
0.7713 0.6441

>>B=flipud(A) 󰎠
0.7713 0.6441
0.3107 0.5865
0.0187 0.9737

위에서 입력 행렬 A의 행의 순서가 바뀌어서 반환된 것이 B에 저장됨을 알 수 있다. 또한 함수 내부에서 사용된 변수 m,n은 작업 공간에 나타나지 않음도 변수창에서 확인할 수 있다. 작업 공간에는 출력변수 B만이 생성되는 것이다.

 함수 m파일은 첫 번째 줄은 이 M파일이 함수라는 것을 나타내는 키워드 'function' 과 출력 변수, 함수 이름, 입력 변수 등으로 구성되며 문법은 다음과 같다.

function [y1, y2, …] = fname(x1, x2, …)

함수의 이름 fname은 m파일의 이름과 되도록이면 같도록 해야 한다. 만약 다르면 m파일의 이름이 우선하게 된다. 함수의 입출력 변수는 여러 개일 수도 있고 하나도 없을 수도 있다. 또한 함수의 정의부에 선언된 입출력 변수보다 적은 수의 변수로 함수를 호출할 수 있지만 더 많은 수의 입출력 변수를 지정하면 에러가 발생하게 된다. 다음의 예를 보자.

function [y1,y2]=mimo(x1,x2,x3)
% function [y1,y2]=mimo(x1,x2,x3)
% y1은 입력변수들의 합
% y2는 입력변수들의 곱
y1=x1+x2+x3;
y2=x1*x2*x3;

이 함수는 입력이 3개이고 출력이 2개인 함수이며 출력 y1은 입력들의 함을, y2는 입력들의 곱을 계산한다. 함수 정의부 밑에 ‘%’로 시작하는 문장들은 주석문으로서 M파일에서 '%'뒤에 오는 것들은 무시하며 실행하지 않으므로 이 기호 뒤에 설명문을 위치시키면 된다. 함수의 선언문과 첫 번째 실행문 사이의 주석문은 단순한 주석문이 아니라 명령창에서 help명령을 수행시켰을 때 보여주는 문장들이다. 명령창에서 다음을 수행해 보자.

>>help mimo 󰎠
function [y1,y2]=mimo(x1,x2,x3)
y1은 입력변수들의 합
y2는 입력변수들의 곱

이제 이 함수를 실행시켜 보자. 함수 정의부에서 출력 변수는 두 개로 지정이 되었지만 함수를 호출할 때는 출력변수를 0개, 1개, 2개 모두 지정할 수 있으며 3개 이상을 지정하면 에러가 발생한다.

>>mimo(3,4,5)
y1 =
12

>>y1=mimo(3,4,5)
y1 =
12

>>[y1,y2]=mimo(3,4,5)
y1 =
12
y2 =
60

>>[y1,y2,y3]=mimo(3,4,5)
-----------------------------------
* MATLAB 디버그 정보 (오류 발생) *
-----------------------------------
파일 이름 : C:\MATLAB_MFILES\mimo.m
라인 번호 : 1 --> function [y1,y2]=mimo(x1,x2,x3)
오류 내용 : 함수의 입출력 갯수가 올바르지 않습니다.

위에서 알 수 있듯이 출력 변수를 지정하지 않으면 첫 번째 출력 변수만을 화면에 보여준다. 그리고 출력 변수를 하나만 지정하면 두 번째 출력변수는 반환되지 않게 된다.
입력 변수도 아예 지정하지 않거나 한 개나 두 개만 입력할 수 있지만 함수 내에서 넘겨받지 않은 변수는 생성되지 않게 되므로 함수 실행 시 에러를 발생하게 된다.

>> mimo(3,4)
-----------------------------------
* MATLAB 디버그 정보 (오류 발생) *
-----------------------------------
파일 이름 : C:\MATLAB_MFILES\mimo.m
라인 번호 : 5 --> y1=x1+x2+x3; %...
오류 내용 : x3라는 변수나 함수가 없습니다. 이름이 맞는지 확인하십시요.

위의 경우는 세 번째 입력변수를 넘겨주지 않았으므로 mimo함수 내부적으로 x3라는 변수 자체가 없어서 덧셈을 수행하지 못하게 되는 것이다. 즉 적은 수의 입력 변수를 넘겨주는 것은 문법적으로 허용이 되지만 넘겨받지 못한 변수를 생성하기 위한 추가적인 실행문이 필요하게 된다. 이에 대해서는 다른 포스트에서 자세히 설명하겠다.



2015년 5월 27일 수요일

파이썬의 익명 함수(lambda function)

 익명 함수(lambda function)란 말 그대로 이름이 없는 함수이며 파이썬에서는 lambda 라는 키워드로 익명 함수를 정의할 수 있다. 주로 비교적 간단한 기능의 함수가 컨테이너의 요소로 들어가는 경우 혹은 다른 함수의 인자로 함수를 넘겨줄 필요가 있을 때 사용된다.

익명 함수는 다음과 같이 생성된다.

lambda 인자1,인자2, … : 표현식

익명 함수는 보통 한 줄로 정의된다. return문도 없으며 단지 인자들과 반환값들의 관계식으로만 표현된다. 예를 들어 두 수의 합을 반환하는 익명 함수는 다음과 같다.

>>> add = lambda a, b : a+b
>>> add(1,2)
3

이 익명 함수는 다음과 같이 일반 함수를 정의하는 것과 동일하다.

>>> def add(a, b) :
…..      return a+b
>>> add(1,2)
3

그렇다면 일반 함수와 익명 함수와의 차이점은 무엇인가? 익명 함수의 기능은 일반 함수보다도 훨씬 제한적이고 익명 함수로 할 수 있는 것은 일반 함수로도 모두 할 수 있다. 그렇다면 왜 익명 함수를 사용할까?

때로는 굳이 번거롭게 일반 함수를 정의할 필요가 없는 간단한 기능만을 구현해도 되는 경우가 있다.  예를 들어서 입력 인수가 0보다 크면 True를 반환하는 함수를 생각해 보자.

>>> def pos(x):
…..    return x>0
>>> list(filter(pos, range(-5,6)))
[1,2,3,4,5]

이것을 익명 함수를 이용하면 다음과 같이 간단하게 처리할 수 있다.

>>> list( filter(lambda x:x>0, range(-5,6)) )
[1,2,3,4,5]

또는 다음과 같이 간단한 함수들을 리스트의 요소로 지정할 경우도 있을 것이다.

>>> fl = [lambda x,y:x+y, lambda x,y:x*y]
>>> fl[0](1,2)
3
>>> fl[1](3,4)
12

이와 같이 람다 함수의 용도는 보다 간결하게 원하는 기능을 수행할 수 있도록 하는 것이다.


파이썬 함수의 기본값 인자 (default parameter)

 파이썬 함수의 기본값 인자(default parameter)란 함수를 호출할 때 인자의 값을 설정하지 않아도 기본값이 할당되도록 하는 기능이다. 기본값은 함수를 정의할 때 지정해준다. 예를 들어 보자.

def funcA(a=10):
   print('a='+str(a))

이 함수는 다음과 같이 두 가지 방법으로 호출할 수 있다.

>>> funcA() # a에 기본값인 10이 자동으로 저장된다.
a=10
>>> funcA(20) #a에 20이 전달된다.
a=20

이 예에서 인자 a는 기본값 인자이고 호출하는 쪽에서 값을 주지 않으면 기본적으로 10 값을 갖게 된다. 그래서 funcA()와 같이 값을 주지 않으면 10이 출력되고 funcA(20)과 같이 값을 주면 그 값이 인자 a 로 전달되는 것이다.

 다른 예를 들어 보자.

def printName(firstName, secondName='Kim'):
   print('My name is '+ firstName +' ' + secondName +'.')

이 함수도 다음과 같이 두 가지 방법으로 호출 가능하다.

>>> printName('Jang-Hyun')
My name is Jang-Hyun Kim.
>>> printName('Jang-Hyun', 'Park')
My name is Jang-Hyun Park.

기본값 인자는 값을 생략해서 호출할 수 있지만 일반 인자는 반드시 값을 지정해 주어야 한다. 따라서 printName()함수를 호출할 때 첫 문자열은 반드시 정해서 넘겨주어야 한다. 여기에서 printName()함수의 두 번째 인자를 입력하지 않으면 기본적으로 ‘Kim’으로 지정된다. 두 번째 인자를 명시적으로 지정하면 그것이 secondName 으로 지정된다.

 한 가지 주의할 점은 일반 인자와 기본값 인자가 같이 올 때에는 반드시 기본값 인자는 뒤에 와야 한다는 점이다. 즉, 아래와 같이 정의하면 오류가 발생한다.

>>> def printName(firstName='Kim', secondName):

기본값 인자가 두 개 이상일 때에서도 항상 일반 인자 뒤에 와야 한다.

>>> def add(a, b=0, c=0):
...     return a+b+c
...
>>> add(1)
1
>>> add(1,2) # b에 2가 들어간다.
3
>>> add(1,2,3) # b에 2, c에 3이 들어간다.)
6

이 예에서 보듯이 함수를 호출할 때 주는 순서 대로 기본값 인자에 값이 할당 됨을 알 수 있다.

 또 한 가지 주의할 점은 기본 인자는 최초의 호출 시에만 지정된 값으로 초기화 되고 이후의 호출에서는 그렇지 않다는 점이다. 따라서 리스트나 딕셔너리와 같은 가변(muatble) 객체를 기본 인자로 사용할 때 문제가 된다.

>>> def f(a, L=[]):
...     L.append(a)
...     return L
...
>>> f(1)
[1]
>>> f(2)
[1, 2]
>>> f(3)
[1, 2, 3]

이 예제를 보면 기본 인자 L은 최초의 호출에서만 빈 리스트로 초기화 되고 그 이후의 호출에서는 그 내용물은 유지된다. 따라서 리스트의 요소가 축적되는 것이다. (마치 C/C++에서의 static 변수와 비슷한 동작을 수행한다.)

 후속 호출에도 mutalbe 객체를 초기화하려면 다음과 같은 방법으로 코딩하면 된다.

def f(a, L=None):
   if L is None:
       L = []
   L.append(a)
   return L

이 경우 실행 결과는 다음과 같다.

>>> f(1)
[1]
>>> f(2)
[2]
>>> f(3)
[3]


파이썬의 함수(function) 정의와 호출

 함수(function)란 실행문들을 묶어서 하나의 블럭으로 만든 후 이름을 붙인 것을 말한다. 이렇게 수행문들의 집합을 함수로 정의하면 그 수행문들을 동작시킬 때 함수 이름을 이용한 간단한 호출(call)로 반복해서 실행시킬 수 있다.

 파이썬 함수는 다음과 같이 정의된다.

def 함수명(인자1, 인자2, …):
함수 본체

함수의 정의는 항상 def로 시작한다. 함수명은 일반적인 식별자를 사용하며 관례적으로 (변수명과 마찬가지로) 소문자로 시작한다. 그다음에 호출하는 쪽에서 넘겨 받을 인자들의 리스트를 괄호(...)안에 콤마로 구별하여 지정해 주고 콜론(:) 뒤에 함수의 본체를 작성해 주면 된다. 함수의 본체는 반드시 def 의 첫 글자 시작 위치보다 들여 써야 한다.

 간단한 예를 들어보자

>>> def sayHi():
print(‘hi’)

이 함수는 ‘hi’라는 문자열을 출력하는 함수이며 함수의 이름은 sayHi()이다. 입력 인자는 없으며 반환값도 없다. 이 함수를 호출하려면 다음과 같이 하면 된다.

>>> sayHi()
Hi

구구단을 출력하는 함수를 예로 들어보자.

>>> def gugu(n):
...     for x in range(2,10):
...         print('%d x %d = %d'%(n,x,n*x))

이 함수는 하나의 입력 인자를 받도록 되어 있다. 다음과 같이 호출한다.

>>> gugu(4)
4 x 2 = 8
4 x 3 = 12
4 x 4 = 16
4 x 5 = 20
4 x 6 = 24
4 x 7 = 28
4 x 8 = 32
4 x 9 = 36

>>> gugu(8)
8 x 2 = 16
8 x 3 = 24
8 x 4 = 32
8 x 5 = 40
8 x 6 = 48
8 x 7 = 56
8 x 8 = 64
8 x 9 = 72

함수명은 함수객체이며 다른 객체와 유사하게 대입이 가능하다. 예를 들어 앞에서 정의한 함수 sayHi()와 gugu()를 하나의 리스트로 묶을 수 있다.

>>> fl = [sayHi, gugu]

여기서 fl 리스트의 첫 번째 요소는 함수 sayHi 이고 두 번째 요소는 gugu 이다. 따라서 다음과 같은 함수 호출이 가능하다.

>>> fl[0]()
Hi.
>>> fl[1](9)
9 x 2 = 18
9 x 3 = 27
9 x 4 = 36
9 x 5 = 45
9 x 6 = 54
9 x 7 = 63
9 x 8 = 72
9 x 9 = 81


또는 함수를 다른 변수에 대입할 수도 있다.

>>> kuku = gugu

이제 kuku 는 함수이며 gugu()함수와 같은 함수이다. 따라서 다음과 같이 동일하게 호출할 수도 있다.

>>> kuku(7)
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63

 가끔 함수의 본체를 구현하지 않고 껍데기만 작성해야 될 경우도 있다. 이럴 경우 다음과 같이 하면 된다.

>>> def nop(): pass

이 함수는 호출은 할 수 있으나 아무런 일도 수행하지 않는다.