読者です 読者をやめる 読者になる 読者になる

勉強メモ1

Ruby Ruby

Rubyは以前遊びぐらいでしか触ったことがありませんが、
今回勉強したいと思い、学習を始めることになりました。

ただ、以前のように分厚い本を買って、表紙(まえがき)から最後の隅々まで
読んで写経することはしないつもりです。

時間を節約し、効率的に勉強したいと思います。

その方法として、既存のプログラミング言語に共通しそうな部分は除いて
Rubyにしかないものだけを勉強する、差分学習を取ります。

この記事はその学習メモです。

文字列中の変数

"#{変数名}"

コメント

  • 1行 : #
  • ブロック =begin ~ =end

変数

ローカル

_variable

グローバル

$variable

インスタンス

@variable

クラス

@@variable

定数

大文字。変更不可能。

多重代入

q, a, z = 1, 2, 3
q, a, *z = 1, 2, 3, 4, 5 # zには配列として代入。1, 2, [3, 4, 5]

q, a = 0, 1
q, a = b, z

ar = [3,4]
q,a = ar # 3, 4

ar = [1, [2, 3], 4] 
q, (a1, a2), z = ar # 1, 2, 3, 4

JavaでのisXXXのようなメソッドRubyでは末尾に?がつく。例)empty?

分岐

  • else ifはelsifと書く。
  • unlessはifの反対。
  • case文にはJavaと違い、色々な条件(固定値、クラス、正規表現)を指定することができる。条件はwhenに書く。

if修飾子とunless修飾子

文の右にifやunlessを書くことができる。ミニif文、ミニunless文ってことか。

a = 1
"Hi" if a > 1 # nil
"Man" if a == 1 # Man
"Hoi" unless a > 1 # Hoi

===

==に似ているが、左辺がリテラル(クラス、数値、文字列など)の場合、右辺との比較を柔軟に行う。
例えば、String == "a"だと、false。String === "a"だと、"a"がStringクラスのインスタンスという比較を行い、trueがリターンされる。

同一性

オブジェクトの値を比較するなら、==を使えばいい。厳密にオブジェクトの同一性を比較するなら、eql?を使う。
Javaとは反対ですね。

繰り返し

  • untilはwhileの反対。
  • nextはJavaのcontinue。
  • redoは同じことをもう一回繰り返す。

times

10.times do 
  print "Duke"
end

配列

names = ["Duke", "Foo"]
names.each do |n|
  puts n
end

ハッシュ

person = { name: "Duke", job: "Engineer" }
person.each do |key, value|
  puts "#{key}: #{value}"
end

ブロック

1行にまとめるときは{ ... }、複数行に分ける場合、do ... end

メソッド

  • 最後にreturn文は省略してもよい。
  • 可変長引数は引数に*argsと書く。

キーワード引数

引数名:デフォルト値

def m1(q:1, a:2, z:3)
  q + a + z
end
m1(z:5,q:3) # 10

ブロック付きメソッド

def m1
  puts "Hello!"
  yield # これがブロックを実行する。
end
m1 do
  puts "Hey Duke!"
end

# Hello!
# Hey Duke!

引数がハッシュの場合、{}を省略可能

def m(q)
  q
end

m({ "n" => "d", "t" => 5 }) # { "n" => "d", "t" => 5 }
m("n" => "d", "t" => 5) # { "n" => "d", "t" => 5 }
m(n: "d", "t": 5) # { "n" => "d", "t" => 5 }

クラス

アクセスメソッド

getter, setterのようなもの。
一々メソッドを定義してもいいが、getter/setterの数が多いと、面倒。
代わりに以下のものを使えばgetterとsetterを自動生成してくれる。

attr_reader :変数名

getter

attr_setter :変数名

setter

attr_accessor :変数名

getter + setter

クラスメソッド

class << オブジェクト ~ end形式がある。
オブジェクトにクラス名を指定すると、以下の例のようにクラスメソッドになる(Classクラスのインスタンスメソッドを追加)。
しかし、オブジェクトに特定インスタンス変数を指定すると、そのインスタンスのみにメソッドが追加される。
まるでJavascriptでオブジェクトに自由にメソッドを追加できるのと同じ感じだ。

