Ruby로 명시 적으로 리턴하는 것이 좋은 스타일입니까?


155

스타일에 관해서는 항상 "올바른 방법"( "Pythonic"방식)이있는 파이썬 배경에서 나온 루비도 마찬가지입니다. 나는 나 자신의 스타일 지침을 사용하고 있지만 소스 코드를 공개하려고 생각하고 있으며 작성되지 않은 규칙을 준수하고 싶습니다.

return메소드 에 명시 적으로 입력하는 것이 "루비 방식" 입니까? 나는 그것이 있거나없는 것을 보았지만 그것을하는 올바른 방법이 있습니까? 그것을 할 적절한 시간 이 있습니까? 예를 들면 다음과 같습니다.

def some_func(arg1, arg2, etc)
  # Do some stuff...
  return value # <-- Is the 'return' needed here?
end

3
이것은 매우 오래된 질문이지만 비슷한 질문이있는 사람들에게는 Eloquent Ruby 책을 적극 권장합니다. 관용적 인 루비 작성에 대한 소개만큼이나 스타일 가이드는 아닙니다. 더 많은 사람들이 읽을 수 있기를 바랍니다!
Brian 친애하는

Ruby에서 개발 된 대형 레거시 앱을 상속받은 사람으로서이 질문을 해주셔서 감사합니다. 이것은 공감할 가치가있다. 레거시 앱 전체에 흩어져 있습니다. 이 팀에서 저의 일 중 하나는 코드베이스를 개선하는 것입니다 (Ruby를 처음 사용하는 경우 어려움).
Stevers

답변:


226

오래된 (그리고 "답변 된") 질문이지만 대답으로 2 센트를 던질 것입니다.

TL; DR-반드시 그럴 필요는 없지만 코드를 좀 더 명확하게 만들 수 있습니다.

명시적인 리턴을 사용하지 않는 것이 "루비 방식"일 수 있지만, 익숙하지 않은 코드를 사용하거나이 루비의 기능에 익숙하지 않은 프로그래머에게는 혼란 스러울 수 있습니다.

다소 고안된 예이지만, 전달 된 숫자에 1을 더하고이를 인스턴스 변수에 할당하는 이와 같은 작은 기능이 있다고 상상해보십시오.

def plus_one_to_y(x)
    @y = x + 1
end

이것은 값을 반환하는 함수입니까? 인스턴스 변수를 할당하고 할당 된 값을 반환하기 때문에 개발자가 의미하는 바를 말하기가 실제로 어렵습니다.

훨씬 나중에 다른 프로그래머 (아마도 루비가 마지막으로 실행 된 코드 라인을 기반으로 반환하는 방법에 익숙하지 않은)가 와서 로깅을 위해 인쇄 문을 넣고 싶다고 가정하면 함수는 다음과 같습니다.

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
end

무엇이든 반환 값을 기대하면 함수가 중단 됩니다 . 아무것도 반환 값을 기대하지 않으면 괜찮습니다. 분명히 코드 체인 아래로 이동하면 무언가를 반환하는 것이 반환 된 값을 기대하고 있으며 예상대로 돌아 가지 않으므로 실패합니다.

실제 질문은 이것입니다. 실제로 반환 값을 기대하는 것이 있습니까? 이 문제가 발생 했습니까? 앞으로 무언가가 깨질까요? 누가 알아! 모든 통화에 대한 전체 코드 검토 만 알려줍니다.

따라서 적어도 모범 사례 접근 방식은 중요한 경우 무언가를 반환한다는 것을 명시 적으로 나타내거나 그렇지 않을 때는 아무것도 반환하지 않는 것입니다.

작은 데모 함수의 경우 값을 반환하기를 원한다고 가정하면 다음과 같이 작성됩니다.

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    return @y
end

그리고 그것은 어떤 프로그래머에게나 그것이 가치를 반환한다는 것을 분명히 알 수 있으며, 그것을 깨닫지 않고 깨뜨리기가 훨씬 더 어렵습니다.

