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

Rubyのスプラット演算子(*と**)で配列を自在に構築・分解する方法

Rubyの魅力のひとつは、その機能の奥深さにあります。普段何気なく使っている演算子でも、少し掘り下げてみると、その実力のごく一部しか使いこなせていなかったことに気づくことがあります。地味ながら強力なスプラット演算子(***)は、まさにその好例です。

スプラットを「可変長引数(キャッチオール)」として使ったことはあるかもしれませんが、多くの人はそこで止まってしまいます。実際には、もっと多彩な使い方ができるのです。

def go(x, *args)
  puts args.inspect
end

go("a", "b", "c")

新しいキーワード引数の構文を使う場合は、ダブルスプラットを次のように使います。

def go(**params)
  puts params.inspect
end

go(x: 100, y: 200)

これは便利ですが、スプラットの可能性はこれだけにとどまりません。それでは詳しく見ていきましょう!

配列を使って複数の引数を渡す

スプラットはメソッドを定義するときだけでなく、メソッドを呼び出すときにも使えます。これにより、複数の引数を受け取るメソッドに対して、配列を展開して渡すことができます。配列の1番目の要素が第1引数に、2番目の要素が第2引数に…という具合に対応付けられます。

def go(x, y)
end

point = [12, 10]
go(*point)

もちろん、新しいスタイルのキーワード引数にもダブルスプラットが使えます。

def go(x:, y:)
end

point = { x: 100, y: 200 }
go(**point)

スプラットは引数リストの末尾に置く必要はない

スプラット付きの引数は引数リストの最後に置くのが一般的ですが、そうしなければならない決まりはありません。どこにでも置けます。

def go(x, *args, y)
  puts x # => 1
  puts y # => 5
  puts args.inspect # => [2,3,4]
end

go(1, 2, 3, 4, 5)

配列の分解(デストラクチャリング)

ここまで紹介した引数に関するテクニックは、実は「配列の分解」の特殊なケースにすぎません。

「配列の分解」という用語に馴染みがない方のために説明すると、これは配列を個々の要素に分解することを意味します。次のような形です。

a, b = [1, 2]
puts a
# 1
puts b
# 2

これはうまく動きますが、配列内のすべての要素を受け取る変数を一つひとつ指定しなければならないのは面倒です。スプラット演算子は、まさにワイルドカードのように振る舞うことで、この問題を解決してくれます。いくつか例を見てみましょう。

配列の先頭要素を取り出す

元の配列を変更せずに、先頭の要素だけを取り出したいことがあります。そんなときはこう書きます。

first, *remainder = [1, 2, 3, 4, 5]
first
# => 1
remainder
# => [2, 3, 4, 5]

残りの配列が必要ない場合は、次の構文が使えます。

first, * = [1, 2, 3, 4, 5]
first
# => 1

配列の末尾要素を取り出す

先頭ではなく末尾から要素を取り出したい場合は、スプラットを先頭に置きます。

*prefix, last = [1, 2, 3, 4, 5]
last
# => 5
prefix
# => [1, 2, 3, 4]

同様に、特定の変数に代入する必要がなければ、代入を省略しても構いません。

配列の先頭と末尾の要素を同時に取得する

スプラットを真ん中に置けば、配列の両端から任意の数の要素を取り出せます。

first, *, last =  [1, 2, 3, 4, 5]
first
# => 1
last
# => 5

制限事項

配列の分解でスプラットを使う場合、要素の位置は必ず配列の先頭または末尾からの相対位置で指定する必要があります。そのため、長い配列の途中にある要素を取り出す用途には向いていません。

また、ハッシュを操作するためのダブルスプラット(**)の面白いテクニックは、どうやら存在しないようです。残念!

配列の構築

スプラット演算子は配列の分解だけでなく、構築にも役立ちます。

次の例では、スプラットを使って2つの配列を結合しています。

[*[1,2], *[3,4]]
=> [1, 2, 3, 4]

これは [[1, 2], [3,4]].flatten と同等です。

しかし、話はこれで終わりではありません。スプラットにはもうひとつ風変わりな能力があります。それは、オブジェクトを賢く配列へ変換(型強制)できることです。

# 通常は `*thing` で thing が配列に包まれる
x = *"hi mom"
# => ["hi mom"]

# ...ただし nil の場合は例外
x = *nil
# => []

# 配列はそのまま通過する
x = *[1,2,3]
# => [1, 2, 3]

# ハッシュは配列に変換される
x = *{a: 1}
# => [[:a, 1]]

これにより、大量の手動の型変換を行わずに済む、配列構築のための強力なツールが手に入ります。

たとえば、設定システム向けに文字列の配列を収集している場面を想像してください。通常なら次のような処理が必要になります。

  • 配列が存在するか確認し、なければ初期化する
  • 単一の文字列だけでなく、文字列の配列が追加された場合にも賢く対応する

スプラット演算子を使えば、これらすべてを無料で実現できます。

# 設定ハッシュには :ignore 配列が
# あるかもしれないし、ないかもしれない
config = { }

# コールバック関数は配列を返すこともあれば、
# 単一の値を返すこともある

def add_ignores
  "scoundrels" # ["scoundrels", "cads", "ne'er-do-wells"] のような配列の場合もある
end

# ここが魔法の部分。どんな初期状態から始めても、
# 無視すべき項目の配列が得られる
config[:ignore] = [*config[:ignore], *add_ignores()]

まとめ

既存の配列操作コードをすべてスプラットに書き換えるべきだ、と言いたいわけではないことは明らかでしょう。スプラットは常に正しい道具とは限りませんし、使いすぎるとコードが読みにくくなります。

しかし、時にはスプラットのテクニックこそが求めているものにぴったり合うことがあります。そんな場面に巡り会ったら、ぜひ試してみてください。:)

  1. GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

    稼働時間の監視には、Webサイト、API、およびサーバーの可用性のチェックが含まれます。モニターは、指定された間隔内で特定のエンドポイントをプローブして、それが使用可能かどうかを判別します。目標は、システムのSLAで指定されているように、契約レベルの可用性を達成し、契約が満たされていない場合の違いを判断することです。 この記事では、Prometheusblackbox_exporterに基づく稼働時間監視システムを構築します。カスタムHTTP監視システムを構築するのは簡単かもしれませんが、エクスポーターの周りにラッパーを構築することで、他の多くのプローブ手法にアクセスし、システムの他の要素を

  2. Rubyのラムダ徹底解説――基本から実践的な応用テクニックまで

    Rubyにおいてブロックは非常に重要な存在であり、ブロックなしにこの言語を語ることはできません。では、ラムダはどうでしょうか?ラムダを愛している人はいるのでしょうか?実際のところ、何年もラムダを使わずに過ごすことだって可能かもしれません。まるで昔の時代の遺物のように見えることもあるでしょう。 しかし、それは完全な真実ではありません。少し掘り下げてみると、ラムダには興味深い技が数多く隠されているのです。 この記事では、まずラムダの基本的な使い方から始め、その後、より高度で面白い応用へと進んでいきます。すでに毎日ラムダを使いこなしている方は、ぜひ後半の応用編までスクロールしてください。 ラムダにつ