# クラス内で定義
class DukeLab
  class << self
    def classMethod1
      "Hey"
    end
  end
end

DukeLab.classMethod1 # Hey

# 特異クラス形式で定義。既にDukeLabクラスが定義されなければならない。
class << DukeLab
  def classMethod2
    "You!"
  end
end

DukeLab.classMethod2 # You!

# クラスの外から定義
def DukeLab.classMethod3
  "Man!"
end

DukeLab.classMethod3 # Man!

# クラス内で定義
class DukeLab
  def self.classMethod4
    "Woman!"
  end
end

DukeLab.classMethod4 # Man!

定数

クラス名::変数名

アクセス制限

何も指定しなければ、public。
public, private, protectedキーワードを使ってアクセス制限を定義できる。

class DukeTest
  def m1
    "Duke1"
  end
  public :m1 #デフォルト

  def m2
    "Duke2"
  end
  private :m2
end

dt = DukeTest.new
dt.m1
dt.m2 # エラー

まとめてprivateにする

class DukeTest
  def m1
    "Duke1"
  end

  private # これより下はprivate
  def m2
    "Duke2"
  end
end

getだけ可能にする。

class DukeTest
  attr_accessor :q, :a  
  private :q=, :a= # xとyにsetできないようにする。
  def initialize(q, a)
    @q, @a = q, a
  end
end

dt = DukeTest.new
dt.q
dt.q = 8 # エラー

継承

class クラス < スーパークラス

メソッドの別名と削除

alias

別名。

undef

削除。スーパークラスメソッドを削除する時に使用。

モジュール

Scalaのtrait、Javaのdefault interfaceって感じ?
クラスでinclude モジュール名すると、そのクラスにモジュールの全てのメソッドが追加される。
モジュールをクラスに入れることをMixinという。

module Mod1
  def m1
    "variable : #{@q}"
  end
end
class Duke
  include Mod1
  def initialize(q)
    @q = q
  end
end

d = Duke.new(5)
d.m1 # variable : 5

Object.extend

特定オブジェクトにモジュールをMixinする。
module Mod1
  def m1
    "variable : #{@q}"
  end
end
class Duke
  def initialize(q)
    @q = q
  end
end
d1 = Duke.new(8)
d1.extend(Mod1)
d1.m1 # variable : 8
extendでクラスメソッドを追加
module Mod1
  def m1
    "Hi!"
  end
end
module Mod2
  def m2
    "Man!"
  end
end
class Duke
  extend Mod1 #クラスメソッド
  include Mod2 #インスタンスメソッド
end
d1 = Duke.new

d1.m1 # エラー。インスタンスメソッドではない。
Duke.m1 # Hi!

d1.m2 # Man!
Duke.m2 # エラー。クラスメソッドではない。

演算子

範囲演算子

Range.new(x, y)又はx..yを使う。

for i in 1..10
  puts i
end

(1..10).to_a # 1,2,3,4,5,6,7,8,9,10
(1...10).to_a # 1,2,3,4,5,6,7,8,9  yを除く。

演算子定義

class Apple
  attr_reader :n, :p
  def initialize(n, p)
    @n = n
    @p = p
  end
  def +@ # 単項演算子の場合、後尾に@をつける。
    "Plus"
  end
  def +(other)
    self.class.new(n + other.n,p + other.p)
  end
  def inspect
    "n : #{n}, p : #{p}"
  end
end

a = Apple.new(1, 10)
b = Apple.new(2, 20)
+a # "Plus"
a + b # n : 3, p : 30

例外

キーワードが違うだけで殆どJavaと同じ。

begin # try
  ...
rescue => ex # catch
  ...
  retry # beginに戻って再実行。無限ループに注意。
ensure # finally 
  ...
end

メソッド全体をtry-catchしたい場合、beginとendは省略可能。