또는 다음과 같이 작성하고 return 문을 생략 할 수 있습니다 ...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    @y
end

그런데 왜 귀환이라는 단어를 남기지 않겠습니까? 왜 거기에 넣고 무슨 일이 일어나고 있는지 100 % 명확하게 만드십시오. 말 그대로 코드의 성능에 영향을 미치지 않습니다.


6
흥미로운 점. 필자는 원래 개발자가 코드에 암시 적 반환 값을 사용하는이 질문을했을 때 실제로 비슷한 상황에 처했다고 생각하며 진행 상황을 이해하기가 매우 어려웠습니다. 답변 주셔서 감사합니다!
사샤 체디 고프

6
나는 이것에 의해 화상을 입었을 때 암묵적 인 반환에 대한 인터넷 검색을하는 동안 질문을 발견했습니다. 암묵적으로 무언가를 반환하는 함수 끝에 논리를 추가했습니다. 그것에 대한 대부분의 호출은 반환 된 값을 신경 쓰지 않거나 확인하지 않았지만 하나는 실패했습니다. 다행히도 적어도 일부 기능 테스트에서 나타났습니다.
팀 홀트

16
사실이지만, 코드를 읽을 수있는 것이 중요하며, 장황한 표현을 선호하는 프로그래밍 언어의 잘 알려진 기능은 내 생각에 나쁜 습관입니다. 개발자가 Ruby에 익숙하지 않은 경우 코드를 만지기 전에 익숙해야합니다. Ruby가 아닌 개발자가 나중에 무언가를 해야하는 미래의 사건을 대비하여 코드의 혼란을 증가시켜야하는 것은 약간 어리석은 일입니다. 언어를 최저 공통 분모로 축소해서는 안됩니다. Ruby의 장점 중 일부는 3 줄만 사용하면 5 줄의 코드를 사용할 필요가 없다는 것입니다.
Brian Dear

25
단어 return가 의미 론적 의미를 코드에 추가 한다면 어떨까요? 거의 장황하지 않습니다. 우리는 5 줄 대 3을 말하지 않고 한 단어 만 더 말하고 있습니다. return코드가 실제로하는 일을 표현하기 위해 적어도 함수 (블록에는 아닐 수도 있음) 를보고 싶습니다 . 때로는 중간 함수를 반환해야 return합니다. 마지막 줄에 마지막 키워드를 남기지 마십시오 . 나는 자세한 표현을 선호하지 않지만 일관성을 선호합니다.
mindplay.dk

6
이것은 좋은 대답입니다. 그러나 여전히 프로 시저 (일명 함수 반환 단위)가되는 Ruby 메서드를 작성하는 위험이 남아 side_effect()있으며 다른 개발자가 프로 시저의 암시 적 반환 값을 사용하기로 결정합니다. 이 문제를 해결하기 위해 절차를 명시 적으로 만들 수 있습니다 return nil.
Alex Dean

66

아닙니다. 좋은 Ruby 스타일은 일반적으로 초기 수익에 대해 명시 적 수익 만 사용합니다 . 루비는 코드 미니멀리즘 / 암시 적 마술에 큰 영향을 미칩니다.

즉, 명시적인 반품으로 물건을 더 명확하거나 읽기 쉽게 만들면 아무 것도 해치지 않습니다.


116
혼동 극대화를 초래하는 코드 미니멀리즘의 요점은 무엇입니까?
jononomo

@JonCrowell 대신 문서 최대화가 이루어져야합니다.
재즈 피

29
@jazzpi 미니멀리즘을 연습하지 않는다는 것을 잘 이해하기 위해 코드를 많이 문서화해야하는 경우 코드 골프를 연습하는 것 입니다.
Schwern

2
최종 수익을 배제하는 것은 혼란스럽지 않으며, 그것을 포함하는 것은 혼란 스럽습니다. 루비 개발자로서 return이미 기능에 대한 초기 종료와 같은 의미 론적 의미가 있습니다.
데본 파슨

