Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyの面白いコード例7選:読んで学べる実践テクニック集

新しいRubyのテクニック、パターン、メソッドを学ぶ方法のひとつが、他の人が書いたコードを読むことです。

しかし、読む価値のある面白いコードはどこで見つければよいのでしょうか?

世の中には多くのオープンソースプロジェクトがあり、またコーディングチャレンジサイトでは、自分の解答を提出した後に他の開発者の解答を見ることができます。

この記事の内容:

楽しんでいただけると思われる7つのコード例を厳選しました。

それぞれの例について解説も加えているので、ぜひ学びの参考にしてください。

2つの数の合計(Sum Of Two Numbers)

この例では、ユニークな数値の配列が与えられたときに、足し合わせると目標の数値になる2つの数の組み合わせが存在するかどうかを調べます。

コード:

def sum_eq_n?(arr, n)
  return true if arr.empty? && n == 0

  arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
end

このコードが面白いのは、productメソッドを使っている点です。

このメソッドを使うと、ループの中にループがあるような状態になり、配列Aのすべての値と配列Bのすべての値を組み合わせることができます。

カウント・マッピング・検索(Counting, Mapping & Finding)

等差数列から欠けている数を見つけたいとしましょう。たとえば (2,4,6,10) のようなケースです。

ここでは、数値同士の差を計算するという戦略を使います。

[2, 2, 4]

私たちの目的は、この数列がどのようなパターンなのかを特定することです。

増加数列か、減少数列か?

その差はどれくらいか?

次のコードで数列のパターンが明らかになります:

differences = [2, 2, 4]

differences.max_by { |n| differences.count(n) }
# 2
# これが数列内の数値間の増分です

数列のパターンがわかれば、すべての数値を比較して欠けている数を見つけることができます。

コードはこちら:

def find_missing(sequence)
  consecutive     = sequence.each_cons(2)
  differences     = consecutive.map { |a,b| b - a }
  sequence        = differences.max_by { |n| differences.count(n) }

  missing_between = consecutive.find { |a,b| (b - a) != sequence }

  missing_between.first + sequence
end

find_missing([2,4,6,10])
# 8

ここで、面倒な作業を代わりに担ってくれているRubyのメソッドがいくつ使われているか、数えてみてください🙂

正規表現の例(Regular Expression)

文字列を扱っていてパターンを見つけたいなら、正規表現が強い味方になります。

少し扱いづらさを感じることもありますが、練習を重ねれば必ずマスターできます!

それでは:

ある文字列が「母音→非母音」というパターンに従っているかどうかを調べたいとします。

たとえばこんな文字列です:

"ateciyu"

この場合、正規表現と match? メソッドを組み合わせることで判定できます。

コード例はこちら:

def alternating_characters?(s)
  type = [/[aeiou]/, /[^aeiou]/].cycle

  if s.start_with?(/[^aeiou]/)
    type.next
  end

  s.chars.all? { |ch| ch.match?(type.next) }
end

alternating_characters?("ateciyu")
# true

注目すべきポイント:

  1. cycle メソッドを使うことで、「母音」の正規表現と「非母音」の正規表現を交互に切り替え続けることができます。
  2. chars で文字列を文字の配列に変換することで、all? メソッドを使えるようにしています。

再帰とスタックの例(Recursion & Stack)

再帰とは、メソッドが自分自身を何度も呼び出すことで、解決に向けて少しずつ前進していく手法です。

多くの興味深い問題は再帰によって解決できます

しかし再帰には限界があるため、代わりにスタックというデータ構造を使うこともできます。

それでは:

与えられた配列の「べき集合(Power Set)」を求めたいとします。べき集合とは、その配列から作れるすべての部分集合の集合のことです。

まずは再帰を使った例です:

def get_numbers(list, index = 0, taken = [])
  return [taken] if index == list.size

  get_numbers(list, index+1, taken) +
  get_numbers(list, index+1, taken + [list[index]])
end

get_numbers([1,2,3])

同じ問題をスタックを使って解いた例がこちらです:

def get_numbers_stack(list)
  stack  = [[0, []]]
  output = []

  until stack.empty?
    index, taken = stack.pop

    next output << taken if index == list.size

    stack.unshift [index + 1, taken]
    stack.unshift [index + 1, taken + [list[index]]]
  end

  output
end

ここでの考え方は、アルゴリズムの各ステップで「数を取る」か「取らない」かのどちらかを選ぶ、というものです。

両方の結果に分岐して試すことで、すべての可能な組み合わせを生成できます。

木構造をイメージしてみてください。それぞれの葉がひとつの解に対応しています。

