matlab

fprintf에 세미콜론이 없어도 되는 이유

게으른 the lazy 2023. 9. 5. 16:20

 

 

수치해석 강의 중 굉장히 좋은 질문을 받았다.

 

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

 

반환값 중 vw는 아예 요청하지 않으면 계산도 하지 않도록 해버렸다. 그래도 에러는 발생하지 않는다.

 

>> 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가 나온다.

 

- 게으른