バリケンのRuby日記 RSSフィード

2006-05-31

[] File::atime, File::ctime, File::mtime  File::atime, File::ctime, File::mtime - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  File::atime, File::ctime, File::mtime - バリケンのRuby日記  File::atime, File::ctime, File::mtime - バリケンのRuby日記 のブックマークコメント

File::atime, File::ctime, File::mtimeは、それぞれ

Timeオブジェクトとして返すよ。

じゃあ、やってみよう!まずはファイルを作るよ。

$ vi test.txt

内容をhogeとして保存したよ。

$ irb
irb(main):001:0> File.atime('test.txt')
=> Wed May 31 09:46:30 JST 2006
irb(main):002:0> File.ctime('test.txt')
=> Wed May 31 09:46:30 JST 2006
irb(main):003:0> File.mtime('test.txt')
=> Wed May 31 09:46:30 JST 2006
irb(main):004:0> exit

次に、ファイルの中身を見てみるよ。

$ less test.txt

どう変わったかな?

$ irb
irb(main):001:0> File.atime('test.txt')
=> Wed May 31 09:47:02 JST 2006
irb(main):002:0> File.ctime('test.txt')
=> Wed May 31 09:46:30 JST 2006
irb(main):003:0> File.mtime('test.txt')
=> Wed May 31 09:46:30 JST 2006
irb(main):004:0> exit

次に、ファイル編集してみるよ。

$ vi test.txt

内容をhogefugaにしてみたよ。

$ irb
irb(main):001:0> File.atime('test.txt')
=> Wed May 31 09:47:36 JST 2006
irb(main):002:0> File.ctime('test.txt')
=> Wed May 31 09:47:36 JST 2006
irb(main):003:0> File.mtime('test.txt')
=> Wed May 31 09:47:36 JST 2006
irb(main):004:0> exit

次に、アクセス権限を変えてみたよ。

$ chmod 600 test.txt

どう変わるかな?

$ irb
irb(main):001:0> File.atime('test.txt')
=> Wed May 31 09:47:36 JST 2006
irb(main):002:0> File.ctime('test.txt')
=> Wed May 31 09:48:11 JST 2006
irb(main):003:0> File.mtime('test.txt')
=> Wed May 31 09:47:36 JST 2006
irb(main):004:0> exit

ふーむ、なるほどね。ほかにはどういった「ファイルアクセスする」方法があるかなあ?

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060531

2006-05-30

[] attr_reader, attr_writer, attr_accessor  attr_reader, attr_writer, attr_accessor - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  attr_reader, attr_writer, attr_accessor - バリケンのRuby日記  attr_reader, attr_writer, attr_accessor - バリケンのRuby日記 のブックマークコメント

Module#attr_readerは、インスタンス変数を外から参照するためのアクセサを定義するよ。たとえば

class Hoge
  def initialize (name)
    @name = name
  end

  attr_reader :name
end

a = Hoge.new('Hello, world!')
puts a.name

と書くと、実行結果は

Hello, world!

となるよ。ちなみに上記は次のように書いたのと同じことだよ。

class Hoge
  def initialize (name)
    @name = name
  end

  def name
    return @name
  end
end

a = Hoge.new('Hello, world!')
puts a.name

Module#attr_writerは、インスタンス変数に外から代入することを可能とするアクセサを定義するよ。たとえば

class Hoge
  attr_writer :name

  def to_s
    puts @name
  end
end

a = Hoge.new
a.name = 'Hello, world!'
a.to_s
a.class

とすると、実行結果は

Hello, world!
Hoge

となるよ。ちなみに上記は次のように書いたのと同じことだよ。

class Hoge
  def name=(val)
    @name = val
  end

  def to_i
    puts @name
  end
end

a = Hoge.new
a.name = 'Hello, world!'
a.to_i
a.class

Module#attr_accessorは、Module#attr_readerとModule#attr_writerの両方を指定したのと同じことになるよ。たとえば

class Hoge
  attr_accessor :name
end

a = Hoge.new
a.name = 'Hello, world!'
puts a.name

と書けば、実行結果は

Hello, world!

となるよ。ちなみに上記は

class Hoge
  def name
    return @name
  end
  def name=(val)
    @name = val
  end
end

a = Hoge.new
a.name = 'Hello, world!'
puts a.name

と書いたのと同じことだよ。

インスタンス変数には通常外からアクセスできないから、クラスを定義したいときには覚えておくと便利だよ。

[] バリケンHaskell日記  バリケンのHaskell日記 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  バリケンのHaskell日記 - バリケンのRuby日記  バリケンのHaskell日記 - バリケンのRuby日記 のブックマークコメント

やっぱり関数型言語の魅力に負けて、Haskell勉強もいっしょにやることにしたよ。

Ruby on Railsからはどんどん遠ざかっちゃうけど‥‥。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060530

2006-05-29

[] シンボル  シンボル - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  シンボル - バリケンのRuby日記  シンボル - バリケンのRuby日記 のブックマークコメント

シンボルは「文字列みたいなもの」だよ。コロンのあとにつづけて文字をかけば、リテラルとしてSymbolクラスオブジェクトなるみたいだよ。たとえば、

a = :hello
puts a.class

とやると、次のようになるよ。

Symbol

「文字列(Stringクラスオブジェクト)」と何が違うのかというと、同じ名前のシンボルは、かならず同じオブジェクトになるよ。たとえば、

puts :hello.object_id
puts :hello.object_id
puts 'hello'.object_id
puts 'hello'.object_id

とやると、実行結果は

4141326
4141326
-605565422
-605569762

となるよ。文字列は毎回オブジェクトを生成しているけど、シンボルは常に同じオブジェクトなのがわかるよね。

予約語変数名、メソッド名なんかは、実はシンボルとして管理されているよ。たとえば

a = :hello

と書いたとき、:helloシンボルだけじゃなくて:aシンボルも内部で生成されているよ。

どんなシンボルが定義されているかは、Linuxならコマンドラインから

$ ruby -e 'Symbol.all_symbols.each_with_index {|x, i| puts "#{i}: #{x}" }' | less

として調べることが出来るよ。ものすごい数のシンボルが最初から定義されているんだねえ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060529

2006-05-28

