始めに

誕生から 25 年以上経って, 今更ですが Ruby に入門しました. 本書は Ruby の重要なトピックスを網羅するのみならず, 開発の現場で役に立つようなテクニックの説明にも力を入れられています. Ruby をなんとなく知っているが改めてきちんと学びたい方や, 私のように他言語での経験はあるが Ruby は未経験という方におすすめです.

親切な説明のおかげですらすら読めましたし, 静的型付け言語に親しんできた身からすると新鮮な驚きが多数あったので非常に楽しめました.

3 行でまとめ

  • 丁寧な説明で読みやすい
  • 静的型付け言語に親しんできた身からすると新鮮な驚きが多数あった
  • 便利なスクリプト言語として使っていきたい

あらゆるものがオブジェクト

すべてがオブジェクト (「Ruby コミュニティサイト: Ruby とは」より)

「すべて」の中には例えばリテラルも含まれます. 数値や文字列のリテラルもオブジェクトなので, メソッド呼び出しが可能です.

1
2
3
"hello".upcase # => HELLO
5.times { puts "hello" }
[1, 2, 3].map { |n| n ** 2 } # => [1, 4, 9]

ついでに, 範囲型や正規表現もリテラルとしてサポートされています.

1
2
3
4
(1..10).include?(4.5) # => true

/[a-z]\d+/.class # => Regexp
"a123-4e56-789".scan(/[a-z]\d+/) # => ["a123", "e56"]

さらに, クラスやモジュール自体もオブジェクトです.

1
2
3
4
String.class # => Class (= クラスは Class クラスのオブジェクト)
String.methods # => [:try_convert, :allocate, :superclass, ...]

Kernel.class # => Module (= モジュールは Module クラスのオブジェクト)

果ては irb(Ruby の対話的実行環境)のトップレベル(=どのクラスにも属していない一番外側の部分)すらオブジェクトです.

1
2
3
4
irb(main):001:0> self
=> main
irb(main):002:0> self.class
=> Object

一度 Ruby のオブジェクト指向を見るとこれが自然であると気付かされます. オブジェクトとそうでないものが混在している方が不自然なのです. このような徹底した姿勢が Ruby の人気の秘訣の一つかもと思いました. 私はこれらの(Ruby にとっては)基本的な事項に触れただけで Ruby は他の言語とは違うのだと感じ, 好きになれそうだと思いました.

驚くほど寛容で柔軟な Ruby

動的型付けならではの自由さに驚かされました. 型が混在した配列やダックタイピングが可能です. 全体的に, なるべく型を意識しなくて済むように作られているのではないかと思いました.

モンキーパッチという機能によって, 既存クラスにメソッドを追加したり, 既存のメソッドを上書きしたりすることができます.

1
2
3
4
5
6
7
class Integer
  def one?
    to_i == 1
  end
end

1.one? # => true

モジュールによる既存クラスの拡張も可能です. 以下は String クラスと Array クラスに独自定義した dagger という関数を追加する例です.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
module Daggerable
  def dagger
    "†#{self.to_s}†"
  end
end

String.include Daggerable
Array.include Daggerable
"hello".dagger # "†hello†"
[3, 1, 4].dagger # "†[3, 1, 4]†"

Ruby のクラスは外部から変更可能なので「オープンクラス」と呼ばれることもあります. これらの機能は非常に強力ですが, 無秩序に使うと大混乱が巻き起こりそうです. 知らぬ間に関数定義が変更されていたり, require(C で言うところの include)する順番によって実際に呼び出される関数が変わったり. Ruby ではプログラマができることをなるべく制限しない方針なのかなと思いました. 危険だから禁止する, ではないのです.

その他, 同じことをするのに複数の記法が用意されているのも面白いです. 例えばメソッドには別名が付けられていることがあり, どの名前でも呼び出せます. 文字列の長さを得る String#size ですが, length でも呼び出せます. 自然な名前が複数考えられるときは, 書いた人にとって自然な書き方ができるよう配慮されています.

少し奇妙だなと思ったのは, 関数呼び出し時の引数のかっこを省略できることです. puts 'hello'でもputs('hello')でも, どちらでも良いです. 本書によると, 慣例的に以下のような場合ではかっこを省略することが多いらしいです.

  • 引数がないとき(12.to_sなど)
  • トップレベルで使えるメソッド(putsなど)
  • 予約語のように使えるメソッド(private, requireなど)

プログラミングの楽しさにフォーカスした言語だけあって, 好きなように書けるのは大きな特徴です. やりたいことが自由にできる設計が, 厳密を良しとする静的型付け言語とは異なるスタンスだと思いました.

名前重要

Ruby 作者の Matz が書いたエッセイ「名前重要」(出典: プログラマが知るべき 97 のこと)を読んだことがあります.
エッセイの通り, 命名の美学が Ruby 全体を貫いています.

