루비에 "do… while"루프가 있습니까?


452

이 코드를 사용하여 사용자가 이름을 입력 할 수있게하고 프로그램은 빈 문자열을 입력 할 때까지 배열에 저장합니다 (각 이름 다음에 Enter 키를 눌러야 함).

people = []
info = 'a' # must fill variable with something, otherwise loop won't execute

while not info.empty?
    info = gets.chomp
    people += [Person.new(info)] if not info.empty?
end

이 코드는 do ... while 루프에서 훨씬 멋지게 보입니다.

people = []

do
    info = gets.chomp
    people += [Person.new(info)] if not info.empty?
while not info.empty?

이 코드에서는 임의의 문자열에 정보를 할당 할 필요가 없습니다.

불행히도이 유형의 루프는 Ruby에 존재하지 않는 것 같습니다. 아무도 이것을하는 더 좋은 방법을 제안 할 수 있습니까?


1
나는 정상적인 while 루프가 더 좋아 보이고 읽기 쉽다고 생각합니다.
— Magne

1
@Jeremy Ruten Siwei Shen의 답변으로 허용되는 답변을 변경하는 데 관심이 있으 loop do; ...; break if ...; end십니까?
— David Winiecki

답변:


643

주의 :

은 begin <code> end while <condition>루비의 저자 마츠에 의해 거부됩니다. 대신 그는 사용 제안 Kernel#loop예를 들어,

loop do 
  # some code here
  break if <condition>
end 

다음 은 Matz가 말하는 2005 년 11 월 23 일 의 이메일 교환 입니다.

|> Don't use it please.  I'm regretting this feature, and I'd like to
|> remove it in the future if it's possible.
|
|I'm surprised.  What do you regret about it?

Because it's hard for users to tell

  begin <code> end while <cond>

works differently from

  <code> while <cond>

RosettaCode 위키 는 비슷한 이야기를합니다 :

2005 년 11 월 Ruby 제작자 인 Matsumoto Yukihiro는이 루프 기능을 후회하고 Kernel # loop 사용을 제안했습니다.


18
에 딱 맞다. 이 begin end while방법이 옳지 않은 것 같습니다. 나머지 팀을 설득하는 데 필요한 사료를 제공해 주셔서 감사합니다.
— Joshua Pinter

2
루프가 시작되기 전에 루프 시작이 실제로 조건을 평가하는 것처럼 보입니다. 이것과 일반적인 while 루프의 차이점은 적어도 한 번 실행된다는 것입니다. 문제를 일으키는 동안 할 수있을 정도로 가깝습니다.
— user1992284

4
따라서 내가 잘 이해 한 한, 수정 자의 의미를 위반하기 때문에 "종료 후"가 시작됩니다. 즉, 블록을 실행하기 전에 확인합니다. 예를 들어 puts k if! k.nil ?. 여기서 'if'는 'modifier'입니다 .'puts k '문 실행 여부를 결정하기 전에 확인됩니다. (start-end-block의 modifier로 사용될 때 while / until 루프의 경우는 아닙니다. !), 첫 번째 실행 후 평가됩니다. 아마도 이것이 후회를 일으켰지 만 이전 포럼 게시물보다 '강력한'것이 있습니까? 많은 다른 경우에 사용되는 것과 같은 공식적인 지원 중단이 있습니까?
— AgostinoX

2
이 루비 'do-while'루프가 왜 작동하지 않는지 알아내는 데 시간이 많이 걸렸습니다. P : 당신은 상태를 반전 할-동안 더 밀접하게 모방의 C-스타일 '않는', 그렇지 않으면 당신은 나처럼 결국 잊지 수 사용해야합니다
— 코너 클라크

1
@ 제임스 (James) 링크 된 메일에 따르면, 그는 "추가해서"후회 "하고 있다고 말했다. 언어 디자이너라도 실수를 저지르는 경우가 있습니다.
— Xiong Chiamiov 19 :

188

Tempfile#initializeRuby 코어 라이브러리에서 소스를 읽는 동안 다음 스 니펫을 발견했습니다 .

begin
  tmpname = File.join(tmpdir, make_tmpname(basename, n))
  lock = tmpname + '.lock'
  n += 1
end while @@cleanlist.include?(tmpname) or
  File.exist?(lock) or File.exist?(tmpname)

