Rubyプログラミングの基礎知識

2014年7月3日(木)
黒田 努

Procオブジェクト

Procオブジェクトとは

Procオブジェクトをひと言で説明すれば「名前のない関数」です。プログラミング用語としての関数(function)は、引数を受け取り、何らかの処理をして戻り値を返すようなコードを指します。働きはメソッドによく似ています。

Procというクラス名は「手続き」を意味するprocedureに由来します。

Procオブジェクトを生成するコードの基本形は次のとおりです。

-> { ... }

行頭の->はProcオブジェクトを生成する特別な記号です。続く波括弧({})で囲まれた部分はブロックです。Procオブジェクトが処理したい内容をここに書きます。

Procオブジェクトに引数を渡したい場合は、次のように書きます。

-> (x, y) { ... }

xが第1引数、yが第2引数です。メソッド定義と同様、引数にデフォルト値を与えることも可能です。

-> (x, y, z: nil) { ... }

使用例を見てみましょう。

chapter03/proc01.rb

p = -> (n) do
  n += 1 if n.odd?
  n ** 2
end
puts p.call(7)

Procオブジェクトを生成して変数pに代入しています。doとendの間に式が2つあります。その内容は「nが偶数ならnの2乗を、nが奇数ならn + 1の2乗を計算する」というものです。2行目のp.call(7)は、7を引数としてこのProcオブジェクトのコードを呼び出す(実行する)ということです。このスクリプトを実行するとターミナルには64と表示されます。

もちろん、メソッドを定義すればこれと同じことが実現できます。

def foo(n)
  n += 1 if n.odd?
  n ** 2
end
puts foo(7)

なぜProcオブジェクトなどというものが必要なのかと不思議に思われることでしょう。しかし、いったんその疑問は脇に置いてください。Procオブジェクトはメソッドと同様の働きをする、という点だけ理解してください。

Procオブジェクトの活用法

では、Procオブジェクトを用いたプログラムの例をお見せしましょう。

lambda、Proc.new、proc

Procオブジェクトを生成するための->は、Ruby 1.9で導入された比較的新しい記号です。それ以前は、lambdaメソッドまたはprocメソッドでProcオブジェクトを生成していました。次の2行は同じ意味です。

-> (x, y, z: nil) { ... }
lambda { |x, y, z: nil| ... }

また、Procオブジェクトは次のように書いても生成できます(2行とも同じ意味です)。

Proc.new { |x, y, z: nil| ... }
proc { |x, y, z: nil| ... }

ただし、Proc.newまたはprocによって作られたProcオブジェクトは、->またはlambdaで作られたProcオブジェクトと異なり、次の2点の性質を持ちます。

  • 呼び出し側とProcオブジェクト側とで引数の個数が食い違っていてもエラーにならない。
  • コードの中でreturnを使えない。

これらの性質を持つProcオブジェクトはあまり使い勝手のよいものではありませんので、本書では常に->記号を用いてProcオブジェクトを生成することにします。

chapter03/proc02.rb

class Robot
  attr_reader :x, :y
  attr_writer :handler
  def initialize
    @x = 0
    @y = 0
    @handler = -> { move(1, 0) }
  end

  def walk
    @handler.call
    puts "(x, y) = (#{@x}, #{@y})"
  end

  def move(d1, d2)
    @x += d1
    @y += d2
  end
end

r = Robot.new
r.walk
r.walk
r.handler = -> { r.move(0, 1) }
r.walk
r.handler = -> { r.x <= 2 ? r.move(1, 1) : r.move(-1, 1) }
r.walk
r.walk

このプログラムを実行すると、次のような結果となります。

$ ruby proc02.rb
(x, y) = (1, 0)
(x, y) = (2, 0)
(x, y) = (2, 1)
(x, y) = (3, 2)
(x, y) = (2, 3)

initializeメソッドの中で(7行目)、Procオブジェクトが生成されインスタンス変数@handlerに代入されています。

  @handler = -> { move(1, 0) }

この@handlerはwalkメソッドの中で使用されます。

def walk
  @handler.call
  puts "(x, y) = (#{@x}, #{@y})"
end

単に@handlerが参照するProcオブジェクトを呼び出しているだけです。つまり、walkメソッドを呼び出すとmove(1,0)が呼び出されます。

ただし、2行目でattr_writer :handlerと宣言されていますので、次のようにして@handlerを置き換えることができます(24行目)。

r.handler = -> { r.move(0, 1) }

慣れないうちは読みにくいですが、-> { r.move(0.1) }の部分でProcオブジェクトを生成して、それをhandler=メソッドに引数として渡しています。

@handlerが置き換えられると、walkメソッドの働きが変化します。それまでは属性xに1を加えていたのですが、属性yに1を加えるようになりました。

さらに26行目で@handlerを置き換えています。

r.handler = -> { r.x <= 2 ? r.move(1, 1) : r.move(-1, 1) }

その結果、walkメソッドは属性xの値が2以下なら属性xとyに1を加え、そうでなければ属性xから1を引いて、属性yに1を加える、という処理を行うようになります。

私がこのプログラムを通じて示したかったのは、クラスの定義を変更せずにそのインスタンスの機能を調整する方法です。クラスの定義を変更すればその影響範囲はそのインスタンス全体に及びます。そういう事態は避けたい場合があります。

また、このプログラムのテクニックを使えば、インスタンスの機能を動的に変更できます。比喩的に言えば、Robotクラスのインスタンス変数@handlerはロボットに内蔵された「記憶装置」と見なせます。そして、そこにセットされるProcオブジェクトはロボットを駆動するための「ソフトウェア」です。つまり、このプログラムの目的は、ロボットのソフトウェアを「アップグレード」することだったのです。

デフォルトのエンコーディングがUTF-8に

Rubyのバージョン1.9.0から1.9.3までは、ソースコードをUTF-8で記述する場合でも、ASCIIの範囲から外れる文字(ひらがな、カタカナ、漢字など)を使用するときは、ファイルの1行目に次のような形式のコメント(通称、マジックコメント)を書く必要がありました。

# coding: utf-8

Ruby 2.0以降ではデフォルトのエンコーディングがUTF-8になったので、このコメントは不要です。

この記事のもとになった書籍
実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング

黒田 努 著
価格:3,500円+税
発売日:2014年05月23日発売
ISBN:978-4-8443-3592-4
発行:インプレスジャパン

実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング

本書は、Ruby on Railsの実践的な学習書です。最新のRuby2.0およびRuby on Rails4.1、RSpec3.0に対応しました。1つの企業向け顧客管理システムを作る中でRailsによるWebアプリケーション開発の基礎知識とさまざまなノウハウを習得していきます。各章末には演習問題が設けられているので、理解度を確かめながら確実に読み進められます。

Amazon詳細ページへImpress詳細ページへ

株式会社オイアクス

東京大学教養学部卒。同大学院総合文化研究科博士課程満期退学。ギリシャ近現代史専攻。専門調査員として、在ギリシャ日本国大使館に3年間勤務。中学生の頃に出会ったコンピュータの誘惑に負け、IT業界に転身。株式会社ザッパラス技術部長、株式会社イオレ取締役を経て、技術コンサルティングとIT教育を事業の主軸とする株式会社オイアクスを設立。現在、同社代表取締役社長。また、2011年末にRuby on Rails によるウェブサービス開発専業の株式会社ルビキタスを知人と共同で設立し同社代表に就任(オイアクス社長と兼任)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています