ざっくりん雑記

プログラミングで忘れそうなことをひたすらメモる

Ruby - メソッドの種類

Rubyメソッドの種類

主に大きく分けて、レシーバによって、3種類に分けることができる。

レシーバとは

ちなみに、レシーバ(receiver)というのは、以下のメソッドを呼び出す構文のオブジェクトを指す。

オブジェクト.メソッド名(引数1, 引数2, …, 引数n)

なぜかというと、オブジェクト指向の考え方として、メソッドを実行することをオブジェクトにメッセージを送る、その処理結果として、オブジェクトはメッセージを受け取る(receiveする)と考えるから。

インスタンスメソッド

メソッドの種類として最もメジャーなもの、インスタンスメソッド。オブジェクト(インスタンス)自身がレシーバとなる。クラスの中にメソッドを定義した場合、基本的にインスタンスメソッドとなる。

p "maguro,100,2016-4-14".split(",")    #=> ["maguro", "100", "2016-4-14"]
p ['sushiro', 'kappazushi', 'hamazushi'].index('sushiro')    #=> 0
p 100.to_s    #=> "100"

上の文字列、配列、数値がそれぞれレシーバになっている。そのオブジェクトが持っているインスタンスメソッドは各オブジェクトクラスによって処理が書かれている。

クラスメソッド

レシーバがインスタンスではなく、クラスそのものであった場合、それはクラスメソッドという。主にインスタンスを生成する際によく使われる。

Array.new    # 新しい配列を生成
Array["maguro", "salmon", "ikura"]    # 要素を持った配列を生成

クラスメソッドの呼び出し方には二通りがあって、.(ドット)::(ダブルコロン)で、どちらも意味。

クラス名.メソッド名
クラス名::メソッド名

同じ意味ではあるが、定数アクセスにも::を使用するため、メソッド名が大文字から始まっている場合、それは定数としてみなされかねない。 そのため、メソッド名には全て小文字で命名することを留意した上で、明示的にメソッドであることを示す場合はクラス名::メソッド名()というようにメソッド名の後ろに()を付加する。

関数的メソッド

最後に、レシーバがない関数的メソッドがある。ない、というと語弊があって、正確には省略されているだけ。 これらの関数的メソッドは全てのクラスからアクセス可能な、Kernelモジュールに備わっている。

例として

puts "sushi"    # sushiを出力する
sleep(5)    # 指定された秒数分, 処理を中断する

上に示したような関数メソッドは、レシーバの情報を必要としない。

参考

たのしいRuby 第5版

たのしいRuby 第5版

特異クラス・特異メソッド・メソッドの種類 Ruby - Qiita

www.task-notes.com

Ruby - 繰り返しいろいろ

おもな繰り返し構文・メソッド

times

繰り返したい処理の回数が既に決まっている場合に使用すべきメソッド

基本構文

繰り返し回数.times do
    処理
end

また、doを省略して{}で囲ってあげる書き方も可能。これは、eachも同じですね。

繰り返し回数.times {
    処理
}

timesの処理の中で、何度目の処理かを知るためには、以下のように書けば、指定した変数に繰り返し回数が数値で参照できるようになる。

繰り返し回数.times do |変数| 
    処理
end

具体的に書くと、以下の様なかたち。

5.times do |i|
    puts "#{i}:進捗どうですか"
end
0:進捗どうですか
1:進捗どうですか
2:進捗どうですか
3:進捗どうですか
4:進捗どうですか

for文

特徴的な点としては、範囲オブジェクトを使用する(in 開始時数値..終了時数値がそれ)と開始時の数値と終了時の数値を直感的に指定できる。でもRubyでfor文ってあまりメジャーではないっぽい。

基本構文

オーソドックスなfor文
for 変数 in オブジェクト do
    処理
end

doは省略できる。 オブジェクトには配列やハッシュを指定できる。

sushi = %w(まぐろ いくら たまご サーモン しめさば)
for neta in sushi
    puts "時速120kmで#{neta}が流れてきた!"