興味深いと思ったのは, メソッドの末尾の?や!です. ?はinclude?empty?といった真偽値を返す関数に使われます. !は危険を表す記号です. 例えばString#upcaseは大文字にした文字列を返すメソッドであるのに対し, String#upcase!は文字列を破壊的に変更するメソッドです. upcase のように安全バージョンと危険バージョンの両方があるときに!が使われます. 珍しい慣習ですが, 簡潔で分かりやすいと感じました.

その他, 英語として自然になるように配慮されたメソッドもあります. 使いやすく読みやすい. まさにプログラミング言語にとって重要な性質を備えています.

1
2
3
4
irb(main):018:0> [1, 2, 3].each.with_index { |n, idx| puts "#{idx}: #{n}"}
0: 1
1: 2
2: 3

小ネタですが, 面白いと思ったのは演算子の名前です. nil かもしれないオブジェクトに対してメソッド呼び出しをするとき, nil でなければ呼び出した結果, nil なら呼び出さずに nil を返す演算子&.があります. 正式には safe navigation operator という名前ですが, 独りぼっちで膝を抱えているように見えるためボッチ演算子と呼ばれることがあるそうです. また, 比較の演算子<=>は UFO 演算子とも呼ばれるそうです.

思えば C++や C#には遊び心ある俗称がない気がします. 私が知っていたのは kotlin のエルビス演算子?:くらいです. こういったところにも, プログラミング言語の性格の違いが出るのだなと思いました.

型が欲しくなる病

型をずっと意識してきた身からすると, 型が欲しくなったり自由すぎて混乱したりすることがあるのも事実です. とはいえ動的型付け言語での経験を積めば自然と慣れてくるものなのかなと思っていたのですが, なんと Ruby にも型検査機能が導入されました. 比較的新しい機能なのでまだ完全ではないようで導入されていないプロジェクトも多数あるらしいのですが, なんだか少しがっかりした気もします.

Ruby に触れて, 静的型付け言語では型がコードを理解するためのガイドとなっているという当たり前の事実に気づきました. そして Ruby にはそのガイドがない分どのような工夫がなされているのか興味を持っていました. 本書を読んでいて気づいたものの中では, 先述の命名へのこだわりや, 意味を推測しやすい演算子('abc' * 3)といった点は Ruby らしさだと思いました.

しかしそういった工夫はあるものの, やはり大規模なプログラムになると型が欲しくなってくるのものなのでしょうか. 動的型付けならではの良さがあることは確かですし, なんとかして静的/動的両方の美味しいところを取り入れられるといいなと思います.

ワンライナーとしても有用

Ruby は Perl を意識して作られただけあって, ワンライナーとしても実に有用です.

-eオプションを付けることで Ruby を実行できます.

1
ruby -e 'puts "hello"'

ls -laの結果の最初のカラムだけ取り出して大文字にする例です. 特に意味はありませんが, 文字列の split や加工が簡単に行えることが伝わるかと思います.

1
2
3
4
5
6
$ ls -la | ruby -ane 'puts $F[0].upcase'
合計
DRWXR-XR-X
DRWXR-XR-X
-RW-R--R--
-RW-R--R--

オプションの意味は以下のとおりです.

  • -e: 文字列を Ruby スクリプトとみなして実行
  • -n: 一行ずつ入力を受け取って$_に代入
  • -a :入力を split して $F に代入

個人的に身近な使いみちだと, このブログの記事を書くとき毎回文章を以下のように整形していました.

  • 「。」 -> 「. 」(ピリオド + 空白)
  • 「、」 -> 「, 」(カンマ + 空白)
  • 末尾の空白は除く

要は「, 」と 「、」 が入り混じったりするのが嫌だと言うことです. これも Ruby の正規表現で簡単に実現可能です.

1
cat article.md | ruby -ne 'puts $_.gsub(/。/, %q{. }).gsub(/、/, %q{, }).gsub(/\s+$/, %q{})'

注意点として, スクリプトの中で''の代わりに%記法を使っています. %q{} で囲むとシングルクオートで囲ったのと同じになります. つまり 'aaa'%q{aaa} は同じです. ちなみに区切り文字は{}以外も可です.

終わりに

全体として Ruby の一通りの文法まとまっていて, 更に自動テストの Minitest やデバッガの使い方など, 実践で Ruby を使うなら必須であろうものの説明もされていて内容は申し分ないと思いました. タイトルに入門とある通り

  1. 各章の最初に例題を挙げて章の目標を明確にする
  2. 例題を解くために必要な知識の説明
  3. 例題の解説, リファクタリングを通じて注意点に触れる
  4. 全体像をつかめたところで残りの詳細を説明

という流れが非常に飲み込みやすいものでした.

Ruby というと Rails がメジャーな使いみちらしく, Ruby をヘビーに使っていくなら今後 Rails の勉強に進むのもありかなと思うのですが, 個人的には手軽なスクリプト言語としての Ruby に魅力を感じました. 日々のちょっとした作業を Ruby で書いてみて慣れていこうと思います.