[] eval  eval - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  eval - バリケンのRuby日記  eval - バリケンのRuby日記 のブックマークコメント

Kernel#evalは、引数として与えられた文字列をRubyスクリプトとして評価するよ。たとえば、

a = 'puts "Hello, world!"'
eval(a)

と書くと、次のような実行結果になるよ。

Hello, world!

戻り値は、一番最後に評価された式の戻り値になるみたい。たとえば、

a = '[ 3, 4, 5 ]; [ 6, 7, 8 ]'
eval(a).each {|x| puts x}

と書くと、次のような実行結果になるよ。

6
7
8

こんなふうに単に文字列がRubyスクリプトそのままの記述だとあんまり面白みがないけど、文字列を動的に生成してから評価するとかが出来るから、(たとえばメソッド名をdefで動的に生成するとか)工夫次第でかなり面白いことができるよ。

あとは、ちょっとした設定ファイルが欲しいときとか(ユーザーデフォルト値を変更してから使うような用途のスクリプトとか)、設定項目をHashにしてconfigファイルにしちゃうとかの用途にも使えるよ。たとえば/etc/hoge.confとかに

{
  :hoge_name => 'hogehoge',
  :hoge_param_1 => 5,
  :hoge_param_2 => 100,
  :hoge_param_3 => 5000,
}

とか設定ファイルを外出ししておいて、メインスクリプトでは

hoge_config = Hash.new

eval(File.open('/etc/hoge.conf').read).each {|key, value|
  hoge_config[key] = value
}

として読み込むようなスクリプトにすれば、ユーザーには/etc/hoge.confだけを変更してもらえば良くなるから便利だよ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060528

2006-05-27

[] 即席ホットスポットの作り方  即席ホットスポットの作り方 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  即席ホットスポットの作り方 - バリケンのRuby日記  即席ホットスポットの作り方 - バリケンのRuby日記 のブックマークコメント

今日Rubyの話題から離れて、Windows XPで即席ホットスポットを作る方法をまとめるよ。

ちょっとした1~2時間の勉強会とかで、3~4人でノートパソコンを持ち寄ったりしたときに、みんなでインターネットに接続しながら作業をしたいときがあるよね(ネットワークが必要なスクリプトを作る、とか)。

そんなとき、そのうちの一人でも「ウィルコムインターネット常時接続」が利用できる環境であれば、無線LANアドホック接続を利用することで、1台のインターネット接続をみんなで共有することが出来るよ。いわば「即席ホットスポット」だね。

即席ホットスポットを作るには、Windows XPの「インターネット接続の共有」という機能を使うよ。

まず、「即席ホットスポットアクセスポイント」にするノートパソコンの設定をするよ。まずはウィルコム携帯電話ノートパソコンとを、ケーブルでつなぐよ。

次に、そのノートパソコン無線LANの機能があれば、それを有効にしてね。もし無線LANの機能がないなら、無線LANPCカードUSB機器を購入して接続してね。

f:id:muscovyduck:20060527233907j:image

じゃあ、設定しよう。事前にプロバイダとの契約を済ませて、ウィルコム携帯電話ノートパソコンからインターネットに接続する設定は終わっていることを仮定するよ。ちなみにぼくは月々315円の「IIJmioモバイルアクセス」を利用しているよ。

(1)まずは「スタート」→「接続」→「ワイヤレス ネットワーク接続」を右クリックして、「プロパティ」を表示させるよ。「ワイヤレス ネットワーク」のタブを選んで、「追加」をクリックしてね。

f:id:muscovyduck:20060527231832p:image

ネットワーク名(SSID)」には「InstantHotSpot」を指定してね。それから「キーは自動的に提供される」のチェックをはずしてから、「ネットワーク認証」には「オープン システム」を、「データ暗号化」には「WEP」を、「ネットワーク キー」には適当な13文字の半角英数字を設定するよ。また、一番下の「これはコンピュータ相互(ad hoc)のネットワークで、ワイヤレス アクセス ポイントを使用しない」にもチェックを入れてね。

f:id:muscovyduck:20060527231830p:image

(2)次に「接続」タブの設定だよ。「このネットワークが範囲内にあるとき接続する」にチェックが入っていることを確認してね。

f:id:muscovyduck:20060527231829p:image

(3)「OK」を押したら、次は右下の「詳細設定」だよ。

f:id:muscovyduck:20060527231821p:image

アクセスするネットワーク」を「コンピュータ相互(ad hoc)のみ」にするよ。

(4)さいごに、「スタート」→「接続」→「(プロバイダの接続名)」を右クリックして、「プロパティ」を表示させるよ。いちばん右の「詳細設定」のタブをクリックしてね。「ネットワークのほかのユーザーに、このコンピュータインターネット接続をとおしての接続を許可する」にチェックを入れて、「ホームネットワーク接続」に「ワイヤレス ネットワーク接続」を設定するよ。

f:id:muscovyduck:20060527231820p:image

これで即席ホットスポットは完成だよ。「スタート」→「接続」→「(プロバイダの接続名)」をクリックして、「ダイヤル」をクリックしてインターネットに接続しておいてね。次は、この即席ホットスポットに接続するために他のノートパソコンも設定するよ。もちろん接続するためには、ノートパソコン無線LANの機能が必要だよ。もし無線LANの機能がついてないなら、無線LANPCカードUSB機器を購入して接続してね。

先ほどの(1)(2)(3)と同じことを、他のノートパソコンでも設定してね。「ネットワーク キー」に設定した13文字の半角英数字は、みんな一緒じゃないと通信できないから注意してね。

勉強会が終わったら、かならず今回作った接続は削除してね。削除するには、「スタート」→「接続」→「ワイヤレス ネットワーク接続」を右クリックして、「プロパティ」を表示させるよ。「ワイヤレス ネットワーク」のタブを選んで、「InstantHotSpot」をクリックしてから「削除」をクリックしてね。

あと、この即席ホットスポットを使い終わったら、かならず(3)で設定した「アクセスするネットワーク」を「利用可能なネットワークアクセス ポイント優先)に戻してね。忘れると、自宅の無線ネットワークにつながらなくなっちゃうからね。