end
時速120kmでまぐろが流れてきた!
時速120kmでいくらが流れてきた!
時速120kmでたまごが流れてきた!
時速120kmでサーモンが流れてきた!
時速120kmでしめさばが流れてきた!
範囲オブジェクトを使ったfor文
for 変数 in 開始時数値..終了時数値
    処理
end

例として50から100までの数値を加算するfor文を書くと以下の様なかたち。

sum = 0
for i in 50..100
    sum += i
end

p sum    #=> 3825

while文

作り方によってどのようなタイプの繰り返しでも対応できるwhile文。

基本構文

while 条件 do
    処理
end

doは省略可能。 whileは他言語と同様に条件が成り立っている間はループし続ける

i = 0
while i < 5
    puts "寿司をたべた。(#{i}個目)"
    i += 1
end
寿司をたべた。(0個目)
寿司をたべた。(1個目)
寿司をたべた。(2個目)
寿司をたべた。(3個目)
寿司をたべた。(4個目)

iを1ずつ加算しないと大変なことになる(わりと忘れやすい)、for文で範囲オブジェクトを使ったほうが直感的でわかりやすいので、こういった場合はfor文を書いたほうが良さそうです。

until文

if文に対してunless文があるように、while文に対してuntil文がある。 until文は条件が成立するまで繰り返す

基本構文

until 条件 do
    処理
end

doは省略可能。 さきほど書いたwhile文の例をuntil文に書き直してみる。

i = 0
until i >= 5
    puts "寿司をたべた。(#{i}個目)"
    i += 1
end
寿司をたべた。(0個目)
寿司をたべた。(1個目)
寿司をたべた。(2個目)
寿司をたべた。(3個目)
寿司をたべた。(4個目)

カウンタの処理が寿司を食べた後にあるため、iが5になったときは既に寿司を5つ食べていることに注意する。いっつも何回かデバッグして確認してフィーリングで合わせていくことが多いけど(ちゃんと考えよう)。

eachメソッド

azuuun-memorandum.hatenablog.com

既に記事としているため、省略。

loopメソッド

終了条件のないループ、究極までに無駄が削ぎ落とされたシンプルな繰り返しメソッド。ただそれゆえに、終了しないのでbreakしないと死ぬ。もし、繰り返しが止まらなくて涙目状態になった場合、[Ctrl] + Z[Ctrl] + Cで終了させることができる。

基本構文

loop do
    処理
end

主に繰り返しを制御するための命令としてbreaknextがある(正確にはredoもあるが、使われない)。

  • break
    • 繰り返しを中断して, 繰り返しの中から抜ける
  • next
    • 次の回の繰り返し処理に飛ぶ

まとめ

ここで取り上げた繰り返し文やメソッドをその時々でベストなものを選択し、使うことが重要になる。

  • 回数が決まっている! => times
  • オブジェクトから要素を取り出して使いたい! => for, each
  • 条件を自由に指定したい! => while
  • こうなったら抜けろ!という条件のときに => until
  • 回数制限がない! => loop

参考

たのしいRuby 第5版

たのしいRuby 第5版

www.task-notes.com

Ruby - 多重代入

一般に代入を行うときはa = 1というように一つの変数に代入することがほとんどであるが、変数への代入を複数まとめて行うことも可能で、これを多重代入と呼ぶ。

基本的な多重代入

a = 1
b = 2
c = 3

以上の代入を多重代入に書き直すと、

a, b, c = 1, 2, 3

というようになる。ただ、闇雲に一行で書けて楽だ〜といって多重代入を使うよりは、変数の使いドコロが似ている(関連性のある)ものをまとめて、多重代入をすることが必要だと思われる。

いろいろな多重代入

多重代入で配列を代入したい

配列で格納したい変数名の前に*(アスタリスク)を付けると、その変数より後ろの値が配列形式で代入される(該当する値がない場合はnilが代入される)。

a, b, *c = 1, 2, 3.0, 3.2, 3.4, 3.6, 3.8
#=> [1, 2, [3.0, 3.2, 3.4, 3.6, 3.8]]

