티스토리 뷰
수치해석 강의 중 굉장히 좋은 질문을 받았다.
왜 fprintf를 쓸 때에는 뒤에 세미콜론을 안 붙여도 되나요?
관찰력이 좋거나 호기심이 많은 사람이라면 한번쯤 가져봄직한 의문이다. 이 질문에 대한 힌트는 함수의 반환값 존재 여부와 반환값 무시 여부에 있다.
우선 세미콜론은 연산 결과를 출력하지 않을 때 사용한다.
>> e = exp(1)
e =
2.7183
>> e = exp(1);
>>
등호 =도 대입연산자라는 연산자이므로 대입연산의 결과가 출력된다. 그 결과를 보고 싶지 않다면, 즉 변수 e에 값을 대입만 하고 그 결과를 보고 싶지 않다면 세미콜론을 붙이면 된다.
반환값이 없는 함수를 호출하면 세미콜론 여부와 무관하게 아무것도 출력되지 않는다.
function fun(x)
x = x^2;
end
>> fun(1)
>>
그렇다면 fprintf는 세미콜론 여부와 무관하게 문자열 출력만 하니까 반환값이 없는 함수일까? 아니다.
>> fprintf('Hello\n') % case 1
Hello
>> n = fprintf('Hello\n') % case 2
Hello
n =
6
>> n = fprintf('Hello\n'); % case 3
Hello
>> n
n =
6
>>
위 코드의 case 1은 fprintf로 문자열 출력만을 한 경우이다. case 2는 뜬금없이 fprintf의 반환값을 받았다. 실행 결과 우선 fprintf가 할 일인 문자열 출력을 했고, 그 다음 n에 6이 저장됐음을 알리고 있다. fprintf의 반환값은 fprintf가 방금 출력한 문자열의 길이이다. 5가 아니고 6인 이유는 \n도 하나의 문자이기 때문이다. case 3에서는 뒤에 세미콜론을 붙였으므로 문자열 출력만 하고 n에 대입연산을 한 결과는 출력하지 않는다.
이제 자연스럽게 질문이 따라나와야 한다.
>> fprintf('Hello\n')
Hello
>>
왜 여기서는 fprintf의 반환값이 무시되는가? 그 이유는 정말로 반환값을 무시했기 때문이다.
매트랩의 함수 내부에서는 [함수 호출 시 반환값 몇 개를 요청했는지] 알 수 있는 방법이 있다. nargout은 함수 외부에서 사용하면 함수가 반환할 수 있는 최대 출력인자 개수를 알려주며, (number of arguments output 정도로 이해하면 된다.)
function [u, v, w] = fun(x)
u = x;
v = x.^2;
w = x.^3;
end
>> nargout(@fun)
ans =
3
>>
함수 내부에서 사용하면, 이 함수를 호출할 때 반환값 몇 개를 요청했는지를 알려준다.
function [u, v, w] = fun(x)
fprintf('%d output arguments required\n', nargout)
u = x;
v = x.^2;
w = x.^3;
end
>> u = fun(2);
1 output arguments required
>> [u, v] = fun(2);
2 output arguments required
>> [u, v, w] = fun(2);
3 output arguments required
>>
fprintf는 코드가 공개되어 있지 않으므로 정확한 구현은 알 수 없다. 허나 fprintf를 직접 구현한다면 아마 아래와 같은 모습일 것이다.
function nbytes = fprintf(s)
(문자열 출력)
if nargout == 1
nbytes = (출력한 문자열의 길이);
end
end
여기서 한 가지 궁금한 점이 또 생길 수 있다. fprintf를 반환값 없이 호출했다면 코드 내에서 nbytes가 아예 생성되지 않는데, 함수의 첫 줄에서 분명히 반환값 nbytes를 명시했으므로 에러가 발생하지 않을까?
정답: 발생하지 않는다. 아래 코드를 보자.
function [u, v, w] = fun(x)
fprintf('%d output arguments required\n', nargout)
u = x;
if nargout>1
v = x.^2;
end
if nargout>2
w = x.^3;
end
end
반환값 중 v와 w는 아예 요청하지 않으면 계산도 하지 않도록 해버렸다. 그래도 에러는 발생하지 않는다.
>> u = fun(2);
1 output arguments required
>> [u, v] = fun(2);
2 output arguments required
>> [u, v, w] = fun(2);
3 output arguments required
>>
요청하지 않은 반환값은 알아서 무시하기 때문이다.
사실 매트랩에서 그래프를 그리는 plot, subplot, fplot, surf 등 많은 함수들은 세미콜론 없이 사용한다. 하지만 반환값이 있다. 각 함수가 만들어내는 그래픽 객체의 핸들을 반환한다.
nargout의 한 가지 특이한 동작을 소개하며 본 글을 마치겠다. 우선 varargout에 대해 알아야 한다. 아래 코드를 보자.
function varargout = fun(x)
fprintf('%d output arguments required\n', nargout)
varargout = cell(1,nargout);
for i=1:nargout
varargout{i} = x.^i;
end
end
varargout은 함수 반환값의 개수를 가변적으로 두고 싶을 때 사용한다. variable-length arguments output 정도로 이해하면 된다. 위 코드를 보면 셀 배열 varargout를 만든 후 nargout개만큼 varargout에 값을 넣는 것을 볼 수 있다. 이 함수는 반환값의 개수가 정해져있지 않다. 1개를 요청하면 1개를, 2개를 요청하면 2개를, 10개를 요청하면 10개를 반환한다.
>> u = fun(2)
1 output arguments required
u =
2
>> [u, v] = fun(2)
2 output arguments required
u =
2
v =
4
>> [u, v, w, x, y, z] = fun(2)
6 output arguments required
u =
2
v =
4
w =
8
x =
16
y =
32
z =
64
셀 배열인 varargout에 저장된 값들은 함수 호출 시 요청한 반환값 변수들에 하나씩 할당된다. 이와 관련된 자세한 내용은 다른 글에서 확인해보자.
그렇다면 nargout은 이 함수의 반환값이 몇 개라고 알려줄까?
>> nargout(@fun)
ans =
-1
음수가 나왔다. 음수는 반환값 목록 중에 varargout이 있음을 뜻한다. 지금은 반환값이 varargout 하나 밖에 없으므로 -1이다. 만약 함수를 아래와 같이 만들었다면,
function [u, varargout] = fun(x)
fprintf('%d output arguments required\n', nargout)
u = x;
varargout = cell(1,nargout-1);
for i=1:nargout-1
varargout{i} = x.^(i+1);
end
end
함수의 반환값이 2개인데 varargout이 포함되어 있으므로,
>> nargout(@fun)
ans =
-2
-2가 나온다.
- 게으른
'matlab' 카테고리의 다른 글
홍길동 기자가 쓴 기사만 찾기 (0) | 2023.10.10 |
---|---|
K-최근접 이웃 알고리즘 소개 (0) | 2023.09.15 |
함수 핸들 이해하기 (0) | 2023.08.31 |
n차원 구의 부피는? (0) | 2023.08.07 |
숫자를 통화 표기로 바꾸기 (0) | 2023.08.02 |