Open11

Ruby Cherry Book vol.2🍒

jinta shimosakajinta shimosaka

基礎知識

全てがオブジェクト

Rubyはオブジェクト指向言語なので、文字列、配列、数値、nil、正規表現まで全てがオブジェクトである。
なので、nilやtrue, falseに対して、メソッドを呼び出すことができるようになっている。

irb(main):001> "1".to_s
=> "1"
irb(main):002> 1.to_s
=> "1"
irb(main):003> nil.to_s
=> ""
irb(main):004> true.to_s
=> "true"
irb(main):005> false.to_s
=> "false"
irb(main):006> /\d+/.to_s
=> "(?-mix:\\d+)"

メソッド呼び出し

メソッドはカッコありなし両方で記述することができる。

irb(main):034> 1.to_s()
=> "1"
irb(main):035> 1.to_s
=> "1"
irb(main):036> 10.to_s(16)
=> "a"
irb(main):037> 10.to_s 16
=> "a"

文の区切り

基本的に改行が文の区切りになる。
ただ、;を使って区切りを指定することもできる。

1.to_s; nil.to_s, 10.to_s(16)

コメント

基本、#の後に書いた文字がコメントになる。
=begin, =endを使ってもコメントを書くことができる。

# コメントだよ。(基本こっち使うかな)

=begin
begin, endで囲った部分がコメントになるよ。
(こっちはあまり使わないかな?)
=end

予約語

変数やメソッド、クラスに付ける名前は「識別子」と言われ、半角英数字(ASCII文字)、漢字、ひらがなを使うことができる。
ただ、Rubyには予約後と言われるものがあり、これは「識別子」には使えなくなっている。

# 予約語一覧
BEGIN class ensure nil self when
END def false not  super while
alias defined? for or then yield
and do if redo true __LINE__
begin else in rescue undef __FILE__
break elsif module retry unless __ENCODING__
case and next return until

変数の宣言

# ローカル変数の定義にはスネークケースを使用する。
special_price = 200

# アンダースコアで始まる書き方も使えるらしい
_special_price = 200

# 数字から始まる書き方はできない
2_special_price = 200
<internal:kernel>:187:in `loop': (irb):40: trailing `_' in number (SyntaxError)

# 一応、漢字、ひらがなも使える
変数 = 200
へんすう = 200

# 多重代入
a, b, = 1, 2  # a=>1, b=>2
a, b = 1  # a=>1, b=>nil
a, b = 1, 2, 3  # a=>1, b=>2, 3は切捨

# 2個以上の変数に同じものを代入
a = b = 200  #a=>200, b=>200
jinta shimosakajinta shimosaka

文字列

シングルクオートとダブルクオート

# 改行文字を埋め込みたい場合
puts "こんにちは\nさようなら"
#=> こんにちは
    さようなら
puts 'こんにちは\nさようなら'
#=> こんにちは\nさようなら

エスケープ(\)

puts "こんにちは\\nさようなら  #=> こんにちは\nさようなら
puts 'He said, "Don\'t speak."' #=> He said, "Don't speak."
puts "He said, \"Don't speak. \""  #=> He said, "Don't speak."

文字列の比較

'ruby' == 'ruby' #=> true
'ruby' == 'Ruby' #=> false
'ruby' != 'perl' #=> true
'ruby' != 'ruby' #=> false

'a' < 'b' #=> true
'a' < 'A' #=> false
'a' > 'A' #=> true
'abc' < 'def' #=> true
'abc' < 'ab' #=> false
'abc' < 'abcd' #=> true
'あいうえお' < 'かきくけこ' #=> true

%記法

# !!の区切り文字はなんでもいい
# %q! !でシングルクオートで囲んだものと同じになる
puts %q!He said, "Don't speak."!  #=> He said, "Don't speak."

# %Q! !(%! !)でダブルクオートで囲んだものと同じになる
something = "Hello"
puts %Q!He said, #{something}!  #=> He said, Hello

ヒアドキュメント

# 通常のドキュメント
text = <<EOS
ここにヒアドキュメントを記述する。
`EOS`で記述している識別子はなんでもいいみたい。(大文字にするのが慣習)
EOS
p text #=> "ここにヒアドキュメントを記述する。\n`EOS`で記述している識別子はなんでもいいみたい。(大文字にするのが慣習)\n"

