프로그래밍 스타일, 하나의 함수에 하나의 리턴!?

gurumong의 이미지

어느 책에서였던가...
하나의 함수에 하나의 리턴문만을 쓰는것이 좋은 프로그래밍 습관이라고 읽은적이 있는데요
꽤 오래전에 읽었는데 어디에서 읽었는지 모르겠네요

그런데 초보인 저로써는 이걸 지키기가 꽤나 힘듬니다
함수의 인자 몇개를 검사하고 처리결과 대신 오류를 리턴해야하는 일이 있을경우
하나의 리턴문을 만들기 위해 함수 전체를 몇개의 if-else로 감싸니
지독한 들여쓰기로 가독성이 상당히 떨어지게 되던데요

지금에서야 다시 생각해보면...
몇십라인이 되는 함수 곳곳에 리턴문이 있는것과(다른 코드들과 섞여서 리턴문이 잘 보이지 않게되는)
함수의 마지막 라인에 유일한 리턴문이 있는것과(함수전체에 걸치는 if-else로 인해 많은 들여쓰기가 된)
어느게 더 좋다고 할수있는지 잘 모르겠습니다

조언 부탁드립니다 (__)

cppig1995의 이미지

자신만의 통일된, 일관적인 coding style이 있다면 괜찮다고 생각합니다.
팀이더라도 이상적인 버전 관리 체계 같은 것을 사용한다면 commit 전에 indent 해주면 되구요.
전 return을 나눠서 씁니다. return문 1개를 쓰던 1000개를 쓰던, 그것은 자신의 자유이죠.



절망으로 코딩하고 희망으로 디버깅하자.

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

dude7853의 이미지

분명히 함수에 return이 많아지면 구멍도 많이 생기기 마련입니다.
중간에 return을 하다보면 close나 free를 빼먹는 일도 생길 확률이 높아지구요.

하지만 return을 쓰지 않을려고 if를 많이 쓰는 것은 반대입니다.
if를 줄일수 있는 적절한 return은 좋다고 생각합니다.

winner의 이미지

상황봐서 중복해서 써야 한다면 쓰지만 되도록 안 쓰자는 주의죠.
continue 는 if else 로 바꿔서 쓰기 때문에 거의 안 씁니다.

하지만 가독성이란 사람의 심리적인 면도 상당히 강하기 때문에 주관성이 강하죠.

몇십줄이라면 봐줄만 하겠지만 역시 긴 함수는 좀더 짧은 함수로 바꿔주는 것도 한 방법입니다.
가독성이 좋다면 한번만 호출되도 짧은 함수로 쪼개는 것을 생각해 볼만 합니다.
Test 에도 좋고요.

Code Complete 에서는 if else 의 조건식 순서에 정형화된 style 을 주어서 하기를 권장합니다.
이 style 에 맞춘다면 break, continue, return 이 쓰일 일이 많겠죠.

return 을 한번만 쓰자는 것이 반드시 옳다면 programming 언어 차원에서 아예 제한을 걸 수도 있을 겁니다.
그렇지 않다는 것은 이유가 있습니다.
하지만 이런 이야기가 나온 것도 나름의 이유가 있기에 고찰해보면 code 정리에 도움이 많이 됩니다.

blkstorm의 이미지

충분히 이해가 되는 고민거리입니다.

그런데, 여러사람이 같이 일하고, 재사용이 자주되는 코드에서는 결국 리턴은 한군데로 몰아주는 것이 맞습니다.

if문이 3단 이상으로 중첩되는 경우가 발생한다면, 매크로나 함수를 이용해서 가독성을 높여주면서 리턴을 하나로 몰아가는 것이 좋다고 생각합니다.

뭐, 함수 진입 부분에서의 간단한 범위 확인 같은것은 그자리에서 리턴해 주셔도 상관없습니다. (그러나 이것도, 경우와 위치를 한정하고 주석문을 앞뒤에 붙여주어야합니다.)

만약, return을 아무곳에나 넣도록 코딩 룰이 정해진다면, (주석문을 '아주' 충실히 달아준다고 할지라도) 후에 그 코드를 재사용하는 사람은 아주 골치아프게 됩니다. 심지어 본인조차도 몇달(제 나이면 며칠)만 지나도, 왜 그렇게 리턴이 여기저기 박혀있는지 아리까리해집니다.

svn이나 cvs히스토리를 잘 관리하고, 후에 재사용할 때도 그 히스토리가 그대로 유지된다면 좋겠지만, 실제로는 그렇지 않은 경우가 더 많습니다.

제가 본 최악의 경우는....

switch/case 문 안에, if가 들어가고, 그 안에서 return을 하는 것이었습니다. 이런 경우에는 다른 사람은 정말 한눈에 알아볼 수 없습니다.

