488 Stimmen

Gibt es in Ruby eine "do ... while"-Schleife?

Ich verwende diesen Code, um den Benutzer Namen eingeben zu lassen, während das Programm sie in einem Array speichert, bis sie eine leere Zeichenfolge eingeben (sie müssen nach jedem Namen die Eingabetaste drücken):

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

Dieser Code würde in einer do ... while-Schleife viel besser aussehen:

people = []

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

In diesem Code muss ich die Informationen nicht einer zufälligen Zeichenfolge zuordnen.

Leider scheint es diese Art von Schleife in Ruby nicht zu geben. Kann jemand einen besseren Weg vorschlagen, dies zu tun?

12voto

jvoorhis Punkte 181

Hier ist der vollständige Artikel von hubbardr's totem Link zu meinem Blog.

Beim Lesen des Quelltextes von Tempfile#initialize in der Ruby-Kernbibliothek:

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)

Auf den ersten Blick habe ich angenommen, dass die while Modifikator würde vor dem Inhalt von begin...end aber das ist nicht der Fall. Beobachten Sie:

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

Wie zu erwarten, wird die Schleife weiter ausgeführt, solange der Modifikator wahr ist.

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

Ich wäre zwar froh, wenn ich diese Redewendung nie wieder sehen würde, begin...end ist ziemlich mächtig. Das Folgende ist ein gängiges Idiom, um eine Einzeilermethode ohne Parameter zu memoisieren:

def expensive
  @expensive ||= 2 + 2
end

Hier ist eine hässliche, aber schnelle Methode, um etwas Komplexeres zu memorieren:

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

6voto

jds Punkte 69

Soviel ich weiß, mag Matz das Konstrukt nicht.

begin
    <multiple_lines_of_code>
end while <cond>

weil es eine andere Semantik hat als

<single_line_of_code> while <cond>

indem das erste Konstrukt den Code zuerst ausführt, bevor es die Bedingung prüft, und das zweite Konstrukt die Bedingung zuerst prüft, bevor es den Code ausführt (wenn überhaupt). Ich gehe davon aus, dass Matz das zweite Konstrukt beibehalten möchte, weil es zu einem einzeiligen Konstrukt von if-Anweisungen passt.

Ich habe das zweite Konstrukt nie gemocht, auch nicht für if-Anweisungen. In allen anderen Fällen führt der Computer den Code von links nach rechts (z. B. || und &&) und von oben nach unten aus. Der Mensch liest den Code von links nach rechts von oben nach unten.

Ich schlage stattdessen die folgenden Konstrukte vor:

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

Ich weiß nicht, ob sich diese Vorschläge mit dem Rest der Sprache vereinbaren lassen. Aber auf jeden Fall ziehe ich es vor, die Ausführung von links nach rechts sowie die sprachliche Konsistenz beizubehalten.

3voto

Paul Gillard Punkte 71
a = 1
while true
  puts a
  a += 1
  break if a > 10
end

2voto

Moray Punkte 37

Hier ist eine weitere:

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

-3voto

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X