即席ホットスポットにしたノートパソコンは、ネットワークの共有も解除してね。解除は、「スタート」→「接続」→「(プロバイダの接続名)」を右クリックして、「プロパティ」を表示させて、いちばん右の「詳細設定」のタブをクリックして、「ネットワークのほかのユーザーに、このコンピュータインターネット接続をとおしての接続を許可する」のチェックをはずしてOKをクリックしてね。

慣れるまでなかなか難しいかもしれないけど、何度かやっていると覚えてくるから、チャレンジしてみてね。ウィルコム携帯電話を利用している人は、即席ホットスポットを使って喫茶店とかでミニ勉強会を開いてみると面白いかもしれないよ。

追記:無線LANアダプタによっては「コンピュータ相互(ad hoc)のネットワーク」ではなくて「ワイヤレス アクセス ポイント」として動作する「ソフトAP機能」を持っている場合があるよ。その場合は、ここで書いた方法よりも簡単に即席ホットスポットが作れるみたいだよ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060527

2006-05-26

[] 勉強の方針どうしよう  勉強の方針どうしよう - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  勉強の方針どうしよう - バリケンのRuby日記  勉強の方針どうしよう - バリケンのRuby日記 のブックマークコメント

[] ActiveRecordを直接利用する  ActiveRecordを直接利用する - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  ActiveRecordを直接利用する - バリケンのRuby日記  ActiveRecordを直接利用する - バリケンのRuby日記 のブックマークコメント

あやうく関数型言語の魅力に取り付かれて、どこか遠いところに行っちゃうところだったよ。

ということで、リハビリのためRuby on RailsActiveRecordを生で触ってみることにするよ。

ActiveRecordRuby on Railsの一部で、リレーショナルデータベースRubyから利用するためのライブラリだよ。ActiveRecordを使うと、まるでオブジェクトを扱うかのようにリレーショナルデータベースアクセスすることができるよ。

じゃあ、ためしにこのあいだの日記で作ったusersテーブルにアクセスしてみることにするよ。

まずはデータベースへ接続するための「おまじない」だよ。

require '/usr/share/rails/activerecord/lib/active_record.rb'

ActiveRecord::Base.establish_connection(
  :adapter  => 'postgresql',
  :host     => 'localhost',
  :username => 'rubyonrails',
  :password => (設定したパスワード),
  :database => 'wdpress'
)

次に、usersテーブルにアクセスするためのUserクラスを生成するよ。

class User < ActiveRecord::Base
end

usersテーブルに、三つのレコードを追加してみるよ。

User.create(
  :name        => 'username1',
  :password    => 'password1'
)

User.create(
  :name        => 'username2',
  :password    => 'password2'
)

User.create(
  :name        => 'username3',
  :password    => 'password3'
)

じゃあ、逆側から2つの要素を取り出してみるよ。

User.find(:all, :limit => 2, :order => "id desc").each do |user|
  puts user.name
end

実行結果だよ。

username3
username2

次は、「user」という文字列がユーザー名に含まれているユーザーを出力するよ。

User.find(:all, :conditions => "name like '%user%'").each do |user|
  puts user.name
end

実行結果だよ。

username1
username2
username3

こんどは、ユーザーの数を数えてみるよ。2行目は「password3」というパスワードを設定しているユーザーの数を数えるよ。

puts User.count
puts User.count(['password = ?', 'password3'])

実行結果だよ。

3
1

最後に、ちょっと変わったメソッド名でアクセスしてみるよ。メソッド名が動的に定義されるんだねえ。

User.find_all_by_name('username3').each do |user|
  puts user.password
end

実行結果だよ。

password3

ActiveRecordを使うと、リレーショナルデータベースの知識がほとんどなくても、リレーショナルデータベースへ簡単にアクセスすることが出来るね。すごいなあ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060526

2006-05-25

[] Rubyの「遅延評価」  Rubyの「遅延評価」 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「遅延評価」 - バリケンのRuby日記  Rubyの「遅延評価」 - バリケンのRuby日記 のブックマークコメント

なかださん(って、「A Strolling Programmer」のなかださん?)から昨日のエントリコメントをいただきました。ありがとうございます!

なるほどRubyだと「Y-Combinator」を使わなくても、「遅延評価」を使えば再帰的な関数を手続きオブジェクトにできるんだね。

fact = lambda { 
  f = lambda {|n|
    if n.zero?
      1
    else
      n * f.call(n - 1)
    end
  }
}.call

「遅延評価」については、essaさんのこのエントリの説明がわかりやすいよ。

せっかく「遅延評価」が出てきたから、今日は「遅延評価」を使って昨日の日記とは別のアプローチでY-Combinatorを求めてみるよ。

昨日と同様、「nの階乗」を求める例だよ。まずは自分自身を再帰的に呼んでいる基本形。

fact = lambda {|n|
  if n.zero?
    1
  else
    n * fact.call(n - 1)
  end
}

fact.call(10)

次に、手続きオブジェクトの中から変数をなくすために、外側にもうひとつlambdaを追加するよ。

fact_maker = lambda {|fact|
  lambda {|n|
    if n.zero?
      1
    else
      n * fact.call(fact).call(n - 1)
    end
  }
}

fact_maker.call(fact_maker).call(10)

ここで、「再帰の定義部分」と「再帰の評価部分」を分離することを考えるよ。まずは「再帰の定義部分」をfとして書き出してみるよ。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

この「再帰の定義部分」をジッとながめてみよう。f自体は単なる関数オブジェクトだよね。

この関数を何とか外側から再帰的に呼び出すには、次のことをすればよさそうだよね。

  1. hに、q.call(q)となるような関数オブジェクトを送り込んでから
  2. fを呼ぶ

を、qを引数にとる関数として実装すれば、「再帰の評価部分」が定義できそうだよね。次のようになるよ。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

fact_maker = lambda {|q|
  f.call(
    q.call(q)
  )
}

fact_maker.call(fact_maker).call(10)

一見うまくいきそうだけど、これでは再帰にならないんだよ。再帰になるには

  1. hに、q.call(q)となるような関数オブジェクトを送り込んでから
  2. fを呼ぶ

という順番が大事なんだ。でもさっきのコードだと、q.call(q)がf.callよりも先に呼ばれてしまうんだよ。

なんとかq.call(q)を評価しないでfの中に送り込みたいよね。そこで先ほどの「遅延評価」というテクニックを使うよ。