언뜻보기에는 while 수정자가 begin ... end의 내용보다 먼저 평가 될 것이라고 가정했지만 실제로는 그렇지 않습니다. 관찰 :

>> begin
?>   puts "do {} while ()" 
>> end while false
do {} while ()
=> nil

예상 한대로 수정자는 true 인 동안 루프는 계속 실행됩니다.

>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

이 관용구를 다시는 볼 수 없어서 기쁘 겠지만 시작 ... 끝은 아주 강력합니다. 다음은 매개 변수없이 단일 라이너 방법을 기억하는 일반적인 관용구입니다.

def expensive
  @expensive ||= 2 + 2
end

더 복잡한 것을 기억하는 추악하지만 빠른 방법은 다음과 같습니다.

def expensive
  @expensive ||=
    begin
      n = 99
      buf = "" 
      begin
        buf << "#{n} bottles of beer on the wall\n" 
        # ...
        n -= 1
      end while n > 0
      buf << "no more bottles of beer" 
    end
end

Jeremy Voorhis가 처음 썼습니다 . 원래 사이트에서 삭제 된 것으로 보이므로 여기에 내용이 복사되었습니다. 사본은 웹 아카이브 및 Ruby Buzz 포럼 에서도 찾을 수 있습니다 . -도마뱀 빌



56
그렇기 때문에 외부 사이트에 연결할 때 항상 여기에 관련 정보를 내 답변에 복사해야합니다.
— davr

10
begin ... end의 내용 전에 while 수정자를 평가해야하는 이유는 무엇입니까? 루프가 작동하는 것으로 가정합니다. 그리고 왜 "이 관용구를 다시는 보지 않겠습니까?" 무슨 일이야? 혼란 스러워요.
— bergie3000

2
begin ... end는 {...}와 마찬가지로 블록처럼 보입니다. 아무 문제가 없습니다.
— Victor Pudeyev 2016 년

1
-1 : Siwei Shen의 대답 begin .. end은 다소 찡그린 설명입니다 . loop do .. break if <condition>대신 사용하십시오 .
— Kevin

102

이처럼 :

people = []

begin
  info = gets.chomp
  people += [Person.new(info)] if not info.empty?
end while not info.empty?

참조 : Ruby 's Hidden은 {} while () 루프를 수행합니다.


1
응, 맞았다 제길.
— Blorgbeard는

이 코드는 입력이 없으면 배열에 빈 문자열을 추가하지 않습니까?
— AndrewR

1
여기서 적용되지는 않지만 begin-end-while 구성의 한 가지 문제점은 다른 모든 루비 구성과 달리 마지막 표현식의 값을 반환하지 않는다는 것입니다. "begin 1 end while false"는 nil (1이 아님, not false)
— tokland

9
until info.empty?대신 사용할 수 있습니다 while not info.empty?.
— Andrew Grimm

실제로 @AndrewR은 일종의 요점입니다. 비교하기 전에 할 일 .. diplay ( "remaining = # {count}) while (count> 0) ..."remaining = 0 "의 표시가 나타납니다. 완벽한!
— baash05

45

이건 어때요?

people = []

until (info = gets.chomp).empty?
  people += [Person.new(info)]
end

4
멋지고 우아합니다.
— Blorgbeard는 09:08

23
그러나 이것은 "do ... while"루프가 아닙니다. :)
— Alexander Prokofyev

4
그러나 내가 실수하지 않는 한,이 경우에도 같은 일을합니다
— Blorgbeard는

18
@Blorgbeard, do..while 루프는 항상 한 번 실행 된 다음 계속 실행해야하는지 평가합니다. 전통적인 while / until 루프는 0 번 실행될 수 있습니다. 큰 차이는 아니지만 서로 다릅니다.
— Scott Swezey 2019 년

5
@Scott, 그건 사실입니다-방금 수행 / 사용하지 않더라도이 코드가 OP와 동일하다는 것을 의미했습니다. 실제로이 코드는 조건에서 루프의 "작업"의 절반을 수행하므로 조건부와 일치하지 않으면 일부 작업이 여전히 수행되는 동안 전통적인 루프는 아닙니다.
— Blorgbeard는

11

다음은 hubbardr의 데드 링크 내 블로그에 대한 전체 텍스트 기사입니다.

Tempfile#initializeRuby 코어 라이브러리에서 소스를 읽는 동안 다음 스 니펫을 발견했습니다 .