変数の値を入れ替える

変数の入れ替えでよく使われるスニペット

a, b = 1, 0
tmp = a
a = b
b = tmp

という一時退避のtmpを使ったものがあるが、Rubyではすっきりと書ける。

a, b = 1, 0
a, b = b, a
p a, b    #=> [0, 1]

配列の要素を変数に落としこむ

以下のように書くと、

array = [1, 2, 3]
a, b, c = array

配列の各要素を左辺の変数に落とし込んでくれる。

p a    #=> 1
p b    #=> 2
p c    #=> 3

先頭だけ取り出したいときは、先頭を格納したい変数の後ろに,(カンマ)を付けると、先頭要素を代入してくれる。

array = [1, 2, 3, 4, 5]
a, = array
p a    #=> 1

複数の戻り値を返し, 受け取りたい

多重代入の使いどころの一つとして、戻り値に多重代入を利用することがある。

def calc(num1, num2)
    values = Array.new
    values.push(num1 + num2)
    values.push(num1 - num2)
    values.push(num1 * num2)
    values.push(num1 / num2)

    return values
end

add, sub, multi, div = calc(10, 2)

puts "加算:#{add}, 減算:#{sub}, 掛算:#{multi}, 割算:#{div}"
加算:12, 減算:8, 掛算:20, 割算:5

加算, 減算, 掛算, 割算を一気に行うメソッド(これが適当かはどうか別として)があったとして、4つの計算結果を一気に戻り値として戻したい場合に配列として渡す。

受け取り側は、戻り値に対応する任意の変数で受け取ることで、複数の戻り値を受け取れている。

参考

www.rubylife.jp

たのしいRuby 第5版

たのしいRuby 第5版

Rubyの多重代入について -- ぺけみさお

Ruby - 配列を列として扱う

配列は列としてみなすことができる

まわりくどいので、ズバッと言うとキュースタックというデータ構造としてみなすこともできる。 このキューやスタックは対等的なデータ構造と言えるが、どちらも

  • 要素を追加し
  • 追加した要素を取り出す

といった操作は共通している。

これをArrayクラスはメソッドとして持っている。

キュー

キューは、最初に格納した要素が、一番最初に取り出される、つまりFIFO(First-in First-out)なデータ構造。

はじめに、キューメソッドとして用意されているのは要素を追加する、要素を取り出すのみなのを意識すると理解しやすいです。

具体的に説明すると、

f:id:azuuun:20160413005731p:plain

まずこのような配列があったとします。 既に要素としてAから順番にDまで格納されています。

ここに、新たに要素を格納するとします。

f:id:azuuun:20160413005740p:plain

するとこのように、Dの後ろに新たな要素がINします(追加されます)。

今度はデータを取り出してみます。 FIFOの原則として、最初に格納したものを最初に取り出す、が前提なので、何が取り出されるかというと、

f:id:azuuun:20160413005743p:plain

このようにAがOUTします(Aが取り出されます)。

最終的には以下の様な配列になります。

f:id:azuuun:20160413005746p:plain

これが、キューです。

スタック

キューの対等的なスタックは、最後に入れたものが最初に取り出される、つまりLIFO(Last-in First-out)なデータ構造です。

これも図でみるとわかりやすいです。

まず、先程と同様にAから順にDまで追加したスタックがあります。原則として、一番上のものしか取れない構造になっていることに注意して下さい。

f:id:azuuun:20160413011036p:plain

ここに新たに要素をINします(追加します)。

f:id:azuuun:20160413011039p:plain

すると一番上に、新たな要素であるEが積み上がる形になります。

f:id:azuuun:20160413011042p:plain

次にデータを取り出してみたいと思います。LIFOの原則なので、ここでは一番最後に追加したEが最初に取り出されます。

f:id:azuuun:20160413011045p:plain

キューとスタックまとめ

キュー

f:id:azuuun:20160413012337p:plain

スタック

f:id:azuuun:20160413012340p:plain

Rubyでどう書くか

キュー