実は、q.call(q)はlambda {|x| q.call(q).call(x) }と全く同じことなんだよ。実際に動かしてみて確認してみてね。

ただし、実際には「この手続きオブジェクトが評価されるまでは、q.call(q)が評価されない」という違いがあるよ。

この「遅延評価」を使うと、fの中にq.call(q)を送り込むことが出来るよ。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

fact_maker = lambda {|q|
  f.call(
    lambda {|m| q.call(q).call(m) } # q.call(q)と等価
  )
}

fact_maker.call(fact_maker).call(10)

あとはここまでくれば昨日と同じだよね。昨日と同じだとつまらないから、評価側を二回繰り返す書き方にしてみるよ。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

fact_maker = lambda {|q|
  f.call(
    lambda {|m| q.call(q).call(m) } # q.call(q)と等価
  )
}.call(
  lambda {|q|
    f.call(
      lambda {|m| q.call(q).call(m) } # q.call(q)と等価
    )
  }
)

fact_maker.call(10)

で、このfact_makerのfを引数として渡すようにしてメソッドにすると、Y-Combinatorの完成だね。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

def y_combinator(func)
  g = lambda {|q|
    func.call(
      lambda {|m| q.call(q).call(m) }
    )
  }.call(
    lambda {|q|
      func.call(
        lambda {|m| q.call(q).call(m) }
      )
    }
  )
  return g
end

fact = y_combinator(f)

fact.call(10)

「遅延評価」は関数型言語のテクニックだけど、このほかにも関数型言語にはいろんなテクニックがあるんだろうねえ。

こうしたテクニックって「デザインパターン」と一緒で、知らなければ知らないで多分きっと困ることはないと思うよ。

でも例えて言うなら「サッカーオーバーヘッドシュート」みたいなもので、使える場面は限られているけど、それができることでしか決められないゴールも確実にあって、それがゲームの結果を左右することもあるかもしれないよね。

だから最近、多くの人が関数型言語に熱をあげているのかも。

[] ザ・ゴール  ザ・ゴール - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  ザ・ゴール - バリケンのRuby日記  ザ・ゴール - バリケンのRuby日記 のブックマークコメント

「遅延評価」の「本当に必要になるまでは作業しないことで、全体としてのパフォーマンスは向上する」ということから、昔読んだ「ザ・ゴール」に書かれていた「制約条件の理論」を思い出したよ。

ザ・ゴール ― 企業の究極の目的とは何か

ザ・ゴール ― 企業の究極の目的とは何か

スティーブ・ジョブズ氏のスピーチ」にもあったけど、「だからこそバラバラの点であっても将来それが何らかのかたちで必ず繋がっていくと信じなくてはならない。」って本当かも。

Ruby勉強も、いつか役に立つといいなあ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060525

2006-05-24

[] 「再帰的な関数」を手続きオブジェクトにする  「再帰的な関数」を手続きオブジェクトにする - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  「再帰的な関数」を手続きオブジェクトにする - バリケンのRuby日記  「再帰的な関数」を手続きオブジェクトにする - バリケンのRuby日記 のブックマークコメント

以前のエントリで「Rubyでは手続きオブジェクトを使うと関数オブジェクトにできる」ということを書いたけど、ひとつ問題があるんだよ。

それは「再帰的に定義された関数」をオブジェクトにできないんだ。

たとえば、「nの階乗」を求める関数再帰的に定義してみるよ。

fact = lambda {|n|
  if n.zero?
    1
  else
    n * fact.call(n - 1)
  end
}

puts fact.call(10)

できたー!と言いたいところだけど、手続きオブジェクトの定義中に自分自身を呼んでるね。これではたとえば次のようなことをするとエラーになっちゃうよね。

fact0 = fact #=>このオブジェクトにfact0というラベルも貼り付ける
fact = nil   #=> factというラベルをはがした!
puts fact0.call(10) #=> factという名前で関数を呼べなくなったので、エラーになる

じゃあ、どうしたらいいのかな?もう一段階外側から手続きオブジェクト化すればいいかな?

fact = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

さて、これで変数を使わずに手続きオブジェクトにすることができたけど、これってどうやって再帰的に呼び出せばいいのかな。

呼び出すときにfact.call(fact).call(10)と書けば一見うまくいきそうだけど、n * h.call(n - 1)のところで問題になるよ。このhには、内側の手続きオブジェクト(lambda {|n| ... })じゃなくて外側の手続きオブジェクト(lambda {|h| ... })が渡されちゃうんだ。これじゃあ再帰にならないよ。

じゃあ、中のh.call(n - 1)をh.call(h).call(n-1)と書いたら、うまくh.call(h)に内側の手続きオブジェクトが渡されて再帰手続きになりそうじゃない?

じゃあ、やってみよう!

fact = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(h).call(n - 1)
    end
  }
}

puts fact.call(fact).call(10)

わーい、できた!と言いたいところだけど、この手続きオブジェクトを使うときに、必ずfact.call(fact).call(10)と二回呼び出さないといけないのは不便だよね。じゃあ、手続きオブジェクトを与えたら、手続きオブジェクト自身で評価した結果を返すメソッドを定義して、そのメソッドを使って再帰オブジェクトを生成すればいいかな?

def recursive_maker(func)
  g = func
  return g.call(g)
end

fact = recursive_maker(
  lambda {|h|
    lambda {|n|
      if n.zero?
        1
      else
        n * h.call(h).call(n - 1)
      end
    }
  }
)

puts fact.call(10)

やった!目的達成!ただ、h.call(h).call(n - 1)と二回繰り返しているところが惜しいよね。

h.call(n - 1)と書くと、内側の手続きオブジェクトのhに外側の手続きオブジェクトが渡されちゃうのが問題なんだよね。

recursive_makerを工夫したら、次のように書くことが出来るかな?

def recursive_maker(func)
  g = (funcが再帰処理されるように変更する)
  return g.call(g)
end

fact = recursive_maker(
  lambda {|h|
    lambda {|n|
      if n.zero?
        1
      else
        n * h.call(n - 1)
      end
    }
  }
)

puts fact.call(10)

とりあえず、わかりやすくするためにrecursive_makerの中で直接記述している手続きオブジェクトをfとして外に出すよ。

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