# 最後の識別子をインデントさせる
text = <<-EOS
最後の識別子をインデントさせる場合は、「-」を付ける。
          EOS
p text #=> "最後の識別子をインデントさせる場合は、「-」を付ける。\n"

# 内部の文字列のインデントを無視させる
text = <<~EOS
        空白を無視させる。
        ちゃんと空白が反映されているか。
          EOS
p text  #=> "空白を無視させる。\nちゃんと空白が反映されているか。\n"

# 内部の空白を反映させる
text = <<~EOS
    \  各行の行頭に空白を2文字入れる。
    \  このとき行頭はバックスラッシュで指定する。
          EOS
p text  #=> "  各行の行頭に空白を2文字入れる。\n  このとき行頭はバックスラッシュで指定する。\n"

# "識別子"なら式展開、改行が有効
name = "Taro"
text = <<"EOS"
こんにちは, #{name}さん
ヒアドキュメント内の改行は\n有効になる。
EOS

puts text
#=>
こんにちは, Taroさん
ヒアドキュメント内の改行は
有効になる。

# '識別子'なら式展開、改行が無効になる。
name = "Taro"
text = <<'EOS'
こんにちは, #{name}さん
ヒアドキュメント内の改行は\n無効になる。
EOS

puts text
#=>
こんにちは, #{name}さん
ヒアドキュメント内の改行は\n無効になる。
# 

jinta shimosakajinta shimosaka

数値

演算子の優先順位

# 上から高い
::
[]
+(単項)
**
-(単項)
* / %
+, -
<<, >>
&
|, ^
>, >=, <, <=
<=>, ==, ===, !=, =~, !~
&&
||
.., ...
?:(条件演算子)
= (+=, -=)
not
and, or
jinta shimosakajinta shimosaka

真偽値と条件分岐

論理演算子

&&と||について

# &&(かつ)は、すべて真であればtrueに、1つでもfalseならfalseを返す。
a = true
b = true
c = false
a && b  #=> ttue
b && c #=> false

# ||(または)は、いずれかが真であればtrue、全て偽であればfalseを返す。
a = true
b = false
c = false
a || b  #=> true
b || c #=> false

# &&は||よりも優先度が高い。
a = true
b = true
c = false
d = false
a && b || c && d  #=> true 下記と同じ意味になる。
(a && b) || (c && d) #=> true

# !で真偽値を反転できる
a = true
!a #=> false

# !()でも反転できる
a = true
b = true
!(a && b)  #=> false

# &&が全て真の場合は、最後に評価されたものを返す(偽は見つけた時点で返す)
a = 1
b = 2
c = 3
d = false
a && b && c #=> 3
a && b && d && c #=> false

# ||は真を見つけた時点で返す。(全て儀の場合は、最後に評価されたものを返す)
a = false
b = 1
c = nil
a || b || c #=> 1
a || c #=> nil

jinta shimosakajinta shimosaka

配列

オブジェクトの作成

# .newで作成する方法
Array.new(1, "apple") #=> ["apple"]
Array.new(3, "apple") #=> ["apple", "apple", "apple"]

# []でも作成できる
["apple"] #=> ["apple"]
["apple", "apple", "apple"] #=> ["apple", "apple", "apple"]

# デフォルト値を指定する際の注意点(ブロックを使うと良い)
fruits = Array.new(3, "apple")
a = fruits[0]
a.upcase!
fruits #=> ["APPLE", "APPLE", "APPLE"] (全てが同じオブジェクトになるらしい)

fruits = Array.new(3) { "apple" }
a = fruits[0]
a.upcase!
fruits #=> "APPLE", "apple", "apple"](異なるオブジェクトが作成されている)

%記法

# %wの場合(エスケープを利用できる)
%w(apple grape orange) #=> ["apple", "grape", "orange"]
%w(big\ apple small\ grape orange) #=> ["big apple", "small grape", "orange"]

