【Ruby入門説明書】ruby sliceについて詳しく解説
Rubyで配列の要素を指定するのに使う[]は、記号でも演算子でもなく、メソッドです。そして、メソッドの中にはエイリアスと呼ばれる別名のものが存在しているものもあります。
エイリアスは、他のプログラム言語からの移植や移行をスムーズにしたり、バージョン変更による機能の違いを明確にしたりするために作られます。
[]メソッドにも、エイリアスであるsliceメソッドが存在しています。sliceメソッドは、[]メソッドとまったく同じことができますが、よりメソッドらしく使えますので、他のプログラム言語から移ってきた人たちは、自然な感覚で使えるかもしれません。
なにより、sliceメソッドや[]メソッドは、他のプログラム言語では珍しい、柔軟な要素指定ができますし、なにより文字列で使うことで強力な武器になります。
ここでは、そんなsliceメソッドと[]メソッドについて、詳しく解説していきましょう。
sliceメソッドとは
Rubyのsliceメソッドは、冒頭にも書いたように配列の[]メソッドのエイリアス(別名)です。
ただし、ただの別名ではなく、より柔軟に使えるようになっています。なにより、Arrayクラス(配列)だけではなく、Stringクラス(文字列)やHashクラス(ハッシュ)でも定義されていますので、それらを扱うときの強い味方になってくれることでしょう。
構文
sliceメソッドの構文は、次のようになっています。
オブジェクト.slice(引数1,引数2)
引数には、要素番号(インデックス番号)を表す数値の他、範囲オブジェクトが指定でき、引数2がある場合もあります。
オブジェクトの種類や引数の型によって違いがありますので、後述します。
[]メソッドと同じ
じつは、sliceメソッドの引数については、[]メソッドでもまったく同じです。実際のプログラムで確認してみましょう。
もっとも直感的で分かりやすい配列で確かめてみます。
arr = [10,20,30,40,50]
p(arr[4])
p(arr.slice(4))
(結果)
[bash]50
50 [/bash]
結果の通り、[]メソッドとsliceメソッドは、同じ引数に対して同じ値を出力しています。
Arrayクラスだけではなく、Stringクラスにもある
前述ではArrayクラスで紹介しましたが、sliceを便利に感じるのは、Stringクラスで使う場合です。
じつは、RubyのStringクラスの文字列は、文字列を構成する文字ごとに要素番号が割り振られています。
そのため、次のようなプログラムも、問題なく動作するのです。
str = “I love Ruby”
p(str[2, 4])
p(str.slice(2, 4))
(結果)
[bash]“love”
“love” [/bash]
こちらも、同じ引数で同じ結果となりました。
なお、引数の意味は(要素番号, 長さ)です。つまり、要素番号の文字から、長さの文字数だけ抽出する、という動作になります。
これだけ簡単に文字列の任意の部分文字列を取得できるのは、他のプログラム言語にはあまりないかもしれません。
では、sliceメソッド([]メソッド)について、オブジェクトごとにもう少し詳しく解説していきましょう。
Arrayクラスのsliceメソッド
sliceメソッド([]メソッド)の基本とも言える、Arrayメソッドでの使い方から、説明します。 他のオブジェクトからsliceメソッド([]メソッド)を使う場合の基本動作とも言えますので、しっかりと押さえておきましょう。
構文
sliceの基本構文は冒頭に紹介しましたが、Arrayオブジェクトで利用する場合については、もう少し詳細な説明が必要です。 Arrayオブジェクトでsliceメソッドを使う場合は、次のような3つの構文があります。
Arrayオブジェクト.slice(整数) # Arrayオブジェクト
[整数] Arrayオブジェクト.slice(整数, 整数) # Arrayオブジェクト
[整数, 整数] Arrayオブジェクト.slice(範囲オブジェクト) # Arrayオブジェクト[範囲オブジェクト]
※コメントアウトして、同等の[]メソッドの構文を記載しています。
Arrayクラスのsliceメソッド([]メソッド)については、上記のように引数の違う3つの種類が存在しています。
Arrayオブジェクト.slice(整数)
1つめの構文は、上述でも紹介している、要素番号を設定する方法です。
もっとも一般的で、多くの人が(他のプログラム言語の技術者も)知っている方法ですが、1点だけ特筆すべきところがあります。
それは、引数(要素番号)に負の数を設定できる、ということです。
Rubyの場合、要素番号に-1を指定すると、末尾の要素を指定しているのと同じ扱いにしてくれるのです。そして、そのまま負の数を-2、-3と小さくしていくことで、後ろから2つめ、3つめの要素を指定することができるのです。
確認しておきましょう。
arr = [10,20,30,40,50]
p(arr.slice(0)) # p(arr[0])
p(arr.slice(1)) # p(arr[1])
p(arr.slice(4)) # p(arr[4])
p(arr.slice(5)) # p(arr[5])
p(arr.slice(-1)) # p(arr[-1])
p(arr.slice(-4)) # p(arr[-4])
p(arr.slice(-5)) # p(arr[-5])
p(arr.slice(-6)) # p(arr[-6])
(結果)
[bash]10
20
50
nil
50
20
10
nil [/bash]
結果のように、同じ要素を指定するのに、正の値と負の値を使うことができます。なにより、配列の要素数を気にすることなく末尾の要素を指定できるのは、とても便利な仕組みです。
なお、1点だけ注意していただきたいのは、先頭から指定する場合の要素番号は0から、末尾の場合は-1からですので、正負を反転させるだけでは1つずれてしまう点です。
要素番号については、気を付けて扱わなければいけません。
また、当然ですが、ありえない要素番号を指定するとnil(無効値)が返ってきます。
Arrayオブジェクト.slice(整数, 整数)
こちらもすでに冒頭で紹介していますが、2つの引数を渡し、第1引数で要素番号を指定し、第2引数で長さを指定する方法です。
第1引数の指定方法は、上述の要素番号を指定するのと同じです。
第2引数については、第1引数で指定した場所から、要素番号が大きくなる方向に向けた0以上の正の数で長さを指定します。
こちらも実際の動きを確認しましょう。
arr = [“You”,”want”,”Ruby”,”like”,”love”]
p(arr.slice(0,2)) # p(arr[0,2])
p(arr.slice(1,2)) # p(arr[1,2])
p(arr.slice(4,2)) # p(arr[4,2])
p(arr.slice(4,-2)) # p(arr[4,-2])
p(arr.slice(5,2)) # p(arr[5,2])
p(arr.slice(-1,2)) # p(arr[-1,2])
p(arr.slice(-4,2)) # p(arr[-4,2])
p(arr.slice(-5,2)) # p(arr[-5,2])
p(arr.slice(-6,2)) # p(arr[-6,2])
(結果)
[bash][“You”, “want”]
[“want”, “Ruby”]
[“love”]
nil
[]
[“love”]
[“want”, “Ruby”]
[“You”, “want”]
nil [/bash]
前述しているように、長さは正の数でなければいけません。もし負の値を指定すると、上のプログラムのようにnilが出力されます。
また、配列の外を指定した場合や、長さを0にした場合は、空の配列が返ってきます。
Arrayオブジェクト.slice(範囲オブジェクト)
Arrayクラスの3つめのsliceメソッドは、引数に範囲オブジェクトを設定するものです。
範囲オブジェクトは、(0..9)や(1…5)などといった、範囲を指定することができるオブジェクトです。つまり、sliceメソッドに範囲オブジェクトを渡すだけで、任意の要素から任意の要素の範囲にある要素を取得できます。
こちらも、プログラムコードで動かしてみましょう。
arr = [“You”,”want”,”Ruby”,”like”,”love”]
p(arr.slice(0..2)) # p(arr[0..2])
p(arr.slice(3..4)) # p(arr[3..4])
p(arr.slice(3…5)) # p(arr[3…5])
p(arr.slice(5..7)) # p(arr[5..7])
(結果)
[bash][“You”, “want”, “Ruby”]
[“like”, “love”]
[“like”, “love”]
[] [/bash]
範囲が配列の外だった場合は、空の配列([])が返ってきています。また、もし範囲が配列をオーバーしてしまった場合、sliceメソッドはできる限り取得して処理を終えます。
Stringクラスのsliceメソッド
Stringクラスのsliceメソッドや[]メソッドを使う場合について、説明しましょう。 まずは、Stringクラスのsliceメソッド([]メソッド)の構文を見てみます。
Stringオブジェクト.slice(整数) # Stringオブジェクト[整数] Stringオブジェクト.slice(整数, 整数) # Stringオブジェクト[整数, 整数] Stringオブジェクト.slice(範囲オブジェクト) # Stringオブジェクト[範囲オブジェクト] Stringオブジェクト.slice(正規表現) # Stringオブジェクト[正規表現] Stringオブジェクト.slice(文字列) # Stringオブジェクト[文字列] Stringオブジェクト.slice(正規表現, 整数) # Stringオブジェクト[正規表現, 整数]
Stringクラスの文字列には、文字1つずつに要素番号が割り振られています。その点を考慮することで、文字列をあたかも文字が格納されている配列のように扱うことができるのです。
そのため、最初の3つは、Arrayクラスのsliceメソッドとまったく同じ動作をすることになりますので、説明は割愛します。
Stringクラスのsliceメソッド([]メソッド)特有の特徴として、正規表現を使った抽出がありますので、そちらを詳しく説明したほうが良いでしょう。
Stringオブジェクト.slice(正規表現)
このsliceメソッドを使うことで、正規表現に最初にマッチした部分を取り出すことができます。
具体的な例を見ていただいてから、説明しましょう。
str = “You want Ruby. like or love ?”
p(str.slice(/\s….\s/)) # スペースに挟まれた4文字
p(str.slice(/\w+\./)) # .で終わる単語
(結果)
[bash]” want “
“Ruby.” [/bash]
コメントにある通り、1つめのsliceメソッドでは、「空白文字に挟まれた4文字」を表す正規表現を引数に渡し、最初に見つかった” want “が取得されています。2つめのsliceメソッドは、同様に「.で終わる単語」を取り出しています。
このように、正規表現を引数に渡すことで、文字列の中で最初にマッチした部分を取得することができるのです。
なお、もしマッチするものがなければ、nilが返ってきます。
Stringオブジェクト.slice(文字列)
正規表現を引数に渡す処理の応用で、文字列を渡すことで、その文字列が含まれているかどうかを判定することができます。
sliceは文字列を渡されると、その文字列を固定の正規表現と解釈して、マッチするものを探すわけです。
そして、マッチすればその文字列が返ってきますし、マッチしなければnilが返ってきます。
つまり、nilが返ってこなければ、Stringオブジェクトに文字列が1つ以上存在するということになるのです。
Stringオブジェクト.slice(正規表現, 整数)
sliceメソッドに正規表現を渡すだけであれば、ただマッチした部分文字列を返してくるだけです。
しかし、文字列の内容を検索するにあたっては、もっと複雑な検索が必要な場合があります。
sliceメソッドの第1引数に正規表現、第2引数に0以上の整数を設定することで、少し複雑な検索を行うことができます。
以下のプログラムを実行してみてください。
birthdays =[“1960/05/15”,
“2008/2/18”,
“1995/08/24”,
“1978/12/02”,
“1985/10/8”,
“1960/10/08”]
years = []
months = []
days = []
birthdays.each do |birthday|
# 年の取得
year = birthday.slice(/(\d+)\/(\d+)\/(\d+)/,1).to_i
years.append(year)
# 月の取得
month = birthday.slice(/(\d+)\/(\d+)\/(\d+)/,2).to_i
months.append(month)
# 日の取得
day = birthday.slice(/(\d+)\/(\d+)\/(\d+)/,3).to_i
days.append(day)
end
p(years)
p(months)
p(days)
(結果)
[bash][1960, 2008, 1995, 1978, 1985, 1960]
[5, 2, 8, 12, 10, 10]
[15, 18, 24, 2, 8, 8] [/bash]
このプログラムは、誕生日の文字列から年と月と日の値、それぞれの配列を作るものです。
\d+で、1桁以上の数字を検索していますが、ポイントは、\d+を囲んでいる()です。
Stringクラスのsliceメソッドの第2引数は、この()の位置を表しており、1が1つめの()になります。そのため、年の取得の部分で、1つめの(\d+)にマッチしている年数が取得できるのです。
同様にして、月の取得では、第2引数に2を渡して、2つめの月の数、日の取得では3つめの日の数を取得して、配列に追加しています。
このように、第2引数に値を渡すことで、正規表現の中の一部にマッチした文字列を抜き出すことができます。
なお、第2引数に0を設定した場合、第1引数の正規表現全体にマッチした文字列が取得できます。
Hashクラスのsliceメソッド
Rubyのバージョンが2.5以上であれば、Hashクラスにもsliceメソッドが追加されています。 (バージョンが2.5未満の場合はActiveSupportをインストールしてrequireする必要があります) そのため、HashクラスでもArrayクラスやStringクラスのように、ハッシュの一部を容易に抜き出すことができるようになっています。 構文は以下です。
Hashオブジェクト.slice(キー,,,)
Hashオブジェクトでsliceメソッドを利用することで、引数に渡したキーに該当するペアだけのハッシュを取得できます。
なお、キーはいくつでも構いません。また、Hashオブジェクトに含まれていないキーは無視されます。
簡単なプログラムで動作確認してみましょう。
pv = {“Apple juice”=>300, “Lemon juice”=>200, “Strawberry juice”=>400, “Orange juice”=>500, “Lemon cookie”=>150, “Peach juice”=>400}
p(pv.slice(“Apple juice”, “Lemon cookie”))
(結果)
[bash]{“Apple juice”=>300, “Lemon cookie”=>150} [/bash]
ハッシュから抽出する方法は他にもいくつかあると思いますが、sliceメソッドを使うことで、とてもすっきりした分かりやすいプログラムになっています。
なお、Hashクラスの場合は、[]メソッドのエイリアスではありませんので、注意してください。
破壊的メソッドslice!
最後に、sliceメソッドの破壊的メソッド版を紹介します。
他の「!」付きの破壊的メソッドの多くは、「!」なしのメソッドの戻り値とまったく同じもので自分自身を書き換えるものがほとんどです。
しかし、slice!メソッドは少し違いますので、気を付けましょう。
slice!メソッドは、戻り値として指定した要素を返しますが、同時に自分自身から戻り値に返した部分を削除してしまいます。
プログラムを動かしてみて、確認してみましょう。
birthdays =[“1960/05/15”,
“2008/2/18”,
“1995/08/24”,
“1985/12/24”,
“1960/10/08”]
p(birthdays) # (1)
p(birthdays[2].slice!(0..4)) # (2)
p(birthdays.slice!(-1)) # (3)
p(birthdays) # (4)
(結果)
[bash][“1960/05/15”, “2008/2/18”, “1995/08/24”, “1985/12/24”, “1960/10/08”]
“1995/”
“1960/10/08”
[“1960/05/15”, “2008/2/18”, “08/24”, “1985/12/24”] [/bash]
(1)は、なんの加工もしていない元の日付のリスト表示です。
(2)で3つめの要素である文字列の最初から4文字めまでの「1995/」を取り出しています。
(3)では、末尾の要素「1960/10/08」を取り出し、最後に(4)で日付のリストを再表示しています。
再表示の結果を見ると、3つめの要素が日付だけになっており、末尾の要素「1960/10/08」がなくなっているのが見て取れます。
このように、slice!メソッドは、「配列や文字列から指定した要素を取り出している」と考えれば良いでしょう
問題「在庫リスト」
一点もののハンドメイド製品を集めた在庫リストがあります。ここから、売れた製品を削除してください。
# 在庫リスト
leathers = [“hand bag”, “half wallet”, “long wallet”, “pen”, “shoulder bag”, “pen case”, “book cover”, “memo pad”, “pass case”]
# 売れた商品
sold_outs = [“book cover”, “long wallet”, “pen”]
ヒント:
ある配列の値から要素番号を取得するには、find_indexメソッドを使いましょう。
# 条件に一致した最初の要素の要素番号を返します
inde = Arrayオブジェクト.find_index {|ブロック引数| 条件}
まとめ
sliceメソッドについて、解説しました。
sliceメソッドは、ArrayオブジェクトやStringオブジェクト、Hashオブジェクトから利用することができ、それぞれ要素番号を使って操作することができます。
特に、Stringクラスのsliceメソッドについては、正規表現が使えることもあって、文字列操作をする上で、とても強い味方になってくれるものです。
正規表現は慣れるまで少し難しいかもしれませんが、それゆえに、ぜひ積極的に使っていったほうが良いでしょう。
ただ、残念ながら、こういった学習サイトや参考書では限界がありますので、プログラミングのセミナーやスクールを利用して、有識者と直接話をして質問するのが、理解できる一番の近道かもしれません。
・Arrayの要素番号を負の数にすると、配列の末尾から数える(末尾は-1)
・範囲外の要素番号を指定すると、nilが返ってくる
・配列の外までの範囲を指定すると、できる限り取得した配列を返す。配列の外を指定すると空の配列([])が返ってくる。
・Stringクラスの文字列は、文字列を構成する文字ごとに要素番号が割り振られている
・HashクラスのSliceメソッドの引数は、いくつでも良い
・Hashクラスのsliceメソッドは、[]メソッドのエイリアスではない
・slice!メソッドは、「配列や文字列から指定した要素を取り出している」
問題の答え
# 在庫リスト
leathers = [“hand bag”, “half wallet”, “long wallet”, “pen”, “shoulder bag”, “pen case”, “book cover”, “memo pad”, “pass case”]
# 売れた商品
sold_outs = [“book cover”, “long wallet”, “pen”]
sold_outs.each do |so|
index = leathers.find_index {|l| l == so}
if not index.nil?
leathers.slice!(index)
end
end
p(leathers)
【インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!
ブラジルから帰国し技術をつけようとRubyエンジニアを目指してWebCampでRubyを学び、見事Webエンジニアとして転職を果たした田中さんにお話を伺いました。
「Rubyの学習がしたい。基礎をしっかりと理解したい」
「転職のサポートがほしい」
と考えている方はぜひお読み下さい。