거기다가, 만약 리턴이 조건문 아래줄에 있지 않고 오른편에 붙어있다면... 인민재판감이죠. ^^;;

충분히 공감하는 고민입니다. 하지만, 재사용과 가독성을 위해서는 (당장은 불편할지 몰라도) 리턴 위치는 하나, 또는 둘 정도로 한정하는 것이 좋습니다.

익명 사용자의 이미지

switch/case 문 안에, if가 들어가고, 그 안에서 return을 하는 것이었습니다. 이런 경우에는 다른 사람은 정말 한눈에 알아볼 수 없습니다.<< 이것을 왜 한눈에 못 알아 보시는지.. 익숙하지 않은 코드라 그런건가요
해당 케이스에서 나온 값을 리턴한다는 것인데 오히려 더 직관적일 수도 있습니다.

익명사용자의 이미지

그 함수를 어떻게 하면 더 짧고 보기 좋게 만들까 고민하면 해결될 문제네요.

리턴의 위치가 그리 중요할까요?

ㅡ,.ㅡ;;의 이미지

비정상, 에러 상황이 발생하면 바로 리턴하고

그렇지 않으면 보통 마지막에 리턴합니다.

----------------------------------------------------------------------------
C Library Development Project


----------------------------------------------------------------------------

feanor의 이미지

return의 개수에는 크게 개의치 않습니다.

한가지 궁금한 게 있는데, return을 한 곳에서만 한다면 어떻게 재귀 함수를 짜나요?

익명사용자31의 이미지

return (a == 0)? 0 : recursive();

feanor의 이미지

?:를 쓰느니 if로 return을 따로 하는 게 낫다고 생각합니다. (재귀의 종료 조건이 한 가지가 아닌 경우도 있습니다.)

익명사용자31의 이미지

(재귀의 종료 조건이 한 가지가 아닌 경우도 있습니다.)

=> 재귀랑 상관없이 첫 포스팅 글과 같은 뜻이라 생각됩니다.

저도 꼭 return을 한개 써야 한다고 생각하지는 않는데, 재귀문을 물어서 답한 것일뿐입니다.

magingax의 이미지

이런 고민을 하신다는건 기본적으로 함수를 너무 크게 만드시는겁니다
함수를 좀더 잘게 쪼개 시지요.
큰함수에 이런식으로 여러리턴이 생기면 상당히 복잡해집니다..

LISP 사용자모임
http://cafe.naver.com/lisper
방송기술 개발업체
http://playhouseinc.co.kr

superkkt의 이미지

함수가 커서 생기는 문제가 아니라고 생각합니다. 예를들어 아래와 같이 실패시에는 -1을 성공시에는 0을 리턴하는 함수의 경우도 결국엔 리턴이 여러개 생기는거죠.

    ...
    if (func())
        return -1;
    ...
    return 0;

만약 함수에서 빠져나가기 전에 해제해야할 자원이 있다면 아래와 같이 바꿔서 리턴을 한곳으로 모을 수 있겠죠.

    ...
    ret_val = -1;
    if (func())
        goto cleanup;
    ...
    ret_val = 0;
 
cleanup:
    ...
    return ret_val;

======================
BLOG : http://superkkt.com

======================
BLOG : http://superkkt.com

gurumong의 이미지

goto문을 그렇게 사용하니 유용하네요 !!

전웅의 이미지

> 어느 책에서였던가...
> 하나의 함수에 하나의 리턴문만을 쓰는것이 좋은 프로그래밍 습관이라고 읽은적이 있는데요
> 꽤 오래전에 읽었는데 어디에서 읽었는지 모르겠네요
>

제 책에도 동일한 내용이 있는 것으로 기억합니다.

> 그런데 초보인 저로써는 이걸 지키기가 꽤나 힘듬니다
> 함수의 인자 몇개를 검사하고 처리결과 대신 오류를 리턴해야하는 일이 있을경우
> 하나의 리턴문을 만들기 위해 함수 전체를 몇개의 if-else로 감싸니
> 지독한 들여쓰기로 가독성이 상당히 떨어지게 되던데요
>
> 지금에서야 다시 생각해보면...
> 몇십라인이 되는 함수 곳곳에 리턴문이 있는것과(다른 코드들과 섞여서 리턴문이 잘 보이지 않게되는)
> 함수의 마지막 라인에 유일한 리턴문이 있는것과(함수전체에 걸치는 if-else로 인해 많은 들여쓰기가 된)
> 어느게 더 좋다고 할수있는지 잘 모르겠습니다
>

"one entry point and one exit point" 원칙은 말 그대로 코딩 스타일의
문제입니다. "들여쓰기는 몇 칸을 해라" 같은 스타일보단 강한 성격을
갖지만, "함수 하나의 길이는 몇줄로 제한해라"와 같은 스타일과는 크게
다르지 않다고 볼 수 있습니다.