def recursive_maker(func)
  g = (funcが再帰処理されるように変更する)
  return g.call(g)
end

fact = recursive_maker(f)

puts fact.call(10)

うーん、この方法論だと煮詰まっちゃったね(evalを使う手もあるけど、汎用性がない)。これ以上進展がなさそうだから、とりあえず元の「二回呼んでいるバージョン」に戻るよ。

fact = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(h).call(n - 1)
    end
  }
}

puts fact.call(fact).call(10)

内側の手続きオブジェクトの中で2回呼んでいるところを、次のように書くと(内側にさらに手続きオブジェクトができちゃうけど)少なくとも一番内側の手続きオブジェクトはスッキリするよね。

fact = lambda {|q|
  lambda {|m|
    f = lambda {|h, n|
      if n.zero?
        1
      else
        n * h.call(n - 1)
      end
    }
    f.call(q.call(q), m)
  }
}

puts fact.call(fact).call(10)

fって外に出せそうだよね。

f = lambda {|h, n|
  if n.zero?
    1
  else
    n * h.call(n - 1)
  end
}

fact = lambda {|q|
  lambda {|m|
    f.call(q.call(q), m)
  }
}

puts fact.call(fact).call(10)

あれ?このfって、さっき煮詰まったほうのfと似てるよね。fの呼び出し側を工夫すれば、まったく同じに出来ない?

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

fact = lambda {|q|
  lambda {|m|
    f.call(q.call(q)).call(m)
  }
}

puts fact.call(fact).call(10)

なるほど、これをさっきの煮詰まったほうに応用すれば、recursive_makerメソッドが完成するんじゃないかな?

f = lambda {|h|
  lambda {|n|
    if n.zero?
      1
    else
      n * h.call(n - 1)
    end
  }
}

def recursive_maker(func)
  g = lambda {|q|
    lambda {|m|
      func.call(q.call(q)).call(m)
    }
  }
  return g.call(g)
end

fact = recursive_maker(f)

puts fact.call(10)

やったあ!これで再帰的な関数も、簡単に手続きオブジェクトにできるね。recursive_makerメソッドを使えば、「nの階乗」だけじゃなくて、その他の再帰的な関数も手続きオブジェクト化することができるよ。

で、ここで定義したrecursive_makerメソッドのことを「Y-Combinator」って呼ぶらしいよ。Y-Combinatorの実装方法はここで書いた方法以外にもいくつかあるみたいだから、興味がある人は調べてみてね。

[] ふつうのHaskellプログラミング  ふつうのHaskellプログラミング - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  ふつうのHaskellプログラミング - バリケンのRuby日記  ふつうのHaskellプログラミング - バリケンのRuby日記 のブックマークコメント

ちょっと浮気して、関数型言語勉強してみたくなったよ。

この本がすごくよさそう。Rubyレシピブック 268の技青木峰郎さんが著者なんだね。

なかだなかだ2006/05/24 09:44fact = lambda {_fact_ = lambda {|n| n.zero? ? 1 : n * _fact_.call(n - 1)}}.call

muscovyduckmuscovyduck2006/05/24 09:47なかださん>
コメントありがとうございます!なるほど、素晴らしいワンライナーですね!

2006-05-23

[] 図解「String#<<」メソッドの動作  図解「String#<<」メソッドの動作 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  図解「String#<<」メソッドの動作 - バリケンのRuby日記  図解「String#<<」メソッドの動作 - バリケンのRuby日記 のブックマークコメント

今日こちらのお題に図解で答えてみるよ!

>> a = 'aaa'
=> "aaa"
>> b = a
=> "aaa"
>> a = a << 'bbb'
=> "aaabbb"
>> b
=> "aaabbb"

じゃあ、最初の行から。

irb(main):001:0> a = 'aaa'
=> "aaa"

次のようなイメージになるよ。

f:id:muscovyduck:20060522092412p:image

次の行は、

irb(main):002:0> b = a
=> "aaa"

こんなイメージだね。

f:id:muscovyduck:20060522092408p:image

さて、問題の次の行。

irb(main):003:0> a = a << 'bbb'
=> "aaabbb"

ここは、「String#<<」メソッドの動作を理解する必要があるよ。このあいだの「String#+」メソッドは破壊的じゃなかったけど、こんどの「String#<<」メソッドは「破壊的メソッド」なんだ。

まず「a = a << 'bbb'」というのは「a = a.<<('bbb')」と書いたのと同じなんだよ。つまり、

  1. Stringクラスオブジェクトである"aaa"に対して、
  2. "bbb"というリテラル引数として、
  3. <<」というメソッドを実行すると、
  4. その結果オブジェクト破壊された(つまり"aaabbb"に変化した!)

という動作になるよ。だから、結果のイメージは次のとおりだね。

f:id:muscovyduck:20060523094010p:image

だから、次のような結果になるんだよ。

irb(main):004:0> b
=> "aaabbb"

昨日の「String#+」メソッドと比べてみてね。もっと詳しく知りたい人は、「Rubyレシピブック 268の技」の「レシピ60 文字列の末尾に文字列を追加する」で詳しい挙動が解説されているよ。

[] Object#object_idメソッド  Object#object_idメソッド - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Object#object_idメソッド - バリケンのRuby日記  Object#object_idメソッド - バリケンのRuby日記 のブックマークコメント

Rubyでは、オブジェクトが生成されると、それぞれのオブジェクトに対して固有のIDが振られるよ。

そして、オブジェクトに対してObject#object_idメソッドを使うと、そのIDを知ることができるよ。

これを利用すると、「String#+」メソッド「String#<<」メソッドの挙動の理解を深めることができるよ。


まずは「String#<<」メソッドだよ。

a = 'aaa'
a.object_id
b = a
b.object_id    #=> a.object_idと同じになる
a = a << 'bbb' #=> ちなみに a << 'bbb' とも書ける
a.object_id    #=> さっきと同じ
b.object_id    #=> やっぱり同じ
puts a
puts b

次に「String#+」メソッドだよ。

a = 'aaa'
a.object_id
b = a
b.object_id    #=> a.object_idと同じになる
a = a + 'bbb'  #=> ちなみに a += 'bbb' とも書ける
a.object_id    #=> 変わっている!
b.object_id    #=> こっちは変わっていない
puts a
puts b