def duke
  ...
  rescue => ex # catch
    ...
    retry # beginに戻って再実行。無限ループに注意。
  ensure # finally 
    ...

例外のスローはraiseキーワードを使う。

rescue修飾子

A rescue Bで、Aが失敗すると、Bが実行される。ミニtry-catchのようなもの。

File.open("duke.txt") rescue "-_-Error" # -_-Error

ブロック

処理のかたまり。

メソッドに何かを実行するブロックを渡すと、前後処理は自動でやってくれるケースが多い。
例えば、以下の場合、ファイルをオープン(前処理)し、その内容を一行ずつ表示する。
ファイルを閉じる処理(後処理)は自動でやってくれる。

File.open("README.md") do |file|
  file.each_line do |line|
    print line
    end
end

ブロックの実行

ブロックはメソッドでyieldコマンドで実行する。
yieldコマンドに引数を指定すると、ブロックに渡される。

def m1(q)
  v = yield(q + 1)
  puts "value : #{v}"
end

m1(5) { |n| "wow => #{n}" }  # value : wow => 6
m1(7) do |n| # value : haha => 7
  "haha => #{n}" 
end

制御

break, next, redoをブロック内でも使うことができる。

def m1(q)
  puts "Step1"
  yield(q)
  puts "Step2"
  yield(q + 1)
  puts "Step3"
  yield(q + 2)
end

m1(1) do |q|   # Step1 Block Step2 Block Step3 Block => 3!!!
  if (q == 5)
    break
  end
  puts "Block"
  "#{q}!!!" 
end

m1(4) do |q|   # Step1 Block Step2 => nil
  if (q == 5)
    break
  end
  puts "Block"
  "#{q}!!!" 
end

# breakやnextに戻り値を指定できる。
m1(4) do |q|   # Step1 Block Step2 => Break!
  if (q == 5)
    break "Break!"
  end
  puts "Block"
  "#{q}!!!" 
end

m1(4) do |q|   # Step1 Block Step2 Step3 Block => 6!!!
  if (q == 5)
    next
  end
  puts "Block"
  "#{q}!!!" 
end

redoは無限ループの危険がありそうだ。

Proc

ブロックをオブジェクトにしてくれるもの。"ブロックをProcオブジェクトにする"と言い方をするらしい。
Procオブジェクト化したブロックはcallメソッドで呼べる。

def m1(q, &b)
  # &はブロックをProcオブジェクトに自動変換する。
  v = b.call(q)
  "yaho!#{v}"
end

m1(5) do |q| # yaho!haha5
  "haha#{q}"
end 

myb = Proc.new do |q|
  "haha2#{q}"
end
m1(8, &myb) # yaho!haha28
m1(8, myb) # エラー。&をつける必要がある。

def m2(q, b)
  v = b.call(q)
  "kaho!#{v}"
end
myb2 = Proc.new do |q|
  "kaha2#{q}"
end
m2(9, myb2) # kaho!kaha29

#別の書き方 : procメソッド
myb3 = proc do |q|
  "kaha3#{q}"
end
m2(10, myb3) #kaho!kaha310

#別の書き方 : lambda
myb4 = lambda do |q|
  "kaha4#{q}"
end
m2(11, myb4) #kaho!kaha411

#別の書き方 : ->
myb4 = ->(q) {
  "kaha5#{q}"
}
m2(12, myb4) #kaho!kaha512

#procとlamdbaの違い1 : lamdbaの方が引数のチェックが厳密だ。
myb5 = proc do |q,a|
  print [q, a]
end
myb6 = lambda do |q,a|
  print [q, a]
end
myb5.call(2) # [2,nil]
myb6.call(2) # エラー : ArgumentError: wrong number of arguments (1 for 2)

#procとlamdbaの違い2 : procを作ったメソッド外でproc内でreturnすると、エラーになる。
def m1
  proc { return 1 }
end
def m2
  lambda { return 2 }
end
p = m1 
p.call # エラー(procが自分を作ったm1の外で呼びだされ、returnしたため) : LocalJumpError: unexpected return
d = m2 
d.call # 2

今日はここまで。