| 類別 | 關鍵字/方法 | 特性 | 適用場景 |
|---|---|---|---|
| 條件迴圈 | while / until | 前置判斷、支援 begin-end 後置 | 未知迴圈次數、條件複雜 |
| 枚舉迴圈 | for ... in | 語法糖,內部呼叫 each | 簡單遍歷、初學者友善 |
| 整數迴圈 | times / upto / downto / step | 數值範圍、步長控制 | 計數、索引迴圈 |
| 集合迴圈 | each / each_with_index / map 等 | 區塊語法、函數式風格 | 陣列、Hash、Range、自訂類別 |
| 流程控制 | break / next / redo / retry | 改變迴圈執行流程 | 提前結束、跳過、重試 |
# while:條件為真時執行
i = 0
while i < 5
puts i
i += 1
end
# until:條件為假時執行(等同 while not condition)
i = 0
until i >= 5
puts i
i += 1
end
# 單行 while/until(需用 begin-end 或修飾語)
i = 0
puts i += 1 while i < 5 # 修飾語形式,先執行再判斷
# 等價於
begin
puts i += 1
end while i < 5
# 至少執行一次
begin
print "繼續? (y/n): "
answer = gets.chomp
end while answer != 'n'
# 遍歷 Range
for i in 1..5
puts i
end
# 遍歷陣列
for item in ['apple', 'banana', 'cherry']
puts item
end
# 遍歷 Hash
for key, value in {a: 1, b: 2}
puts "#{key}: #{value}"
end
注意:for 不建立新作用域,迴圈變數會洩漏到外部:
for i in 1..3
x = i * 2
end
puts x # => 6 (x 仍存在!)
# vs each 區塊建立新作用域
5.times { |i| print i } # 01234
5.times do |i|
puts "第 #{i+1} 次"
end
1.upto(5) { |i| print i } # 12345
5.downto(1) { |i| print i } # 54321
# 等價於
(1..5).each { |i| print i }
(1..5).reverse_each { |i| print i }
0.step(10, 2) { |i| print i } # 0246810
10.step(0, -2) { |i| print i } # 1086420
# Range step
(0..10).step(2) { |i| print i }
# 陣列
[1,2,3].each { |x| puts x }
[1,2,3].each_with_index { |x,i| puts "#{i}: #{x}" }
# Hash
{a: 1, b: 2}.each { |k,v| puts "#{k}=#{v}" }
{a: 1, b: 2}.each_key { |k| puts k }
{a: 1, b: 2}.each_value { |v| puts v }
# Range
(1..3).each { |i| puts i }
('a'..'c').each { |c| puts c }
| 關鍵字 | 作用 | 範圍 | 範例 |
|---|---|---|---|
| break | 跳出最內層迴圈 | while/until/for/each/times... | 找到目標提前結束 |
| next | 跳過本次迴圈,進入下一次 | 所有迴圈 | 跳過無效資料 |
| redo | 重新執行本次迴圈(不重新評估條件/迭代器) | 所有迴圈 | 輸入驗證重試 |
| retry | 重新開始整個迴圈(重新評估條件/迭代器) | begin-rescue 區塊 | 例外處理後重試 |
# 找到第一個偶數就停止
[1,3,5,6,7].each do |n|
break if n.even?
puts n
end
# 輸出: 1, 3, 5
# 跳過偶數
[1,2,3,4,5].each do |n|
next if n.even?
puts n
end
# 輸出: 1, 3, 5
3.times do |i|
print "輸入正整數: "
input = gets.chomp
unless input =~ /^\d+$/ && input.to_i > 0
puts "無效,重新輸入"
redo # 重新執行同一次迴圈
end
puts "第 #{i+1} 個: #{input}"
end
attempts = 0
begin
attempts += 1
response = HTTP.get("https://api.example.com/data")
rescue TimeoutError => e
if attempts < 3
puts "超時,重試 (#{attempts}/3)"
retry # 重新執行 begin 區塊
else
raise
end
end
# 陣列解構
[[1,2], [3,4]].each { |a,b| puts a+b } # 3, 7
# Hash 解構
{a: 1, b: 2}.each { |k,v| puts "#{k}=#{v}" }
# 索引與值
%w[a b c].each.with_index(1) { |v,i| puts "#{i}. #{v}" }
# loop 方法(Kernel 模組)
loop do
print "> "
cmd = gets.chomp
break if cmd == 'exit'
puts "執行: #{cmd}"
end
# while true
while true
# ...
end
# 建立枚舉器
enum = 1.upto(10)
enum.each { |i| puts i }
# 鏈式:with_index, with_object, slice_after 等
(1..10).each_slice(3).to_a # [[1,2,3], [4,5,6], [7,8,9], [10]]
%w[a b c d].each_cons(2).to_a # [["a","b"], ["b","c"], ["c","d"]]
require 'benchmark/ips'
N = 1_000_000
Benchmark.ips do |x|
x.report("while") { i=0; while i each > for
# 區塊呼叫有微小開銷,for 內部轉 each 最慢
dup 或建立新集合each 區塊建立新作用域find_each (ActiveRecord)、Enumerator::Lazy
# 迴圈 -> 方法鏈
(1..10).select(&:even?).map { |x| x*2 }.sum # 110
# 累積
(1..5).inject(0) { |sum, n| sum + n } # 15
(1..5).reduce(:+) # 15
[1,2,3].each_with_object([]) { |x, arr| arr << x*2 } # [2,4,6]
Ruby 提供豐富的迴圈構造:條件迴圈用 while/until,計數用 times/upto/downto/step,集合遍歷用 each 系列。流程控制 break/next/redo/retry 語義清晰。現代 Ruby 風格偏好區塊式迭代器而非 for 迴圈,利用 Enumerable 模組的強大方法鏈(map, select, reduce 等)寫出更具宣告式、可組合的程式碼。理解作用域差異、效能特性、函數式替代方案,能根據場景選擇最適合的迴圈寫法。