図解と比較してみてね。

secondlifesecondlife2006/05/23 09:55わかりやすい図付き解説ですばらしいですね!

muscovyduckmuscovyduck2006/05/23 10:05secondlifeさん>
ありがとうございます!

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060523

2006-05-22

[] 図解「String#+」メソッドの動作  図解「String#+」メソッドの動作 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  図解「String#+」メソッドの動作 - バリケンのRuby日記  図解「String#+」メソッドの動作 - バリケンのRuby日記 のブックマークコメント

こちらの動作を図解で追ってみるよ!

irb(main):001:0> a="aaa"
=> "aaa"
irb(main):002:0> b=a
=> "aaa"
irb(main):003:0> a=a+"bbb"
=> "aaabbb"
irb(main):004:0> b
=> "aaa"
irb(main):005:0> puts b
aaa
=> nil

じゃあ、最初の行から。

irb(main):001:0> a="aaa"
=> "aaa"

次のようなイメージになるよ。

f:id:muscovyduck:20060522092412p:image

次の行は、

irb(main):002:0> b=a
=> "aaa"

こんなイメージだね。

f:id:muscovyduck:20060522092408p:image

さて、問題の次の行。

irb(main):003:0> a=a+"bbb"
=> "aaabbb"

ここは、「String#+」メソッドの動作を理解する必要があるよ。a=a+"bbb"というのは、a=a.+("bbb")と書いたのと同じことになるんだよ。つまり、

  1. Stringクラスオブジェクトである"aaa"に対して、
  2. "bbb"というリテラル引数として、
  3. 「+」というメソッドを実行すると、
  4. その結果オブジェクトが生成され、(つまり"aaabbb")
  5. そのオブジェクトにaというラベルを(先ほどのオブジェクトからはがして)貼り付ける

という動作をしているよ。その結果は、次のようなイメージになるよ。

f:id:muscovyduck:20060522092407p:image

だから、次のような結果になるんだよ。

irb(main):004:0> b
=> "aaa"

irb(main):005:0> puts b
aaa
=> nil

ついでにaの内容も見てみると、次のようになるよ。

irb(main):006:0> a
=> "aaabbb"
irb(main):007:0> puts a
aaabbb
=> nil

あと、すべてのラベルがはがされたオブジェクトは、自動的にメモリ上から削除されるよ(ガーベージコレクションって言うんだって)。

上記の例だと、(図には描かなかったけど)実は"bbb"というオブジェクトが一瞬だけ生成されているんだけど、ラベルを貼っていないからすぐに消えてしまったんだよ。

[] Rubyの「破壊的メソッド」  Rubyの「破壊的メソッド」 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「破壊的メソッド」 - バリケンのRuby日記  Rubyの「破壊的メソッド」 - バリケンのRuby日記 のブックマークコメント

Rubyインスタンスメソッドのうち、オブジェクト自身の内容を変えてしまうようなものを「破壊的メソッド」と呼ぶよ。多くの破壊的メソッドには、メソッド名の最後に「!」が付いているよ。

例として、破壊的じゃない「String#chop」と、破壊的な「String#chop!」の違いを図解で追ってみるよ。まずは破壊的じゃないメソッド「String#chop」の例だよ。

a = "abc"
b = a.chop
puts a
puts b

まずは、最初の行。

a = "abc"

こういうイメージだね。

f:id:muscovyduck:20060522114055p:image

そして、問題の次の行。

b = a.chop

これは、次のような動作をしているよ。

  1. Stringクラスオブジェクトである"abc"に対して、
  2. chop」というメソッドの実行を依頼すると、
  3. その結果オブジェクトが生成され、(つまり"ab")
  4. そのオブジェクトにbというラベルを貼り付ける

その結果のイメージは、次のようになるよ。

f:id:muscovyduck:20060522114053p:image

だから、このスクリプトの実行結果は次のようになるんだね。

abc
ab

次は、破壊的なメソッド「String#chop!」の例だよ。

a = "abc"
b = a.chop!
puts a
puts b

最初の行は、さっきと同じだよね。

a = "abc"

こういうイメージだね。

f:id:muscovyduck:20060522114055p:image

そして、いよいよ次の行。

b = a.chop!

これは、次のような動作をしているよ。

  1. Stringクラスオブジェクトである"abc"に対して、
  2. chop!」というメソッドの実行を依頼すると、
  3. その結果オブジェクト自身が破壊され、("ab"に変化した!)
  4. そのオブジェクトにbというラベルも貼り付ける

その結果のイメージは、次のようになるよ。

f:id:muscovyduck:20060522114054p:image

なので、このスクリプトの実行結果は、次のようになるよ。

ab
ab

[] オブジェクトクラスについて  オブジェクトとクラスについて - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  オブジェクトとクラスについて - バリケンのRuby日記  オブジェクトとクラスについて - バリケンのRuby日記 のブックマークコメント

Rubyオブジェクトは、必ず何らかの「クラス」から生成されているよ。そのオブジェクトの生成元のクラスを「所属クラス」と呼ぶよ。

そして一度生成されたオブジェクトは、消滅するまで所属する「クラス」が変わることはないよ。

たとえば文字列(Stringクラス)として生成されたオブジェクトは、消滅するまで所属クラスStringクラスから変わることはないよ。String#to_iを使って

a = '100'
a = a.to_i

と書いた場合、aがStringクラスからFixnumクラスに変化したように見えるかもしれないけど、実は

  1. Stringクラスオブジェクトである"100"に対して、
  2. to_i」というメソッドの実行を依頼すると、
  3. その結果Fixnumクラスオブジェクトが生成され、(つまり100)
  4. そのオブジェクトにaというラベルを貼り付ける("100"に貼られていたラベルを張り替える)
  5. ラベルをはがされた"100"は、ガーベージコレクションによってメモリから消滅する

という動作をしているよ。

イメージとしては、次のようになるよ。

a = '100'

f:id:muscovyduck:20060522144328p:image

a = a.to_i

f:id:muscovyduck:20060522144327p:image

あと、Stringクラスから生成されたオブジェクトのことを「Stringクラスインスタンス」と呼ぶみたいだよ。「インスタンスとは、オブジェクトのこと」という理解で、たぶん大丈夫だと思うよ。

