Ruby'de bir "do… while" döngüsü var mı?


454

Program boş bir dize girene kadar bir dizide saklarken kullanıcı adlarını girmesine izin vermek için bu kodu kullanıyorum (her adın ardından enter tuşuna basmaları gerekir):

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

Bu kod bir do ... while döngüsünde çok daha hoş görünecektir:

people = []

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

Bu kodda bazı rastgele dizelere bilgi atamak zorunda değilim.

Maalesef Ruby'de bu tür bir döngü yok gibi görünüyor. Herkes bunu yapmanın daha iyi bir yolunu önerebilir mi?


1
Sanırım normal while döngüsü daha güzel görünüyor ve okunması daha kolay.
Magne

1
@Jeremy Ruten, Siwei Shen'in cevabının kabul edilen cevabını değiştirmekle ilgilenme şansınız var loop do; ...; break if ...; endmı?
David Winiecki

Yanıtlar:


645

DİKKAT :

Bu begin <code> end while <condition>, Ruby'nin yazarı Matz tarafından reddedildi. Bunun yerine Kernel#loop,

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

İşte 23 Kasım 2005'te Matz'ın belirttiği bir e-posta alışverişi :

|> 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 wiki'nin benzer bir hikayesi var:

Kasım 2005 boyunca, Ruby'nin yaratıcısı Yukihiro Matsumoto bu döngü özelliğinden pişman oldu ve Kernel # loop'un kullanılmasını önerdi.


18
Açık. Bu begin end whileyöntem doğru görünmüyordu. Bana takımı verdiğiniz için teşekkürler, ekibin geri kalanını ikna etmem gerekiyordu.
Joshua Pinter

2
Başlangıç-bitiş-while döngüsü, döngüyü çalıştırmadan önce durumu değerlendiriyor gibi görünüyor. Bu ve normal while döngüsü arasındaki fark, en az bir kez çalışacağı garantisidir. Sorunlara neden olurken ... yapacak kadar yakın.
user1992284

4
Yani, iyi anladığım kadarıyla, başlangıç-bitiş-zamanı "pişman" çünkü değiştiricilerin semantiğini ihlal ediyor, yani: bloğu yürütmeden önce kontrol ediliyorlar, örneğin: k if! K.nil? İ koyar. Burada 'if' bir 'değiştiricidir': 'puts k' deyiminin yürütülüp yürütülmediğini belirlemek için ÖNCE denetlenir.Bu, bir başlangıç-bitiş bloğunun değiştiricileri olarak kullanıldığında ( !), ilk yürütme SONRASI değerlendirilir. Belki bu pişmanlığa neden oldu, ama eski bir forum görevinden daha güçlü bir şey var mı, diğer birçok durumda olduğu gibi resmi bir kullanımdan kaldırılıyor mu?
AgostinoX

2
Bu yakut 'do-while' döngüsünü kullanmamın neden işe yaramadığını anlamak utanç verici bir zaman aldı. Bir c-tarzı bir işi daha yakından taklit etmek için 'sürece' kullanmalısınız, aksi takdirde benim gibi bitirebilir ve durumu tersine çevirmeyi unutabilirsiniz: p
Connor Clark

1
@James Bağlı postaya göre, ekleyerek "pişman" olduğunu söyledi. İnsanlar dil tasarımcısı olsalar bile bazen hata yaparlar.
Xiong Chiamiov

188

Tempfile#initializeRuby çekirdek kitaplığındaki kaynağı okurken aşağıdaki snippet'i buldum :

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)

İlk bakışta, while değiştiricisinin başlangıç ​​... sonundan önce değerlendirileceğini varsaydım, ancak durum böyle değil. Gözlemek:

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

Beklediğiniz gibi, döngü değiştirici doğruyken çalışmaya devam edecektir.

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

Bu deyimi bir daha asla görmekten mutlu olsam da, başlangıç ​​... son oldukça güçlü. Aşağıda, hiçbir parametresiz tek katmanlı bir yöntemi hatırlamak için yaygın bir deyim yer almaktadır:

def expensive
  @expensive ||= 2 + 2
end

İşte daha karmaşık bir şeyi hatırlamanın çirkin ama hızlı bir yolu:

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

Aslen Jeremy Voorhis tarafından yazılmıştır . İçerik, kaynak siteden kaldırılmış gibi göründüğü için buraya kopyalandı. Kopyaları Web Arşivinde ve Ruby Buzz Forumunda da bulabilirsiniz . Kertenkele fatura



56
Bu nedenle harici bir siteye bağlanırken, ilgili bilgileri her zaman buradaki cevaba kopyaladığımdan emin olurum.
davr

10
While değiştiricisinin içerik başlamadan önce neden değerlendirilmesini beklersiniz? Bu şekilde döngüler işe yarayacaktır. Ve neden "bu deyimi bir daha asla görmekten mutlu olursun?" Bunun nesi var? Kafam karıştı.
bergie3000

2
başlangıç ​​... son benzer şekilde bir blok gibi görünüyor {...}. Bunda yanlış bir şey yok.
Victor Pudeyev

1
-1: Siwei Shen'in cevabı bunun begin .. endbiraz kaşlarını çattığını açıklıyor . loop do .. break if <condition>Bunun yerine kullanın .
Kevin

