web-dev-qa-db-fra.com

Existe-t-il une boucle "do ... while" en Ruby?

J'utilise ce code pour permettre à l'utilisateur d'entrer des noms pendant que le programme les stocke dans un tableau jusqu'à ce qu'ils entrent une chaîne vide (ils doivent appuyer sur entrée après chaque nom):

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

Ce code serait beaucoup plus beau dans une boucle do ... while:

people = []

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

Dans ce code, je n'ai pas à attribuer d'informations à une chaîne aléatoire.

Malheureusement, ce type de boucle ne semble pas exister en Ruby. Quelqu'un peut-il suggérer un meilleur moyen de le faire?

444
Jeremy Ruten

CAUTION:

Le begin <code> end while <condition> est rejeté par l'auteur de Ruby, Matz. Au lieu de cela, il suggère d'utiliser Kernel#loop, par exemple.

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

Voici n échange de courrier électronique en 23 novembre 2005, où Matz déclare:

|> 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 a une histoire similaire:

En novembre 2005, Yukihiro Matsumoto, le créateur de Ruby, a regretté cette fonctionnalité de boucle et a suggéré d'utiliser la boucle Kernel #.

623

J'ai trouvé l'extrait de code suivant lors de la lecture du code source de Tempfile#initialize dans la bibliothèque principale Ruby:

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)

À première vue, j'ai supposé que le modificateur while serait évalué avant le contenu de begin ... end, mais ce n'est pas le cas. Observer:

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

Comme on peut s'y attendre, la boucle continuera à s'exécuter tant que le modificateur est vrai.

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

Bien que je serais heureux de ne plus jamais revoir cet idiome, commencez ... la fin est assez puissante. Voici un idiome courant pour mémoriser une méthode one-liner sans paramètre:

def expensive
  @expensive ||= 2 + 2
end

Voici un moyen laid mais rapide de mémoriser quelque chose de plus complexe:

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

Initialement écrit par Jeremy Voorhis . Le contenu a été copié ici car il semble avoir été retiré du site d'origine. Des copies peuvent également être trouvées dans le Web Archive et à Ruby Buzz Forum . -Bill the Lézard

188
hubbardr

Comme ça:

people = []

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

Référence: Ruby's Hidden do {} while () Loop

101
Blorgbeard

Que dis-tu de ça?

people = []

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

Voici l'article en texte intégral du lien mort de hubbardr vers mon blog.

J'ai trouvé l'extrait de code suivant lors de la lecture du code source de Tempfile#initialize dans la bibliothèque principale Ruby:

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)

À première vue, j'ai supposé que le modificateur while serait évalué avant le contenu de begin...end, mais ce n'est pas le cas. Observer:

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

Comme on peut s'y attendre, la boucle continuera à s'exécuter tant que le modificateur est vrai.

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

Bien que je serais heureux de ne plus jamais revoir cet idiome, begin...end est assez puissant. Voici un idiome courant pour mémoriser une méthode one-liner sans paramètre:

def expensive
  @expensive ||= 2 + 2
end

Voici un moyen laid mais rapide de mémoriser quelque chose de plus complexe:

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
11
jvoorhis

Cela fonctionne correctement maintenant:

begin
    # statment
end until <condition>

Mais, il sera peut-être supprimé ultérieurement, car l'instruction begin est contre-intuitive. Voir: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/Ruby/ruby-core/6745

Matz (Ruby’s Creator) a recommandé de le faire de la manière suivante:

loop do
    # ...
    break if <condition>
end
9
Steely Wing

D'après ce que je comprends, Matz n'aime pas la construction

begin
    <multiple_lines_of_code>
end while <cond>

parce que sa sémantique est différente de celle

<single_line_of_code> while <cond>

en ce que la première construction exécute le code avant de vérifier la condition, et la seconde teste la condition avant d'exécuter le code (si jamais). Je suppose que Matz préfère conserver la deuxième construction car elle correspond à une construction de ligne d'instructions if.

Je n'ai jamais aimé la deuxième construction, même pour les déclarations if. Dans tous les autres cas, l'ordinateur exécute le code de gauche à droite (par exemple || et &&) de haut en bas. Les humains lisent le code de gauche à droite de haut en bas.

Je suggère les constructions suivantes à la place:

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

Je ne sais pas si ces suggestions seront analysées avec le reste de la langue. Mais dans tous les cas, je préfère garder l’exécution de gauche à droite ainsi que la cohérence linguistique.

5
jds
a = 1
while true
  puts a
  a += 1
  break if a > 10
end
3
Paul Gillard

En voici un autre:

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