루비에 "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.