"코딩 스타일"이란 손해나 불이익을 감수하고 논리적 모순에 빠지면서
까지 지켜야 하는 규칙이라기 보다는 그 스타일이 전달하고자 하는
"개념"을 지키겠다는 생각으로 접근하면 충분하다고 봅니다.

"one entry point and one exit point" 의 스타일이 말하고자 하는 바는
사용한 자원에 대한 뒷정리가 필요한 경우에는 이 스타일을 지킴으로써
혹은 지키려 노력함으로써 처음부터 memory leak 같은 자원 관리에
생기는 문제를 피해갈 수 있다는 것입니다 (실은 structured programming
에서 단일 repeatation 구문에 적용되는 원칙입니다만, 실용적인 관점에서
저 스타일을 통해 얻을 수 있는 바를 설명했습니다). 그와 같은 코딩
스타일이 "품는" 개념은 보지 않고 코딩 스타일이 "표면적으로 말하는"
것만을 지키려할 경우 오히려 모순되는 상황에 빠질 가능성이 큽니다.

예를 들어, 말씀하신 것과 같은 상황에서 "if 문 중첩의 깊이는 몇개 이상
으로 해서는 안 된다" 같은 스타일과 문제의 스타일이 상충된다면 어떻게
하시겠습니까? 혹은 위에서 언급한 것처럼 "함수의 길이를 몇 줄로 제한
해라" 라는 스타일과 "하나의 함수에는 하나의 완전한 논리적, 기능적
단위 만을 담아라" 라는 스타일이 상충된다면 어떻게 하시겠습니까?

스타일은 경우에 따라 "합리적으로" 무시할 수 있기에 스타일입니다. 예를
들어, 어떤 분은 "one entry point and one exit point" 원칙이 부적당
하다고 판단해 "two exit points" (하나는 함수가 성공한 경우, 다른 하나
는 실패한 경우) 를 나름의 기준으로 세워 코딩을 하기도 합니다.

긴 시간 동안 여러 개발자들을 통해 전해온 스타일은 대부분의 경우 그
가치를 가지고 있습니다. 만약, 지금 당장 그와 같은 스타일이 도움은
커녕 방해만 된다고 판단하신다면 무시하셔도 괜찮습니다. 어차피 긴 시간
개발을 진행하다보면 누가 지키지말라고 해도 방어적으로 그와 같은
스타일을 지켜 코딩하는 자신의 모습을 발견하실 수 있을 것입니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

익명 사용자의 이미지

뭐 스타일로 봐서는 return몰아줄려면
중첩된 if문이나 loop문에서 빠져나오기가 힘들껍니다.
그러한 경우에만 goto문을 써서 return을 한곳으로
처리하는 것은 좋은 습관이 될겁니다.

lacovnk의 이미지

한 함수의 return이 여러곳에서 수행되어서, 그 함수를 "사용"하는데 곤란한 점이 있나요? return하는 경우에 따라 뭐가 달라진다.. 이런 것은 함수의 내부를 들여다 보기 이전에 잘 정의해서 문서화를 해주고, "사용"만 해야지, 안을 들여다볼 필요는 없다고 생각하거든요.

문제가 되는 것은 그 함수를 들여다보고 수정해야 하는 경우 같은데..

그 함수를 수정하려고 하는데 return이 너무 많은 곳에서 이루어져서 곤란하다면, 그건 함수를 기능별로 더 쪼개야 한다는 뜻이 되지 않을까요? 또는 예외 처리를 활용해서,try-catch 부분에서만 return하게 만든다던가, 아니면 위 처럼 goto 문을 활용한다던가 해서 일관적으로 작성하면 무난할 것이라고 생각해봅니다~

예를 들어 특정 알고리즘을 구현할 경우, 알고리즘을 이해하면 중간에 이런식으로 리턴하는 걸 이해하겠죠. 코드의 내용 (알고리즘)을 알지 못하고 "리턴이 왜 여깄지? 의미가 뭐지?"라고 궁금해 하지 않도록 뒤로 잘 몰아준다고 해서 그 코드를 잘 수정할 수 있게 되는 건 아니죠.. 그 코드를 단지 이용하는 것이면 함수로 이용하면 되는 것이고요~

kalstein의 이미지

예를 들어...

if (0 > func())
  return -1;
else ...
...

이런경우는 오히려 더 깔끔하지않나요? 에러나자마자 바로 함수가 종료되니까요.
다만 조심해야되는것은...자원의 관리 인데...C에서는 return전에 자원해제되어야되는것이 많을 경우
정말 구질구질해지거나...아님 goto문을 사용하는게 깔끔하겠지요.

C++이라면...스마트포인터를 이용해서 깔끔하게 해결되지만요 ^^ (C++을 좋아하는 이유중 하나입니다 ㅎㅎ)


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.