メソッドの戻り値として生成されたオブジェクトや、リテラルとしてオブジェクトを生成した場合、生成元のクラス名がわからないときがあるよね。そういうときは、Object#classメソッドで調べることが出来るよ。たとえば次のように書くと、

a = 'Hello, world!'
puts a.class
a = 123
puts a.class
a = [ 1, 2, 3 ]
puts a.class
a = /abc123/
puts a.class
a = { 'a' => 1, 'b' => 2, 'c' => 3 }
puts a.class
a = :helloworld
puts a.class
a = a.class
puts a.class

実行結果は次のようになるよ。

String
Fixnum
Array
Regexp
Hash
Symbol
Class

2006-05-21

[] Rubyの「変数」再考  Rubyの「変数」再考 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「変数」再考 - バリケンのRuby日記  Rubyの「変数」再考 - バリケンのRuby日記 のブックマークコメント

以前の日記で『Rubyの変数は「オブジェクトに貼り付けるラベル」』というエントリを書いたけど、もういちど復習するよ。

他のプログラミング言語をすでに覚えた人は、たいてい『変数とは、値を入れておく「入れ物」のこと』と習っていると思うんだ。

でも、Rubyでは違うんだよ。「変数」は「オブジェクトに貼り付けておくラベル」なんだよ。

イメージとしては、次のような感じだよ。

f:id:muscovyduck:20060521220153p:image

たとえば、

foo = "Hello, world!"

と書いたときは、Stringオブジェクトにfooというラベルを貼り付けたことになるんだよ。イメージとしては、次のようになるよ。

f:id:muscovyduck:20060521220152p:image

そして、貼り付けたラベルをコード中で記述すると、そのオブジェクトを指し示したことになるんだよ。だから

foo = "Hello, world!"
puts foo

と書いたとき、「puts foo」は「foo」が指し示しているオブジェクトを「puts」する、という意味になるんだよ。

もうひとつ面白い例として、次のように書くと、

foo = "Hello, world!"
bar = foo

次のようなイメージになるんだよ。

f:id:muscovyduck:20060521220151p:image

bar = foo」の右側のfooは、fooというラベルが貼り付けられているオブジェクトのことを示しているから、そのオブジェクトbarというラベルも貼り付ける、という意味になるんだよ。

だから、次のように書くと、

foo = "Hello, world!"
bar = foo
bar.chop!
puts foo
puts bar

実行結果は次のようになるよ。

Hello, world
Hello, world

ひとつのオブジェクトに二つのラベルを貼り付けても、実体のオブジェクトはひとつだよ。だからbar.chop!(オブジェクトの最後の一文字を削除する)とすると、fooで参照してもやっぱり最後の一文字が削除されているんだよね。

同じ内容のオブジェクトコピーして別のラベルを貼り付けたいというときは、Object#cloneを使うよ。たとえば

foo = "Hello, world!"
bar = foo.clone

と書くと、fooというラベルが貼り付けられているオブジェクトコピーして、新たに増えたオブジェクトbarというラベルを貼り付ける、という意味になるよ。

イメージとしては、次のようになるよ。

f:id:muscovyduck:20060521222221p:image

だから、次のように書けば、

foo = "Hello, world!"
bar = foo.clone
bar.chop!
puts foo
puts bar

実行結果は次のようになるよ。

Hello, world!
Hello, world

こんどはfooが貼り付けられたオブジェクトbarが貼り付けられたオブジェクトは別物だから、bar.chop!とやってもfooの貼り付けてあるオブジェクトには変化がなかったんだね。

あと、「オブジェクト変数を貼り付けること」を「代入」と呼ぶみたいだよ。これは他のプログラミング言語変数は「入れ物」だから「代入」)の表現と合わせるためにそう呼んでいるだけで、あくまでもRubyでは「オブジェクト変数を貼り付けること」と脳内で変換してね。

Rubyの「変数」は「オブジェクトに貼り付けておくラベル」だというイメージ、つかめたかな?

mr-80bmr-80b2006/05/21 22:52目からウロコ。

mr-80bmr-80b2006/05/21 22:53非常によくわかりました。すばらしい解説ありがとうございます。

muscovyduckmuscovyduck2006/05/21 23:12id:mr-80bさん:
コメントありがとうございます!反応いただけると励みになります!

2006-05-20

[] Rubyの「イテレータ」再考  Rubyの「イテレータ」再考 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「イテレータ」再考 - バリケンのRuby日記  Rubyの「イテレータ」再考 - バリケンのRuby日記 のブックマークコメント

以前Rubyで「イテレータ」というエントリを書いたけど、もういちど復習するよ。

「手続きオブジェクト」の使い道として、おとといの「無名関数」昨日の「クロージャ」のほかに、「イテレータの定義」というものがあるよ。

メソッド定義中に「ブロックを指定せずにProc.newで手続きオブジェクトを生成」するようにメソッドを定義すると、そのメソッドを実行するときにブロックを与える「ブロック付きメソッド」として定義することが出来るよ。

じゃあ、Arrayクラス継承したMyArrayクラスに、配列の各要素に「与えたブロックの手続きを実行する」ブロック付きメソッドmy_iteratorを定義してみるよ。

class MyArray < Array

  def my_iterator
    i = 0
    pr = Proc.new
    while i < self.size
      pr.call self[i]
      i += 1
    end
  end
end

fruits = MyArray.new

fruits.push('りんご')
fruits.push('みかん')
fruits.push('いちご')

fruits.my_iterator {|fruit| puts fruit }

実行結果だよ。

りんご
みかん
いちご

配列の各要素に対して、与えられたブロックが実行されていくのがわかるよね。

でもpr = Proc.newとしたあとにループの中でpr.callとするのはちょっとカッコ悪いよね。この「まず最初に手続きオブジェクトを生成してから、ループ内とかでcallする」という場合、yieldを使って次のように書くことも出来るんだ。

class MyArray < Array

  def another_my_iterator
    i = 0
    while i < self.size
      yield self[i]
      i += 1
    end
  end
end