注目すべきポイント:

  1. 再帰を使った解法の方がコードが短い
  2. アルゴリズムの実際の「前進」部分(index + 1)はほぼ同じ
  3. Rubyには Stack クラスがないため、使っているスタックは単なる配列

メソッドチェーンの例(Method Chaining)

これは私のお気に入りの例です。Rubyの強力さがよくわかるからです。

メソッドを組み合わせれば、あるメソッドが出力した結果を別のメソッドに渡すことができます。

まるで工場の生産ラインのようです!

原材料(入力)から始めて、メソッドを呼び出す過程で、原材料を少しずつ望む結果へと変形させていくのです。

例を見てみましょう:

def longest_repetition(string)
  max = string
          .chars
          .chunk(&:itself)
          .map(&:last)
          .max_by(&:size)

  max ? [max[0], max.size] : ["", 0]
end

longest_repetition("aaabb")
# ["a", 3]

文字列が与えられると、このコードは最も長く繰り返されている文字を見つけます。

注目ポイント:

  • 可読性を最大化するためにコードが整形されている点
  • Symbol#to_proc パターン(&:size)の活用

ちなみに、これを「デメテルの法則」と混同しないでください。

あの「法則」は、別のオブジェクトの内部に踏み込むことについてのものです。

ここではオブジェクトを変形させているだけなので、問題ありません。

with_indexメソッドの例(With Index)

アイテムのコレクションを反復処理するときに、現在のインデックスも一緒に欲しいと思ったことはありませんか?

そんなときは with_index メソッドが便利です。

例を見てみましょう:

def reverse_alternate(string)
  string.gsub(/[^\s]+/).with_index { |w, idx| idx.even? ? w : w.reverse }
end

reverse_alternate("Apples Are Good")
# "Apples erA Good"

注目ポイント:

  • with_indexeven? を組み合わせて、現在の単語を反転させる必要があるかどうかを判定しています
  • ブロックなしの gsubEnumerator オブジェクトを返すため、他のメソッドとチェーンできます

each_with_objectメソッドの例(Each With Object)

もうひとつ興味深いメソッドが each_with_object で、その仲間に with_object があります。

結果を保持するためのオブジェクトが必要なときに、これらのメソッドを活用できます。

例を見てみましょう:

def clean_string(str)
  str
    .chars
    .each_with_object([]) { |ch, obj| ch == "#" ? obj.pop : obj << ch }
    .join
end

clean_string("aaa#b")

この例では、# 記号を見つけたら直前の文字を削除したい、という処理を行っています。

注目ポイント:

  • each_with_object は引数を1つ取ります。これは処理の開始時に使うオブジェクトで、この引数がブロックの2番目のパラメータになります。
  • 文字列を文字の配列に変換し(chars)、処理が終わったら文字列に戻しています(join)。
  • 三項演算子を使って処理を分岐させることで、コードを簡潔にしています。

まとめ

この記事では、より良いRubyコードを書くためのヒントとして、興味深いコード例とその解説・説明を紹介しました。

より多くの人に楽しんでもらえるよう、この記事のシェアをお忘れなく。

最後までお読みいただき、ありがとうございました!

  1. Rubyでファイルを読み書きする方法を実例つきで徹底解説

    Rubyでファイルの読み書きができるようになると、ファイルからデータを抽出したり、新しいファイルを作成したり、必要な情報を素早く見つけたりできるようになります。この記事では、Rubyにおけるファイル操作の基本を、実際のコード例とともにわかりやすく解説します。 この記事で学べること Rubyでファイルを読み込む方法 Rubyでファイルに書き込む方法 覚えておきたいFileクラスのメソッド ディレクトリ操作の基礎 FileUtilsモジュールの使い方 まとめ それでは始めましょう! Rubyでファイルを読み込む方法 Rubyでファイルを読み込む基本的な流れは、次の3ステップです。 op

  2. Rubyの配列(Array)クラスの使い方徹底解説!基本操作と便利なメソッド一覧

    配列(Array)とは? 配列はRubyに組み込まれたクラスで、0個以上の要素をリスト形式で保持することができます。さらに、要素の追加・アクセス・繰り返し処理を簡単に行えるメソッドも多数用意されています。 もし配列が存在しなければ、値ごとに個別の変数を用意しなければならず、管理が非常に大変になります。 例: a = 1 b = 2 c = 3 しかし、配列を使えば次のように書けます: numbers = [1, 2, 3] さらに嬉しいことに、配列の中には何でも入れられます! 例えば: 数値 文字列 さらに別の配列!(これは多次元配列になります) それでは、配列を最大限に活用できる