102

Bunun gibi:

people = []

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

Referans: Ruby'nin Hidden'ı {} while () Döngüsü


1
heh, dövüldü. Lanet olsun.
Blorgbeard

Giriş yoksa bu kod diziye boş bir dize eklemez mi?
AndrewR

1
Burada geçerli olmasa da, başlangıç-bitiş-süre yapısının bir problemi, diğer tüm yakut yapıların aksine, son ifadenin değerini döndürmemesi: "yanlışken 1 son başlar" nil döndürür (1 değil, yanlış değil)
tokland

9
Bunun until info.empty?yerine kullanabilirsiniz while not info.empty?.
Andrew Grimm

Aslında @AndrewR bu tür bir nokta .. bir karşılaştırma yapmadan önce bir şeyler yapmak için .. diplay ("rema = # {count}) iken (count> 0) ..." kalan = 0 "bir ekran olsun .. perfect!
baash05

44

Buna ne dersin?

people = []

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

4
Güzel, çok daha zarif.
Blorgbeard

24
Ama bu "do ... while" döngüsü değil. :)
Alexander Prokofyev

4
Ama yanılmıyorsam bu durumda aynı şeyi yapıyor
Blorgbeard dışarı

19
@Blorgbeard, bir do..while döngüsü her zaman bir kez çalışır, ardından çalışmaya devam edip etmeyeceğini değerlendirir. Geleneksel bir while / until döngüsü 0 kez çalışabilir. Bu çok büyük bir fark değil, ama farklılar.
Scott Swezey

5
@Scott, bu doğru - sadece bir do / while kullanmasa bile bu kodun OP'lere eşdeğer olduğunu kastediyorum. Gerçekten de, bu kod döngüdeki "iş" in yarısını yapar, bu yüzden de oldukça geleneksel bir while döngüsü değil - koşul eşleşmiyorsa, bazı işler hala yapılır.
Blorgbeard

11

İşte hubbardr'ın bloguma olan ölü bağlantısından tam metin makalesi.

Tempfile#initializeRuby çekirdek kitaplığındaki kaynağı okurken aşağıdaki snippet'i buldum :

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)

İlk bakışta, whiledeğiştiricinin içeriğinden önce değerlendirileceğini varsaydım begin...end, ancak durum böyle değil. Gözlemek:

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

Beklediğiniz gibi, döngü değiştirici doğruyken çalışmaya devam edecektir.

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

Bu deyimi bir daha asla görmekten mutlu olsam da begin...end, oldukça güçlü. Aşağıda, hiçbir parametresiz tek katmanlı bir yöntemi hatırlamak için yaygın bir deyim yer almaktadır:

def expensive
  @expensive ||= 2 + 2
end

İşte daha karmaşık bir şeyi hatırlamanın çirkin ama hızlı bir yolu:

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

Topladığımdan Matz, yapıyı sevmiyor

begin
    <multiple_lines_of_code>
end while <cond>

çünkü anlambiliminden farklı

<single_line_of_code> while <cond>

ilk yapının, koşulu kontrol etmeden önce kodu yürütmesi ve ikinci yapının, kodu çalıştırmadan önce koşulu test etmesi (eğer varsa). Matz, if ifadelerinin bir satır yapısıyla eşleştiği için ikinci yapıyı korumayı tercih ediyor.

If ifadeleri için bile ikinci yapıyı hiç sevmedim. Diğer tüm durumlarda, bilgisayar kodu soldan sağa (ör. || ve &&) yukarıdan aşağıya yürütür. İnsanlar kodu soldan sağa yukarıdan aşağıya okur.

Bunun yerine aşağıdaki yapıları öneririm:

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

Bu önerilerin dilin geri kalanıyla ayrışıp ayrışmayacağını bilmiyorum. Ancak her durumda, dil tutarlılığını olduğu kadar soldan sağa yürütmeyi tercih ediyorum.


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

3
bu biraz goto gibi görünüyor. Kod niyetinizi engelliyor.
Gerhard

Benim ile harika görünüyor, dışında while truedeğiştirilebilir loop do.
David Winiecki

@DavidWiniecki, aslında while trueile değiştirilebilir loop do. Ama her iki yapıyı da döngü içinde çok fazla yineleme ile test ettim ve bunun while trueen az 2 kat daha hızlı olduğunu keşfettim loop do. Farkı açıklayamıyorum, ama kesinlikle orada. (Advent Advent
2017'yi

2

İşte burada bir başkası:

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

Ben unlesssağ ön ön olduğu gibi bu tercih ve (sadece burada gösterilenden daha fazla olabilir) kod unlesssonunda sadece 'sarkan' bulmak için okuma yok . Kodda genel bir ilke, değiştirici ve koşulların böyle 'ön' olduklarında daha kolay kullanılmalarıdır.
Michael Durrant

Keşke kodlayıcılar her ekstra karşılaştırma için nakit ödemek zorunda kalsaydım. Bize nasıl "nasıl" baktığı, onu günde milyon kez kullanan şeye nasıl göründüğünden daha az önemliydi.
baash05

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

2
bu biraz goto gibi görünüyor. Kod niyetinizi engelliyor ve çok çirkin görünüyor.
Gerhard

şaşkın değil.
qedk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.