sushi = %w(まぐろ サーモン いくら とびっこ)
p sushi.push('うに')    # うにをIN
p sushi.first                # 最初に入れた要素を参照する
p sushi.shift              # 最初の要素(まぐろ)を取り出す
p sushi
["まぐろ", "サーモン", "いくら", "とびっこ", "うに"]
"まぐろ"
"まぐろ"
["サーモン", "いくら", "とびっこ", "うに"]

スタック

sushi = %w(まぐろ サーモン いくら とびっこ)
p sushi.push('うに')   # うにをIN
p sushi.last                # 最後に入れた要素を参照する
p sushi.pop               # 最後に入れた要素(うに)を取り出す
p sushi
["まぐろ", "サーモン", "いくら", "とびっこ", "うに"]
"うに"
"うに"
["まぐろ", "サーモン", "いくら", "とびっこ"]

参考

たのしいRuby 第5版

たのしいRuby 第5版

Ruby - 配列を集合として扱う

インデックスでいじるだけが配列じゃない

配列を操作するとき、どれを取り出すか、どこからどこまでを置き換える、どこに挿入するか、といったインデックスを意識したようなものが多い印象がある。

しかし、配列のArrayクラスは、「インデックス付きオブジェクト」の集まりに過ぎず、集合としての配列という、インデックスを意識しない配列の使い方もあることもある。

「インデックスを意識しない配列の使い方」とは、[1,2,3][3,1,2][1,3,2]も全て同じ集合を表している、考えることが出来る、ということ。

なので、インデックスを意識した操作は、Arrayクラスの持ったメソッドの一部でしかない。

集合の演算

基本的に、集合の演算というと、AND(共通集合)とOR(和集合)である。

  • AND(共通集合)
    • 2つの集合どちらにも含まれる要素を抽出し 新たな集合を作る
  • OR(部分集合)
    • どちらか一方に含まれる要素を抽出し 新たな集合を作る

以上の演算は、Rubyのコードで表すと

  • AND(共通集合)=> array1 & array2
  • OR(部分集合)=> array1 | array2

となる。

また、Rubyでは補集合(ある集合からその集合に属していない要素を抽出する)がない。それは、Arrayクラスに全体集合に相当するものが定義されていないからで、その代わりに、ある集合からある集合の差を求める差の演算が可能。

  • 差の演算 => array2 - array1

具体的な例

array1 = %w(1 2 3 4 5 6 7 8 9 10)
array2 = %w(2 4 6 8 10)

p (array1 & array2)
p (array1 | array2)
p (array1 - array2)
["2", "4", "6", "8", "10"]
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
["1", "3", "5", "7", "9"]

|+

集合を結合させるメソッドである|(OR)と+は、似てるようで、一部振る舞いが違うことに注意。

  • |(OR)
    • 2つの集合で被った要素はその要素は1つのみ含まれる
  • +
    • 2つの集合で被った要素はその数だけ含んでいく

具体的な例

array1 = %w(1 2 3 4)
array2 = %w(2 4)

p (array1 | array2)
p (array1 + array2)
["1", "2", "3", "4"]
["1", "2", "3", "4", "2", "4"]

参考

たのしいRuby 第5版

たのしいRuby 第5版

Ruby - 配列におけるインデックス

要素の取り出し方いろいろ

要素を取り出す方法は、さまざまある。

  • array[n]
  • array[n..m] , array[n...m]
  • array[n, length]

簡単な例として以下に示す。

season = ['はる', 'なつ', 'あき', 'ふゆ']

p season[0] # はる
p season[0..3] # はる なつ あき ふゆ (配列)
p season[0, 2] # はる なつ (配列)
"はる"
["はる", "なつ", "あき", "ふゆ"]
["はる", "なつ"]

または、これらをメソッドに置き換えても書ける

  • array.at(n)
  • array.slice(n)
  • array.slice(n..m)
  • array.slice(n, length)
p season.at(0) # はる
p season.slice(3) # ふゆ
p season.slice(0, 2) # はる なつ (配列)
"はる"
"ふゆ"
["はる", "なつ"]