# %Wの場合(式展開、改行文字を含めることができる)
a = "apple"
g = "grape"
o = "orange"
%W(#{a}, #{g}, #{o}) #=> ["apple,", "grape,", "orange"]

要素の変更・追加・削除

# インデックスを指定して要素を変更
a = [1, 2, 3]
a[1] = 20
a #=> [1, 20, 3]

# 存在するインデックスよりも大きいインデックスを指定した場合(間にはnilが入る)
a = [1, 2, 3]
a[5] = 6
a #=> [1, 2, 3, nil, nil, 6]

# <<で末尾に要素を追加できる
a = [1, 2, 3]
a << 4
a #=> [1, 2, 3, 4]

# 特定に位置の要素を削除(delete_at)
a = [1, 2, 3]
a.delete_at(2)
a #=> [1, 2]

配列を使った多重代入

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

a, b = [1]
a #=> 1
b #=> nil

a, b = [1, 2, 3]
a #=> 1
b #=> 2
# 2は切り捨て

a, b, c = 1, [2, 3], 4
a #=> 1
b #=> [2, 3]
c #=> 4

a, *b, c, d = 1, 2, 3, 4, 5
a #=> 1
b #=> [2, 3]
c #=> 4
d #=> 5

配列の配列を使った多重代入

demension, i = [[10, 20], 0]
demension #=> [10, 20]
i #=> 0

(length, width), i = [[10, 20], 0]
length #=> 10
witdh #=> 20
i #=> 0

インデックス

a = [1, 2, 3]
a[0] #=> 1
a[1] #=> 2
a[-1] #=> 3
a[-2] #=> 2

和集合・差集合・積集合

# 和集合
a = [1, 2, 3]
b = [3, 4, 5]
a | b #=> [1, 2, 3, 4, 5]

# 差集合
a = [1, 2, 3]
b = [3, 4, 5]
a - b #=> [1, 2]

# 積集合
a = [1, 2, 3]
b = [3, 4, 5]
a & b #=> [3]
jinta shimosakajinta shimosaka

範囲

オブジェクトの作成

1..5
1...5
'a'..'e'
'a'...'e'

範囲を使った配列の作成

# to_aメソッドを使う
(1..5).to_a #=> [1, 2, 3, 4, 5]
(1...5).to_a #=> [1, 2, 3, 4]
('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a #=> ["a", "b", "c", "d"]

# splat演算子(*)を使う
[*1..5] #=> [1, 2, 3, 4, 5]
[*1...5] #=> [1, 2, 3, 4]
[*'a'..'e'] #=> ["a", "b", "c", "d", "e"]
[*'a'...'e'] #=> ["a", "b", "c", "d"]

始端や終端のない範囲オブジェクト

numbers = [1, 2, 3, 4, 5]
numbers[2..] #=> [3, 4, 5]
numbers[2..nil] #=> [3, 4, 5](nilでもいい)
numbers[..3] #=> [1, 2, 3, 4]
numbers[nil..3] #=> [1, 2, 3, 4](nilでもいい)
jinta shimosakajinta shimosaka

メソッド

可変長引数

# 引数を配列をして受け取れる
def greet(*names)
    "#{names.join('と')}、こんにちは!"
end
greet('田中さん', '加藤さん') #=> "田中さんと加藤さん、こんにちは!"
jinta shimosakajinta shimosaka

繰り返し処理の制御

break(一番内側の処理を脱出)

breakについては、繰り返し処理からの脱出になる。

fruits = ["apple", "grape", "orange"]
numbers = [1, 2, 3]
fruits.each do |fruit|
  numbers.shuffle.each do |n|
    puts "#{fruit}, #{n}"
      break if n == 3
  end
end
#=>
apple, 2
apple, 3
grape, 1
grape, 3
orange, 2
orange, 3

throwとcatch(外側のループまで脱出)

fruits = ["apple", "grape", "orange"]
numbers = [1, 2, 3]
catch :done do
  fruits.each do |fruit|
    numbers.shuffle.each do |n|
      puts "#{fruit}, #{n}"
      if n == 3
        throw :done
      end
    end
  end
end
#=>
apple, 1
apple, 2
apple, 3

return(メソッドの脱出)

returnの場合は、繰り返し処理及びメソッドからの脱出になるので、注意する。

# breakの場合
def calc_with_break
  numbers =[1, 2, 3, 4, 5, 6]
  target = nil
  numbers.shuffle.each do |n|
    target = n
    break if n.even?
  end
  target * 10
end
calc_with_break #=> 20

# returnの場合(nが2の時に処理を脱出して、さらにメソッドも脱出するから。。。)
def calc_with_break
  numbers =[1, 2, 3, 4, 5, 6]
  target = nil
  numbers.shuffle.each do |n|
    target = n
    return if n.even?
  end
  target * 10
end
calc_with_break #=> nil

# ただただ繰り返しの箇所でreturnを呼ぶとエラーになる(あくまでメソッドの脱出用)
(1..3).each do |n|
  puts n
  return
end
# =>
1
(irb):271:in `block in <top (required)>': unexpected return (LocalJumpError)

next(繰り返し処理の次の処理に進む(一番内側の処理のみ))

numbers = [1, 2, 3, 4, 5]
numbers.each do |n|
  next if n.even?
  puts n
end
# =>
1
3
5
jinta shimosakajinta shimosaka

ハッシュ

オブジェクトの作成

{}
Hash.new([])
{ "japan" => "yen", "us" => "dollar", "india" => "rupee" }
{ japan: "yen", us: "dollar", india: "ruppe" }

要素の追加・変更・取得

# 追加
currencies = { "japan" => "yen", "us" => "dollar", "india" => "ruppe" }
currencies["italy"] = "euro"
currencies
#=> {"japan"=>"yen", "us"=>"dollar", "india"=>"ruppe", "italy"=>"euro"}

# 変更
currencies = { "japan" => "yen", "us" => "dollar", "india" => "ruppe" }
currencies["japan"] = "円"
currencies
#=> { "japan" => "円", "us" => "dollar", "india" => "ruppe" }

# 取得
currencies = { "japan" => "yen", "us" => "dollar", "india" => "ruppe" }
currencies["india"]
# => "ruppe"

繰り返し処理

# ブロックパラメータを2つで受け取る場合
currencies = { "japan" => "yen", "us" => "dollar", "india" => "ruppe" }
currencies.each do |key, value|
  puts "#{key}, #{value}"
end
#=>
japan, yen
us, dollar
india, ruppe

# ブロックパラメータを1つで受け取る場合
currencies = { "japan" => "yen", "us" => "dollar", "india" => "ruppe" }
currencies.each do |key_value|
  puts "#{key_value[0]}, #{key_value[1]}"
end
#=>
japan, yen
us, dollar
india, ruppe

ハッシュの展開

# ダブルスプラット演算子「**」を使って、ハッシュを展開できる
h = { us: "dollar", india: "rupee" }
{ japan: "yen", **h }
#=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}

擬似キーワード引数(変数 = {})

# 変数 = {}でハッシュの形で引数を受け取れる(何でも受け取れるので注意)
def buy_burger(menu, options = {})
  drink = options[:drink]
  potato = options[:potato]
  puts "menu: #{menu}, drink: #{drink}, potato: #{potato}"
end
buy_burger("cheese", drink: true, potato: true)
#=> menu: cheese, drink: true, potato: true

任意のキーワードを受け取る引数(**変数)

def buy_burger(menu, **others)
  puts others
end
buy_burger("cheese", drink: true, potato: true, chicken: false)
#=> {:drink=>true, :potato=>true, :chicken=>false}(ハッシュの形で値を受け取れる)

ハッシュから配列、配列からハッシュへ

# ハッシュから配列へ
currencies = { japan: "yen", us: "dollar", india: "rupee" }
currencies.to_a
#=>[[:japan, "yen"], [:us, "dollar"], [:india, "rupee"]]

# 配列からハッシュへ
currencies = [[:japan, "yen"], [:us, "dollar"], [:india, "rupee"]]
currencies.to_h
#=> { japan: "yen", us: "dollar", india: "rupee" }
jinta shimosakajinta shimosaka

シンボル

メリット

  • 文字列っぽいので理解しやすい
  • 内部的には整数なので、高速に値を比較できる
  • 同じシンボルは同じオブジェクトなので、メモリの使用効率が良い
  • イミュータブル(破壊的変更が不可)なので、勝手に値を変えられる心配がない。