هل هناك حلقة "do... while" في روبي؟
سؤال
أنا أستخدم هذا الرمز للسماح للمستخدم بإدخال الأسماء بينما يقوم البرنامج بتخزينها في مصفوفة حتى يدخل سلسلة فارغة (يجب عليه الضغط على زر الإدخال بعد كل اسم):
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
سيبدو هذا الرمز أجمل بكثير في حالة ...حائط اللوب:
people = []
do
info = gets.chomp
people += [Person.new(info)] if not info.empty?
while not info.empty?
في هذا الكود ليس من الضروري تعيين معلومات لبعض السلاسل العشوائية.
لسوء الحظ، لا يبدو أن هذا النوع من الحلقات موجود في روبي.هل يمكن لأي شخص أن يقترح طريقة أفضل للقيام بذلك؟
المحلول
حذر:
ال begin <code> end while <condition>
تم رفضه من قبل مؤلف روبي ماتز.بدلا من ذلك يقترح استخدام Kernel#loop
, ، على سبيل المثال.
loop do
# some code here
break if <condition>
end
هنا تبادل البريد الإلكتروني في 23 نوفمبر 2005 حيث صرح ماتز:
|> 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>
روزيتا كود ويكي لديه قصة مماثلة:
خلال نوفمبر 2005، أعرب يوكيهيرو ماتسوموتو، مبتكر روبي، عن أسفه لميزة الحلقة هذه واقترح استخدام Kernel#loop.
نصائح أخرى
لقد وجدت المقتطف التالي أثناء قراءة المصدر لـ
Tempfile#initialize
في مكتبة روبي الأساسية: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 ?> puts "do {} while ()" >> end while false do {} while () => nil
كما هو متوقع، سيستمر تنفيذ الحلقة عندما يكون المُعدِّل صحيحًا.
>> 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
كتبها في الأصل جيريمي فورهيس.تم نسخ المحتوى هنا لأنه يبدو أنه قد تمت إزالته من الموقع الأصلي.يمكن العثور على نسخ أيضًا في أرشيف الويب وفي منتدى روبي باز.-بيل السحلية
مثله:
people = []
begin
info = gets.chomp
people += [Person.new(info)] if not info.empty?
end while not info.empty?
وماذا عن هذا؟
people = []
until (info = gets.chomp).empty?
people += [Person.new(info)]
end
إليك النص الكامل للمقالة من رابط hubbardr الميت إلى مدونتي.
لقد وجدت المقتطف التالي أثناء قراءة المصدر لـ Tempfile#initialize
في مكتبة روبي الأساسية:
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
كما هو متوقع، سيستمر تنفيذ الحلقة عندما يكون المُعدِّل صحيحًا.
>> 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
هذا يعمل بشكل صحيح الآن:
begin
# statment
end until <condition>
ولكن من الممكن أن تتم إزالته في المستقبل، لأن begin
البيان غير بديهي.يرى: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745
أوصى ماتز بالقيام بذلك بهذه الطريقة:
loop do
# ...
break if <condition>
end
من ما جمعته، ماتز لا يحب البناء
begin
<multiple_lines_of_code>
end while <cond>
لأن دلالاتها تختلف عن
<single_line_of_code> while <cond>
في ذلك ، يقوم البناء الأول بتنفيذ الكود أولاً قبل التحقق من الشرط ، ويختبر البناء الثاني الشرط أولاً قبل تنفيذ الكود (إن وجدت).أعتبر أن ماتز يفضل الاحتفاظ بالبنية الثانية لأنها تطابق بنية سطر واحد من عبارات 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
لا أعرف ما إذا كانت هذه الاقتراحات سيتم تحليلها مع بقية اللغة.ولكن على أي حال ، أحافظ على تنفيذ من اليسار إلى اليمين وكذلك تناسق اللغة.
a = 1
while true
puts a
a += 1
break if a > 10
end
تفضل واحد اخر:
people = []
1.times do
info = gets.chomp
unless info.empty?
people += [Person.new(info)]
redo
end
end
ppl = []
while (input=gets.chomp)
if !input.empty?
ppl << input
else
p ppl; puts "Goodbye"; break
end
end