あまりメソッドで取り出す方法というのは行わないっぽい。

要素を置き換える

一つの要素を置き換える

まず、一つの要素のみを置き換えたい場合はかんたんにarray[index] = valueというかたちで代入する感じで置き換えることができる。

direction = ['', '西', '', '']

direction[0] = 'east'
direction[3] = 'north'

p direction
["east", "西", "南", "north"]

複数要素を置き換える

直感的に、複数要素を置き換えることができる。

direction[1..2] = ['west', 'south']
p direction
["東", "west", "south", "北"]

要素を挿入する

今ある要素に変更を加えず、配列に新たな要素を追加したい場合に、挿入操作を行う。

あまり直感的ではないかもしれないが、挿入の考え方として、0個の要素と置き換えるとみなして、コードを書く。つまり[n, 0]とするとインデックスがnの要素の前に挿入される。

一つの要素を挿入する

direction = ['', '西', '', '']
direction[1, 0] = ['東と西のまんなか']
p direction
["東", "東と西のまんなか", "西", "南", "北"]

複数要素を挿入する

こちらも、似たような感じで行えばよくて、挿入する配列の要素を増やせば増やした分だけ挿入される感じ。

direction = ['', '西', '', '']
direction[1, 0] = ['東西 その1', '東西 その2']
p direction
["東", "東西 その1", "東西 その2", "西", "南", "北"]

とびとびの要素をかいつまんで、配列にする

冒頭で書いた要素の取り出し方は、連続した要素番号だけ対応できた。しかし、複数のインデックスを指定して、それを一つの配列にする、といったことはできなかった。

そういったことをしたい場合はarray.value_at(n1, n2, ...)メソッドを利用する。

sushi = %w(まぐろ サーモン いくら うに かに えんがわ)
p sushi.values_at(0,2,4)
["まぐろ", "いくら", "かに"]

うまく取り出すことができている。

参考

たのしいRuby 第5版

たのしいRuby 第5版

Ruby - 配列の作り方いろいろ

Rubyでの配列の作り方

Array.new

array = Array.new
p array
array = Array.new(3)
p array
array = Array.new(5, 'Ruby')
p array
  • 何も指定しない
    • そのまま0個の配列ができる
  • 引数を1つだけ(数値)
    • その引数の数だけnilの要素が入った配列ができる
  • 引数を2つ
    • 1つの目の引数:要素数を指定
    • 2つの目の引数:格納したい値 で配列ができる
[]
[nil, nil, nil]
["Ruby", "Ruby", "Ruby", "Ruby", "Ruby"]

%w, %i

要素に空白が含まれていない、つまりそれぞれの区切りが明確になっている場合のみ、使える %w, %i

neta = %w(まぐろ サーモン えび かに うに)
p neta

neta2 = %i(まぐろ サーモン えび かに うに)
p neta2
["まぐろ", "サーモン", "えび", "かに", "うに"]
[:まぐろ, :サーモン, :えび, :かに, :うに]

%wは文字列の配列をつくりたいときに、%iは文字列がシンボルの配列となる。

to_a

配列の作り方としては、あまりメジャーでないがこれでもできなくはない、的なもの。 to_aは多くのクラスで定義されているメソッドでそれぞれのオブジェクトを配列に変換してくれる。

sushi_hash = {
    :maguro => ['まぐろ', 100],
    :salmon => ['サーモン', 120],
    :egg => ['たまご', 150]
}

sushi_array = sushi_hash.to_a

p sushi_array
[[:maguro, ["まぐろ", 100]], [:salmon, ["サーモン", 120]], [:egg, ["たまご", 150]]]

例ではハッシュを配列に変換している。

文字列split

あらゆる情報がカンマで句切られた一つの文字列をsplitメソッドで分割した結果が配列になるパターン。 わりと使われる手法。

person = "寿司太郎,男,まぐろが好き,2016-4-12"
profile = person.split(',')
p profile
["寿司太郎", "男", "まぐろが好き", "2016-4-12"]

参考

たのしいRuby 第5版

たのしいRuby 第5版