14
@DevonParsons return어쨌든 누군가가 어째서 마지막 출구에서 마지막 줄을 혼동 할 수 있습니까?
DarthPaghius

31

필자는 개인적으로 return키워드를 사용하여 기능적 메소드 , 즉 주로 반환 값 으로 실행되는 메소드 와 주로 부작용으로 실행되는 절차 적 메소드 를 구별 합니다. 따라서 반환 값이 중요한 메서드는 반환 값에 return주의를 끌기 위해 추가 키워드를 가져 옵니다.

함수를 호출 할 때 동일한 구별을 사용합니다 . 함수형 메소드는 괄호를 얻지 만 절차 적 메소드는 그렇지 않습니다.

마지막으로 블록과의 구별도 사용합니다. 기능 블록은 중괄호, 절차 블록 (즉, "일을하는"블록)은 do/를 얻습니다 end.

그러나 블록, 중괄호 및 do/ end가 다른 우선 순위를 사용하여 표현을 명확하게하기 위해 명시적인 괄호를 추가하는 대신 다른 스타일로 전환합니다. 매개 변수 목록 주위에 괄호를 추가하면 코드를 더 읽기 쉽게 만들면 문제의 방법이 절차 적 인 경우에도 수행합니다.


1
지금 나는 당신이하고있는 것과 비슷한 "한 줄짜리 (one-liners)"에서 반환을 생략하고, 다른 모든 것을 똑같이합니다. 나는 내 스타일에 완전히 빠져 있지 않다는 것을 알고 있습니다. :)
Sasha Chedygov

@ 조그 : 코드에서 초기 반품을 사용합니까? 이것이 절차 / 기능 체계와 혼동을 유발합니까?
Andrew Grimm 3

1
@Andrew Grimm : 그렇습니다. 나는 그것을 뒤집을 생각입니다. 내 메소드의 대부분은 참조 적으로 투명하고 순수하고 기능적이며 어쨌든 호출하고자하는 모든 것이므로 더 짧은 형식을 사용하는 것이 좋습니다. 그리고 기능적 스타일로 작성된 메소드는 초기 단계로 돌아 가지 않는 경향이 있습니다. 왜냐하면 "단계"라는 개념을 의미하기 때문입니다. 비록 중간에 반품하는 것을 좋아하지는 않습니다. 처음에는 제어 흐름을 단순화하기 위해 경비원으로 사용하거나 마지막에 사용합니다.
Jörg W Mittag

훌륭한 답변이지만 실제로 () 및 함수형 메소드를 사용하지 않고 절차 적 메소드를 호출하는 것이 더 좋습니다. 이유에 대해서는 아래 답변을 참조하십시오.
Alex Dean

13

실제로 중요한 것은 다음을 구별하는 것입니다.

  1. 함수-반환 값에 대해 실행 된 메소드
  2. 절차-부작용에 대해 실행되는 방법

루비는 이것들을 구별하는 기본 방법을 가지고 있지 않으므로 프로 시저를 작성하는 데 취약 side_effect()하고 다른 개발자 는 프로 시저 의 암시 적 반환 값을 남용하기로 결정합니다 (기본적으로 불순 함수로 처리).

이 문제를 해결하려면 스칼라와 하스켈의 책에서 잎사귀를 꺼내어 절차를 명시 적으로 반환하십시오 nil(일명 Unit또는 ()다른 언어로).

당신이 이것을 따른다면, 명시적인 return문법 을 사용 하거나 개인적인 스타일의 문제가되지는 않습니다.

기능과 절차를 더 구분하려면 다음을 수행하십시오.

  1. Jörg W Mittag의 중괄호로 기능 블록을 작성하고 절차 블록으로 do/end
  2. 프로 시저를 호출 할 때 () 하고 함수를 호출 할 때는

