てきとうなメモ

本の感想とか技術メモとか

Rubyの勉強 (4) - メソッド

メソッド

メソッドはdef〜endで定義する.仮引数を指定できるのでPerlのように@_を用いる必要はない.戻り値は最後に実行した式の値になる.

def fact(n)
  if n == 0
    1
  else
    n * fact(n-1)
  end
end

puts fact(5) # 120

引数

引数は以下のようなものを扱える.

param 通常の引数
param = val デフォルト引数
*param 可変引数
&block ブロック引数

デフォルト引数

デフォルト引数は引数を指定しなかった時のデフォルト値を指定する.

def hello(str="Ruby")
  puts "Hello, #{str}"
end

hello # Hello, Ruby
hello "Perl" # Hello, Perl

可変引数

可変長引数を扱うときは'*'を用いる.すると,Arrayオブジェクトとして利用できる.

def foo(*arg)
  puts arg.join(' ')
end

foo("Hello,", "Ruby") # Hello, Ruby
foo("Hello,", "Ruby", "and", "Perl") # Hello, Ruby and Perl

ブロック

ブロックは無名の関数のようなもの.Perlではmapやsortやgrepなどの引数として利用されてきた.

例えばPerlのmapでは

@array = map {$_ * $_} 1,2,3,4,5;
print "@array\n"; # 1 4 9 16 25

のように利用される.これはRubyでは以下のように書ける.

p [1,2,3,4,5].map {|e| e * e} # [1,4,9,16,25]

ここで,pはオブジェクトを人間が読みやすいよう標準出力に出力するメソッドである.

ブロックはRubyの方が自由に記述でき,例えば,Hash#each_pairはハッシュのキーと値を用いるブロックを引数にとる.

{'foo'=>'bar', 'spam' => 'egg'}.each_pair {|k, v| puts "#{k} => #{v}"}
# foo => bar
# spam => egg

'{}'の代わりにdo〜endを用いることもできる.

{'foo'=>'bar', 'spam' => 'egg'}.each_pair do |k, v|
  puts "#{k} => #{v}"
end

ブロック引数

ブロックを用いるメソッドはブロック引数を用いて定義する.例えば,Perlgrepを定義したい場合,

# オブジェクト指向っぽくないです
def grep(l, &f)
  if l.empty?
    []
  else
    e = l.shift
    if yield e
      [e] + grep(l, &f)
    else
      grep(l, &f)
    end
  end
end

p grep([0, 1, 2, 3, 4, 5]) {|n| n % 2 == 0} # [0, 2, 4]

yieldはブロック引数をメソッドとして実行する.ブロック引数は一つしか利用できず,最後
の引数にしなければならない.

関数

Rubyは全てがオブジェクトなので,正確な意味での関数はなく,全てはメソッドである.putsなどの組み込み関数はKernelクラスのメソッドである.

irb(main)> Object.respond_to?(:puts, true)
=> true
irb(main)> Kernel.respond_to?(:puts, true)
=> true

Objectは全てのクラスのスーパークラスであり,Kernelをインクルードしている.respond_to?(name[, priv=false])はオブジェクトがpublicメソッドnameを持つかどうかの真偽値を返すメソッドである.2つ目の引数がtrueの時はprivateメソッドでも真を返す.

シンボル

Object#respond_to?は第一引数に文字列かシンボルを取る.シンボルは関数名を表したりするらしい.':symbol'で表現できる.

irb(main)> :symbol
=> :symbol
irb(main)> :symbol.class
=> Symbol

スクリプトで定義するメソッド

スクリプトのトップレベルはmainという名のObjectクラスのようである.

puts self # main
puts self.class # Object

そこで定義されるメソッドもObjectのメソッドとして定義される

irb(main)> def fact(n)
irb(main)>   if n == 0
irb(main)>     1
irb(main)>   else
irb(main)>     n * fact(n-1)
irb(main)>   end
irb(main)> end
irb(main)> Object.fact(5)
=> 120

ただし,irbではなくrubyスクリプトとして実行するとprivate methodであるというエラーが出る.

private method `fact' called for Object:Class (NoMethodError)