🐨

Ruby ループ文

2023/07/24に公開

フィボナッチ数列

フィボナッチ数列の基本的な発想とその解法を理解するために、
標準的なフィボナッチ数列を例に説明します。

標準的なフィボナッチ数列は、最初の2つの数が0と1で、
それ以降の数が直前の2つの数の和になるというシーケンスです!

つまり、フィボナッチ数列の最初の10個の数は以下のようになります。

0, 1, 1, 2, 3, 5, 8, 13, 21, 34

Rubyでフィボナッチ数列のn番目の数を計算する方法は以下の通りです🙆🏻‍♀️

def fibonacci(n)
  a = 0
  b = 1

  (n - 1).times do
    temp = a
    a = b
    b = temp + b
  end

  return a
end

puts fibonacci(10) # => 34

この関数は、2つの変数aとbを使って、フィボナッチ数列の直前の2つの数を保持します。

それぞれのループの繰り返しで、aとbは1つ前と2つ前のフィボナッチ数に更新されます。
そして、n番目のフィボナッチ数を計算するには、これを(n - 1)回繰り返します。

問題が異なっても、基本的なアプローチは同じです!

トリボナッチ数列

トリボナッチ数列は、最初の3つの数が定められ、
それ以降の数が直前の3つの数の和になる数列です。

これはフィボナッチ数列の一般化とも言えます。
フィボナッチ数列では、各数が直前の2つの数の和でしたが、
トリボナッチ数列ではそれが3つの数に拡張されています。

トリボナッチ数列に関する簡単な問題

最初の3つの数が1、1、2で、それ以降の数が直前の3つの数の和になる
トリボナッチ数列の10番目の数を求める。

まずは数列を書き出してみましょう。

1, 1, 2, 4, 7, 13, 24, 44, 81, ...

最初の3つの数を用意し、それから始めて各ステップで直前の3つの数を
足して新しい数を作ります。
その繰り返しを10番目の数が出てくるまで行います。

tribonacci = [1, 1, 2]
while tribonacci.length < 10
  tribonacci << tribonacci[-1] + tribonacci[-2] + tribonacci[-3]
end

puts tribonacci.last

このコードはまず、最初の3つの数を配列tribonacciに入れます。
そして、配列の長さが10未満の間、whileループで最新の3つの数を足し合わせて
新しい数を配列に追加します。

ループが終わると、putsを使って配列の最後の数(つまり10番目の数)を出力します。

ループ文

ループ文っていろんな種類あるけど、eachばっかり使ってたな、何がどう違うんだっけとなったので復習。

while

条件が真である間、ループ内のコードが実行されます。
ループを開始する前に条件が偽であれば、ループの中身は一度も実行されません。

i = 0
while i < 5
  puts i
  i += 1
end

このコードは、iが5より小さい間、iの値を出力し続け、それからiを1つずつ増やします。
iが5になると、whileの条件が偽になり、ループは終了します。

for

集合体(配列や範囲など)に対してイテレーション(反復処理)を行います。
集合体の各要素に対して一度ずつループ内のコードが実行されます。

for i in 0..4
  puts i
end

このコードは、範囲0..4の各値に対して、その値を出力します。
これは0から4までの数値を順番に出力します。

each

forループに似ていますが、eachは配列やハッシュなどのオブジェクトに対して
イテレーションを行います。
これはRubyの繰り返し処理に最もよく使われるメソッドです。

[0, 1, 2, 3, 4].each do |i|
  puts i
end

このコードは、配列の各要素に対して、その要素を出力します。

それぞれのループ構文はそれぞれのユースケースに適しています。
whileは一定の条件が満たされている間は何度でも繰り返すような場合に、
forとeachは一定の数の要素すべてに何かを行うような場合に便利です。

forとeachの違い

forループとeachメソッドは、両方とも配列やハッシュのようなコレクション全体を反復処理するために使用されますが、いくつか重要な違いがあります。

変数のスコープ

forループは新しいスコープを作成しません。
これは、ループ内で宣言された変数がループ外でも使用できることを意味します。

一方、eachメソッドは新しいスコープを作成し、
ループ内で宣言された変数はループ外では使用できません。

例えば、次のコードではforループ後にiが使用できます。

for i in [1, 2, 3]
  # ...
end

puts i  # This works, i is available here.

しかし、次のコードではeach後のiはスコープ外となり、使用できません。

[1, 2, 3].each do |i|
  # ...
end

puts i  # This raises an error, i is not available here.

戻り値

forループの戻り値は反復処理したコレクション自体になります。
一方、eachメソッドも同じく反復処理したコレクションを戻り値とします。
この点はforループとeachメソッドの間で差はありません。

"ruby-way"

eachメソッドは、一般的にRubyコミュニティにおいてforループよりも好まれます。
Rubyには、多くの組み込みメソッドがあり、それらはループ処理をより直感的で効率的に行うのに役立ちます。eachメソッドはその一例です。

配列に対するいくつかの組み込みメソッド

each

配列の各要素に対してブロックを実行します。

numbers = [1, 2, 3, 4, 5]
numbers.each do |num|
  puts num * 2  # 配列の各要素を2倍して出力
end
2
4
6
8
10

map

配列の各要素に対してブロックを実行し、その結果から新しい配列を作成します。

numbers = [1, 2, 3, 4, 5]
doubled = numbers.map do |num|
  num * 2  # 配列の各要素を2倍した新しい配列を生成
end
puts doubled  # [2, 4, 6, 8, 10] と出力

select

配列の各要素に対してブロックを実行し、その結果が真である要素からなる新しい配列を作成します。

numbers = [1, 2, 3, 4, 5]
evens = numbers.select do |num|
  num.even?  # 配列から偶数だけを抽出した新しい配列を生成
end
puts evens  # [2, 4] と出力

inject

配列の要素を順に集約していく際に使用します。
例えば配列の要素の総和を計算する際などに使用します。

numbers = [1, 2, 3, 4, 5]
sum = numbers.inject do |total, num|
  total + num  # 配列の各要素の合計を計算
end
puts sum  # 15 と出力

それぞれの違いを理解し、コードの文脈や要件に応じて適切な反復処理を選択することが重要です!!

Discussion