피 - 요 르그 W MITTAG 실제로 다른 방법으로 주위 옹호주의 ()의 절차를 -하지만 당신은 측면 초래 메소드 호출이 인수에 대응이 0으로 표시되는 특히 때, 변수에서 명확하게 구별되고 싶어 때문이 바람직하지의 메소드 호출에 스칼라 스타일 가이드 에 대한 세부.


내가로부터 반환 값이 필요한 경우 function_a, 그리고 function_a전화를 작성되었습니다 (및 호출에 의해 동작 만 가능) procedure_b, 다음입니다 function_a정말 기능? 함수에 대한 요구 사항을 충족시키는 값을 반환하지만 부작용이있어 절차 요구 사항을 충족시킵니다.
데본 파슨스

2
당신은 맞습니다 Devon- 실제로 호출 트리를 포함 할 수있는 것처럼 호출 function_a트리를 포함 할 수 있습니다 . 스칼라처럼 하스켈과는 달리 루비에서는 함수가 부작용이 아니라고 보장 할 수 없습니다. procedure_...procedure_afunction_...
Alex Dean

8

스타일 가이드에 따르면 return마지막 문장에 사용해서는 안됩니다 . 마지막이 아닌 경우 에도 계속 사용할 수 있습니다 . 이것은 커뮤니티가 엄격하게 준수하는 규칙 중 하나이므로 Ruby를 사용하는 사람과 협력 할 계획이라면 반드시 사용해야합니다.


그러나 명시 적 returns 를 사용하는 주된 주장 은 다른 언어를 사용하는 사람들에게는 혼동된다는 것입니다.

  • 첫째, 이것은 전적으로 루비 전용이 아닙니다. 예를 들어 Perl은 암시 적 리턴도 있습니다.
  • 둘째, 이것이 적용되는 대다수의 사람들이 Algol 언어에서 온 것입니다. 이것들 대부분은 루비보다 "낮은 수준" 이기 때문에 무언가를하려면 더 많은 코드를 작성해야합니다.

자바에서 메소드 길이 (게터 / 세터 제외)에 대한 일반적인 휴리스틱은 한 화면입니다. 입니다. 이 경우 메소드 정의를 보지 못했거나 이미 돌아온 위치를 잊어 버렸을 수 있습니다.

반면 루비에서는 길이가 10 줄 미만인 메소드를 사용하는 것이 가장 좋습니다 . 이를 감안할 때, 명확하게 암시 될 때 왜 ~ 10 % 더 많은 문장을 써야하는지 궁금 할 것입니다.


루비에는 void 메소드 가 없으며 모든 것이 훨씬 간결하기 때문에 명시 적으로 사용하면 이점에 오버 헤드를 추가하는 것입니다 return.


1
"이것은 커뮤니티가 엄격하게 준수하는 규칙 중 하나입니다." 제 경험은 아니요, 경험이 많은 루비 개발자가 아니라면 사람들은 엄격히 따르지 않습니다.
Tim Holt