たぶんProc.newは「手続きオブジェクト」というものの理解を助けるための表現で、いちど「手続きオブジェクト」について理解できたら、次からはProc.newを直接使わないでyieldを使うようにするとRubyistっぽくてちょっとカッコイイかも。

ついでに言うと、クロージャを作りたいときはproc、さらに無名関数であることを強調したいときはlambda、と使い分けるともっとカッコイイかも。

余談1:

配列のように「複数の要素からなるもの」に対して、その各要素に一連の手続きを繰り返し実行することを「イテレータ」と言うみたいだよ。すべての「ブロック付きメソッド」を「イテレータ」と呼んでいることが多いけど、混乱の元だからぼくはお勧めしないよ。「イテレータ」は「繰り返し」をしているときにだけ使って、繰り返さないときは単に「ブロック付きメソッド」と言うほうが混乱しないと思うよ。

余談2:

ちなみにArray#pushは「<<」とも書けるよ。

fruits << 'りんご'
fruits << 'みかん'
fruits << 'いちご'

次のようにも書けるよ。

fruits << 'りんご' << 'みかん' << 'いちご'

2006-05-19

[] Rubyの「クロージャ」再考  Rubyの「クロージャ」再考 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「クロージャ」再考 - バリケンのRuby日記  Rubyの「クロージャ」再考 - バリケンのRuby日記 のブックマークコメント

以前Rubyで「クロージャ」というエントリを書いたけど、もういちど復習するよ。

昨日の日記で「ブロック」の概念と、「ブロック」をオブジェクト化した「手続きオブジェクト」について復習したけど、この「手続きオブジェクト」には面白い性質があるんだ。

というのは、「手続きオブジェクト」の中から変数を使って別のオブジェクトを参照するような場合、その手続きオブジェクトを生成した「環境」の中の変数は、その「手続きオブジェクト」が生きている間はずっと有効なんだよ。

たとえば

def counter_closure
  count = 0
  f = lambda {|n| count += n }
  return f
end

count = 10
counter = counter_closure
puts counter.call(1)
puts counter.call(2)
puts counter.call(3)
puts counter.call(4)

のように書くと、実行結果は

1
3
6
10

のようになるよ。counter_closureのメソッド定義内で「count = 0」としてFixnumオブジェクトを参照している変数countは、手続きオブジェクトが生きている間は(手続きオブジェクトの中で)ずっと有効だよ。だから、手続きオブジェクトの外で「count = 10」としても、それは別の変数になるから「手続きオブジェクト内の変数count」の参照先は変わらないんだよ。

こんな風に『「手続きオブジェクトの内部でのみ有効な変数」を包み込んで持っている手続きオブジェクト』のことを、「クロージャ」と呼ぶみたいだよ。

tanii-takamunetanii-takamune2008/10/23 08:04クロージャーについて、よくわからなかったのでバリケンの記事とても参考になりました。ありがとうございます。

2006-05-18

[] Rubyの「ブロック」再考  Rubyの「ブロック」再考 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「ブロック」再考 - バリケンのRuby日記  Rubyの「ブロック」再考 - バリケンのRuby日記 のブックマークコメント

以前Rubyの「手続きオブジェクト」というエントリを書いたけど、「ブロック」はRubyキモだから、もういちど復習するよ。

ブロック」と「手続きオブジェクト

Rubyには「コードの塊(かたまり)」「コードブロック」あるいは単に「ブロック」と呼ばれるものがあるよ(全部同じことを言っているよ。以下「ブロック」で統一するよ)。「ブロック」は「{」と「}」で囲むか、「do」と「end」で囲むことで表現できるよ。

たとえば

{
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
}

とか、

do
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
end

とかを「ブロック」と呼ぶよ。

ただし「ブロック」はリテラルじゃないから、単にブロックだけをスクリプトに書いてもエラーになるよ。

そして「ブロック」は、Procクラスインスタンスとしてオブジェクト化することができるよ。たとえば

hello = Proc.new {
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
}

とか、

hello = Proc.new do
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
end

とかだね。あと、こちらにあるとおり、Proc.newproclambdaとも書くことができるよ。たとえば

hello = proc {
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
}

とか、

hello = lambda {
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
}

だね。そして手続きオブジェクトに対してcallメソッドを依頼すると、ブロックの中身を実行できるよ。

hello = lambda {
  puts 'Hello, world!'
  puts 'Hello, world!'
  puts 'Hello, world!'
}

hello.call

実行結果だよ。

Hello, world!
Hello, world!
Hello, world!

ブロックパラメータ

ブロックは、パラメータをつけることで「手続き」だけでなく「関数」のようにふるまうことができるよ。パラメータは「|」で定義するよ。

パラメータに2つの値を与えられたら、その二つを足し算するブロック」は、次のようになるよ。

{|x, y| x + y}

もちろん、Proc.new(やproclambda)で「手続きオブジェクト」にできるよ。

sum = lambda {|x, y| x + y}

「手続きオブジェクト」を使って関数計算させるには、パラメータに値を渡してcallしてあげればいいみたいだよ。

puts sum.call(2, 3)

Proc.new(やproclambda)で作った「パラメータ付きブロック」はオブジェクトだから、まるで「名前のない関数」のようだよね(変数名は便宜的につけているラベルなので、オブジェクトの名前ではない)。

ぼくは関数型言語のことはよく知らないけど、関数型言語で言う「無名関数」っていうのは、きっと「手続きオブジェクト」のようなものなんだろうねえ。

2006-05-17

[] Hello, world!  Hello, world! - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Hello, world! - バリケンのRuby日記  Hello, world! - バリケンのRuby日記 のブックマークコメント

今までRuby勉強自分の日記に書いていたけど、今日からはこちらに書くことにしたよ!よろしくね。

最初はやっぱりHello, world!だよね。

puts 'Hello, world!'

printだと改行してくれないので、文字列の最後に\nをつけるよ。

print "Hello, world!\n"

オブジェクト指向っぽく、文字列リテラルに対してdisplayメソッドで表示させてみるよ。

"Hello, world!\n".display

Rubyらしく、無駄ブロック付きメソッドを使ってみるよ。

"Hello, world!\n".each_byte do |i|
  printf "%c", i
end

最後に、例外処理の練習をしてみるよ。

begin
  helloworld
rescue
  puts "Hello, world!"
end

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20060517