begin
  tmpname = File.join(tmpdir, make_tmpname(basename, n))
  lock = tmpname + '.lock'
  n += 1
end while @@cleanlist.include?(tmpname) or
  File.exist?(lock) or File.exist?(tmpname)

언뜻보기에 while수정자는의 내용보다 먼저 평가 될 것이라고 가정 begin...end했지만 그렇지 않습니다. 관찰 :

>> begin
?>   puts "do {} while ()" 
>> end while false
do {} while ()
=> nil

예상 한대로 수정자는 true 인 동안 루프는 계속 실행됩니다.

>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

나는이 관용구를 다시 begin...end는 볼 수 없을 것이지만 매우 강력합니다. 다음은 매개 변수없이 단일 라이너 방법을 기억하는 일반적인 관용구입니다.

def expensive
  @expensive ||= 2 + 2
end

더 복잡한 것을 기억하는 추악하지만 빠른 방법은 다음과 같습니다.

def expensive
  @expensive ||=
    begin
      n = 99
      buf = "" 
      begin
        buf << "#{n} bottles of beer on the wall\n" 
        # ...
        n -= 1
      end while n > 0
      buf << "no more bottles of beer" 
    end
end


6

내가 수집 한 것에서 Matz는 구조물을 좋아하지 않습니다.

begin
    <multiple_lines_of_code>
end while <cond>

의미론이 다르기 때문에

<single_line_of_code> while <cond>

첫 번째 구문은 조건을 확인하기 전에 먼저 코드를 실행하고 두 번째 구문은 코드를 실행하기 전에 조건을 먼저 테스트합니다 (있는 경우). Matz는 if 문의 한 줄 구문과 일치하기 때문에 두 번째 구문을 유지하는 것을 선호합니다.

if 문에 대해서도 두 번째 구문을 좋아하지 않았습니다. 다른 모든 경우에 컴퓨터는 왼쪽에서 오른쪽으로 코드를 실행합니다 (예 : || 및 &&). 인간은 왼쪽에서 오른쪽으로 위에서 아래로 코드를 읽습니다.

대신 다음과 같은 구성을 제안합니다.

if <cond> then <one_line_code>      # matches case-when-then statement

while <cond> then <one_line_code>

<one_line_code> while <cond>

begin <multiple_line_code> end while <cond> # or something similar but left-to-right

그 제안이 나머지 언어와 구문 분석되는지는 모르겠습니다. 그러나 어쨌든 나는 언어 일관성뿐만 아니라 왼쪽에서 오른쪽으로 실행하는 것을 선호합니다.


3
a = 1
while true
  puts a
  a += 1
  break if a > 10
end

3
이것은 고토처럼 보입니다. 이 코드는 당신의 의도를 모호하게합니다.
— Gerhard

while true로 교체 할 수있는 것을 제외하고 는 나에게 멋지게 보입니다 loop do.
— David Winiecki

실제로 @DavidWiniecki는로 바꿀 while true수 있습니다 loop do. 하지만 루프 내부 반복의 많은 양 구조를 테스트하고, 그 발견 while true적어도 2 배이다 빠른 것보다 loop do. 차이점을 설명 할 수는 없지만 분명히 있습니다. (코드 번호 2017, 일 (15)의 출현을 테스트하는 동안 발견)
— Jochem Schulenklopper

2

다른 하나는 다음과 같습니다.

people = []
1.times do
  info = gets.chomp
  unless info.empty? 
    people += [Person.new(info)]
    redo
  end
end

나는 이것을 unless바로 앞 대로 선호 unless하며 끝에 있는 '댕글 링'을 찾기 위해 많은 코드 (여기에 표시 될 수 있음)를 읽지 않습니다 . 코드에서 수정 자와 조건이 '앞에'있을 때 사용하기 쉽다는 것이 일반적인 원칙입니다.
— Michael Durrant

때로는 코더가 추가 비교마다 현금을 지불하기를 바랍니다. 그리고 그것이 우리에게 "보이는"방법은 그것을 하루에 백만 번 사용하는 것을 보는 것보다 덜 중요합니다.
— baash05

-3
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end

2
이것은 고토처럼 보입니다. 이 코드는 의도를 불쾌하게하며 매우 루비처럼 보입니다.
— Gerhard

난독 화 * 난독하지 않습니다.
— qedk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.