1
@TimHolt 나는 그때 매우 운이 좋았을 것입니다. (:
ndnenkov

1
@ndn은 완전히 동의합니다. 중요한 것은 메소드 (또는 클래스)가 5/100 라인을 초과하기 시작할 때 달성하려는 것을 재고하는 것이 좋습니다. 실제로 Sandi의 규칙은 임의의 교리가 아닌 코드에 대해 생각하기위한인지 프레임 워크입니다. 내가 직면 한 대부분의 코드는 인위적으로 구속되어 있기 때문에 문제가되지 않습니다. 여러 책임을 가진 클래스, 많은 클래스보다 긴 메소드. 본질적으로 너무 많은 사람들이 "상어를 뛰어 넘다"-책임을 섞는다.
Brian 친애하는

1
때때로 컨벤션은 여전히 ​​나쁜 생각입니다. 마술에 의존하는 스타일 규칙은 반환 유형을 추측하기 어렵게 만듭니다. 7 개의 키 입력을 저장하는 것 외에는 이점이 없으며 거의 ​​모든 다른 언어와 일치하지 않습니다. 루비 이제까지 파이썬 3 스타일의 통합 및 단순화 패스를 않는 경우, 나는 암시 희망 아무것도 도마에 처음이다.
Shayne

2
분명히 그것을 제외하고는 실제로 물건을 더 쉽게 읽을 수는 없습니다! 마술과 이해력은 단순한 것이 아닙니다.
Shayne

4

나는 벤 휴즈 (Ben Hughes)에 동의하고 팀 홀트 (Tim Holt)에 동의하지 않는다.이 질문은 파이썬이 결정적인 방식을 언급하고 루비가 비슷한 표준을 가지고 있는지 묻기 때문이다.

그렇습니다.

이것은 루비에서 문제를 디버깅 할 것으로 예상되는 사람이 그 언어에 대해 합리적으로 알아야 할 언어의 잘 알려진 기능입니다.


3
이론적으로는 동의하지만 실제로 프로그래머는 실수를하고 혼란을 겪습니다. 예를 들어, "="가 아닌 조건부에서 "=="를 사용한다는 것을 모두 알고 있지만, 우리는 모두 전에 실수를 저지르고 나중에는이를 깨닫지 못했습니다.
Tim Holt

1
@TimHolt 특정 사용 사례를 다루지 않고 있으며 질문에 대답하고 있습니다. 커뮤니티에서 선택한대로 return키워드를 불필요하게 사용하는 것은 명시 적으로 잘못된 스타일 입니다.
데본 파슨스

3
그럴 수 있지. 당신이 언급하지 않는 스타일은 조심하지 않으면 실수로 이어질 수 있습니다. 내 경험을 바탕으로 개인적으로 다른 스타일을 옹호합니다.
Tim Holt

1
@TimHolt 그건 그렇고,이 질문은 스타일 가이드가 작성되기 전에 요청되었으므로 동의하지 않는 대답은 반드시 오래된 것이 아닙니다. 나는 어떻게 든 여기에서 우연히 만났고 내가 제공 한 링크를 보지 못했기 때문에 다른 누군가도 여기에 올 수 있다고 생각했다.
데본 파슨스

1
공식적인 "표준"이 아닙니다. rubocop에서 사용하고 Devon Parsons의 주장과 관련이있는 루비 스타일 가이드는 비 코어 루비 해커가 만듭니다. 따라서 Devon의 의견은 사실 간단하지 않습니다. 물론 여전히 사용할 수 있지만 표준은 아닙니다.
shevy

1

어떤 스타일을 가장 선호 하느냐가 중요합니다. 메소드 중간의 어딘가에서 리턴하려면 키워드 return을 사용해야합니다.


0

벤이 말했듯이 '루비 메소드의 리턴 값이 함수 본문에서 마지막 명령문의 리턴 값'이라는 사실 때문에 리턴 키워드가 대부분의 루비 메소드에서 거의 사용되지 않습니다.

def some_func_which_returns_a_list( x, y, z)
  return nil if failed_some_early_check


  # function code 

  @list     # returns the list
end

4
당신은 또한 당신의 의도가 여전히 명확하다면 "실패한 경우 반환 nil_some_early_check"라고 쓸 수 있습니다
Mike Woodhouse

@Gishu 나는 실제로 메소드 이름이 아니라 if 문에 대해 불평했다.
Andrew Grimm

@Andrew .. 아, 누락 된 끝 :). 알았다. Mike의 다른 의견도 진정하도록 수정되었습니다.
Gishu

왜 안돼 if failed_check then nil else @list end? 정말 기능적 입니다.
cdunn2001

@ cdunn2001 왜 다른 기능이 작동하는지 명확하지 않습니다.이 스타일의 이유는 초기 종료를 통해 중첩이 줄어들 기 때문입니다. IMHO도 더 읽기 쉽습니다.
Gishu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.