成らぬは人の為さぬなりけり

エンジニアライフをエンジョイする為のブログ

Ruby2.0の新機能を勉強してみる その1 キーワード引数

久々のRubyネタです。
2013年2月14日にRuby2.0.0-p0がリリースされました。
2ヶ月程経ってしまいましたが、自称Rubyistとして、
新機能を勉強したいと思います。
Ruby 2.0.0-p0 リリース

ちなみに、本シリーズは1回1テーマでお送り致します。

今回のテーマ

表題にある通り、「キーワード引数」をやってみたいと思います。

環境

インストール

今回もRVMでインストールしました。
で、普通にインストールしようとしたら怒られました、、、


Missing required packages: libyaml, readline, libxml2, libxslt, libksba, openssl, sqlite.
RVM autolibs is now configured with mode '2' => 'check and stop if missing',
please run `rvm autolibs enable` to let RVM do it's job or run and read `rvm autolibs [help]`
or visit https://rvm.io/rvm/autolibs for more information.

色々とパッケージが足りませんよ、と。
「rvm autolibs enable」を叩きなさいよ、とおっしゃっているので、
指示通りコマンドを叩いてからインストールすると、
Homebrewを使って勝手に必要なパッケージをインストールしてくれました。
※この機能は初めて知りました。

キーワード引数とは?

メソッド呼び出し時の引数に仮引数名を指定して渡すことができる機能です。
今までも似たような事はできました。
例えば、

def hoge(options={})
  puts options[:a]
end

hoge(a: "hoge")

このように引数にhashをもらう事で、明示的にパラメータを指定する事ができました。
これは、Rubyのメソッドの引数はhashのみもしくは最後がhashの場合、{}を省略できる事によって実現できていました。
つまり、

hoge({a: "hoge"})

これと同じ事なわけです。

ただ、実際こういうコードを書くと、実際にコードの中身を追わないと
どういうキーのオプションを渡せるのかわかりません。

これは、たまに結構しんどいことになります。
そこで、キーワード引数が役に立つんですね。

キーワード引数を使ってみる

まずは、キーワード引数の基本的な文法をみてみます。

※Ruby2.0.0からマジックコメント不要なようですが、私のEmacsの設定上、勝手に入ってしまうので無視してください。。。

  • 7行目 : 引数を省略したパターン
  • 9&11行目 : キーワード引数を一つだけ指定したパターン
  • 13行目 : キーワード引数を両方指定したパターン
  • 15行目 : キーワード引数の順序を変えたパターン

と、いくつかパターンを試してみました。

実行結果
a is 1.
b is hoge.
--------------------------
a is 100.
b is hoge.
--------------------------
a is 1.
b is 100.
--------------------------
a is aaa.
b is 100.
--------------------------
a is aaa.
b is 100.

キーワード引数ではない引数と混ぜる

キーワード引数は

def メソッド名(キーワード名:デフォルト値)
…
end

という書き方になります。
普通の仮引数と混ぜる場合はどうなるんでしょうか?
やってみます。

このように、キーワード引数は通常の仮引数よりも後に指定する形になるようです。

実行結果
1
hoge

キーワード引数で定義されていないキーワードを指定した場合

どうなるんでしょうか?
やってみます。

def foo(a:1, b:100)
  puts a
  puts b
end

foo(c:200)
実行結果


unknown keyword: c (ArgumentError)

怒られました。ちゃんと怒られるようです。

キーワード引数で定義されていないキーワードに指定された値を取得する

こんなこともできるようです。

引数の一番最後(キーワード引数よりも後)に「**」を付けて仮引数を定義すると、
キーワード引数に定義されていないキーワードを指定した引数がhashで渡されます。
因みに、

foo: 1

とした場合、当然hashのkeyはシンボルになりますが、

"foo" => 1

文字列で渡す事もできます。
しかし、以下の様な書き方はできません。

def bar("a" => 1, "b" => 2)
  puts a
  puts b
end
bar
: syntax error, unexpected tSYMBEG, expecting ')'
def bar("a" => 1, "b" => 2)
         ^
: syntax error, unexpected =>, expecting end-of-input
def bar("a" => 1, "b" => 2)
               ^

と怒られます。
なので、当然これもできません。

def bar(:a => 1, :b => 2)
  puts a
  puts b
end
bar
: syntax error, unexpected tSYMBEG, expecting ')'
def bar(:a => 1, :b => 2)
         ^

ちなみに、上記の結果から予想はできますが、これもできません。

def bar(a:1)
  puts a
end
bar("a" => 100)
: unknown keyword: a (ArgumentError)

ただし、これはできます。

def bar(a: 100, **others)
  puts others["a"]
end
bar("a" => 10000)

まぁ、こんな使い方しないとは思いますが、
何ができて何ができないのか、という事を調べてみると、
つまりこれはどういうことなのか、という事が見えてくることがあるので、
基本いろいろ試すようにしています。

まとめ

  1. キーワード引数は仮引数の最後にしか書けない
  2. キーワード引数に定義されていないものは「**」で取得できる

という事を踏まえて、ひとつ試してみました。


これを実行してみます。

head
100
1000
{:c=>10000}

おぉ、できますね。
なるほど、なんか一見、呼び出し側も新しいシンタックスが増えたような気持ちになっていたんですが、
そうではなく、メソッド定義側だけの問題だったんですね。

次回は遅延リストあたりをやろうかな、と思います。