rubyのProcとは?ブロックとの違いは?

このQ&Aのポイント
  • rubyでは、再利用するためのコードとしてProcが使われます。Procは、blockと異なり保存することができます。ブロックは保存できないため、同じblockを何度も使いたい場合にはProcを使用します。
  • lambdaはメソッドと同じように振る舞い、returnを使用することができます。一方、Procではreturnを使用するとエラー処理が行われず、なんらかの出力がされてしまいます。
  • また、関数名の前にコロン(:)が付く記述は、メソッドや関数を参照するための記法です。この場合、method(:square)はsquareというメソッドを参照しています。
回答を見る
  • ベストアンサー

rubyでのproc

rubyでのproc http://d.hatena.ne.jp/shunsuk/20090101/1230816826 ここのサイトで解説されていることなんですけど, 「同じblockを何度も使いたいときがあります。そんなときに再利用するためのコードがProcです。blockとProcの唯一の違いは、blockは保存できないということです。Procについて見てみましょう。」 とあるのですが,なにをもってblockは保存できないといってるのでしょうか? 「lambdaはメソッドと同じように振る舞いますから、returnを使うことができます。この意味の違いは、下の例に現れます。」 の下にサンプルプログラムがあると思いますが, puts generic_return(Proc.new { |x, y| x + 2; y + 2 }) puts generic_return(Proc.new { |x, y| [x + 2, y + 2] }) この2行ではエラー処理が行われず,なんらかの出力がされています, これはなぜでしょうか?メソッドの中にreturnがある限りエラー処理が起こると思っているのですが・・・ あと,あまり関係ない質問になるかもしれませんが array.iterate!(method(:square)) このような記述のとき,squareは関数名ということはわかるのですが, :(コロン)これはなにを指すのでしょうか? よろしくおねがいします.

  • Ruby
  • 回答数4
  • ありがとう数5

質問者が選んだベストアンサー

  • ベストアンサー
  • siffon9
  • ベストアンサー率64% (136/211)
回答No.2

> なにをもってblockは保存できないといってるのでしょうか? Lambdaの項の最初に“Procを2通りの方法で使ってきました。直接渡す方法と、変数に保存する方法です。”という記述があります。 保存とは変数に保存するということの様です。Procのインスタンスは変数に保存して使い回しができますが、ブロックはできないということではないでしょうか。 > この2行ではエラー処理が行われず,なんらかの出力がされています, > これはなぜでしょうか?メソッドの中にreturnがある限りエラー処理が起こると思っているのですが・・・ メソッドの中ではなくて、Proc.new{ }の中にreturnがある場合ですね。 この中にreturnがあると、そのProcが定義されたレベルからのreturnを行おうとするようです、このサンプルだとProcはプログラムのトップレベルで定義されているので、トップレベルからreturnしようとしてエラーになっている様です。 要するにProcを使用する場合はreturnを使うな、returnを使う場合はlambdaを使えということではないでしょうか。

その他の回答 (3)

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.4

はまりやすいところですが、proc組み込み関数はくせ者です。 Ruby1.8ではproc組み込み関数は、lambdaを返します。 Proc.new{。。。。} # => procオブジェクト lambda {。。。。} # => lambdaオブジェクト(クラスはProc) proc {。。。。} # => lambdaオブジェクト(クラスはProc)!!!!!注意 Ruby1.9ではproc組み込み関数の機能が変更されています。 Proc.new{。。。。} # => procオブジェクト lambda {。。。。} # => lambdaオブジェクト(クラスはProc) proc {。。。。} # => procオブジェクト #3の方は、Ruby1.9をお使いと思われます。

回答No.3

#解説は他の方に任せてサンプルのみ。 #ブロックを返す単純なメソッドを定義 def make_proc1(&b) b end #別の例 def make_proc2 proc end #ブロック保存もどきが作れる。 p= make_proc1{puts "proc test"} p.call p= make_proc2{puts "proc test"} p.call #が以下のように直接は保存できない。(blockは保存できないにあたる?) p = {puts "proc test"} return #トップレベルのreturn(※1)は戻り先が無いのでLocalJumpError p = proc{ p "proc test";return}#トップレベルのreturn(※1)を含むproc作成。 p.call #これもップレベルのreturn(※1)と一緒LocalJumpError def proc_test return #メソッド内のreturn(※2)。nilを返してproc_testを終了する。 p "fin proc_test" #これは実行されない。 end def proc_test p = proc{ p "proc test";return}#proc_testのreturn(※2)と一緒。 p.call #proc_testのreturn(※2)と一緒なのでLocalJumpErrorはおきない。 p "fin proc_test" #これは実行されない。 end def proc_test(b) b.call #トップレベルのreturn(※1)を含むprocの場合、戻り先が無いのでLocalJumpError p "fin proc_test" #これは実行されない。 end proc_test(proc{ p "proc test";return})#トップレベルでreturn(※1)を含むprocを渡す。 def sub_proc #sub_procからのreturnだが、呼び出す際にはsub_procは #すでに終了しているのでエラー。 proc{ p "proc test";return} end proc_test sub_proc

penpower
質問者

補足

#!/usr/bin/ruby p = proc{ p "proc test";return}#トップレベルのreturn(※1)を含むproc作成。 p.call #これもップレベルのreturn(※1)と一緒LocalJumpError これを実行したところ, "proc test" と出力されました. #!/usr/bin/ruby def proc_test p = proc{ p "proc test";return}#proc_testのreturn(※2)と一緒。 p.call #proc_testのreturn(※2)と一緒なのでLocalJumpErrorはおきない。 p "fin proc_test" #これは実行されない。 end としたところ,proc testが出力されそうですが,なにも出力されませんでした. #!/usr/bin/ruby p = proc{ p "proc test";return}#トップレベルのreturn(※1)を含むproc作成。 p.call #これもップレベルのreturn(※1)と一緒LocalJumpError これを実行したところ, "proc test" と出力されました. #!/usr/bin/ruby def proc_test(b) b.call #トップレベルのreturn(※1)を含むprocの場合、戻り先が無いのでLocalJumpError p "fin proc_test" #これは実行されない。 end proc_test(proc{ p "proc test";return})#トップレベルでreturn(※1)を含むprocを渡す。 これを実行したところ, "proc test" "fin proc_test" と出力されました. #!/usr/bin/ruby def proc_test(b) b.call #トップレベルのreturn(※1)を含むprocの場合、戻り先が無いのでLocalJumpError p "fin proc_test" #これは実行されない。 end def sub_proc #sub_procからのreturnだが、呼び出す際にはsub_procは #すでに終了しているのでエラー。 proc{ p "proc test";return} end proc_test sub_proc これを実行したところ, "proc test" "fin proc_test" と出力されました. LocalJumpErrorrubyのバージョンにもよるのでしょうか?特にエラーは出力されませんでした. ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux] また,実行時には ruby -W test.rb と警告をつけて実行しています.

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.1

>なにをもってblockは保存できないといってるのでしょうか? 原文は見てませんが、翻訳を見る限り、この文脈で「保存」とはどういう意味なのか定義されてません。従って意味不明の文章です。 あえて想像すると、「変数に直接代入できない」という意味ではないでしょうか。別の所に書いてありますが、呼び出され側のメソッドで、&が付いた仮引数で受け取ることは出来るので、変数には代入できるのですが、直接ではありません。 a = {|x| x*2} ということは出来ないので。 >これはなぜでしょうか?メソッドの中にreturnがある限りエラー処理が起こると思っているのですが・・・ 何故かと問われても、「正しいプログラムだから」としか言いようがないです。逆に何故「エラー処理が起こる」と思ったのでしょうか? >このような記述のとき,squareは関数名ということはわかるのですが,:(コロン)これはなにを指すのでしょうか? 単に square と書くと、square というメソッドを呼び出してその結果を表します。 ここでは、呼び出さずにメソッドの名前を伝えたいわけです。:square はシンボルというもので、簡単には説明できませんので、理解できるまでは『「書き換えられず部分取り出しも出来ない文字列」のようなもの』と思っておけば良いです。実際、 array.iterate!(method("square")) と書いても同じ結果です。文字列よりシンボルの方が処理効率が良く、またシンボルしか書けないところもあります。

関連するQ&A

  • 【ruby】無名関数?でブロック付きメソッドって作れませんか?

    Proc.new do|arg|  arg.each do |x|   puts x  end end.call([1,2,3,4,5]) 上記のようなものを無名関数と理解しているのですが、 これを下記の様にすると、 Proc.new {|arg|  arg.each do |x|   yield(x)  end }.call([1,2,3,4,5]) do |x|  puts x end no block given とエラーになってしまいます。 無名関数で、ブロック付きメソッドって作れないものでしょうか? ご指導の程、宜しくお願いいたします。

    • ベストアンサー
    • Ruby
  • Ruby の 文法に困ってます。

    以下のようなソースの場合、to_sメソッドは何時呼ばれているのでしょうか。またどういう意味を成しているのでしょうか。 Ruby初心者のためご教授頂けないでしょうか。 よろしくお願いします。 class Vector   attr_accessor :x, :y   def initialize(x=0, y=0)     @x = x     @y = y   end   def length     Math::sqrt(@x ** 2 + @y ** 2)   end   def to_s      "(#{@x},#{@y})"   end   def <=> other     length <=> other.length   end end arr =[] arr << Vector.new(2,2) arr << Vector.new(3,2) arr << Vector.new arr.sort.each do |item|   puts item end 結果は以下になります。 (0,0) (2,2) (3,2)

    • ベストアンサー
    • Ruby
  • 【ruby】二つのブロック付きメソッドをif文で使い分けたい

    二つのブロック付きメソッドhogeとpiyoがあるとします。 ブロックの中に記述するプログラムは同じだが、hogeを使うか、piyoを使うかは条件により制御したいです。 このような場合、どのように書いたら簡素になるでしょうか? sw=true if sw then  hoge() do |x|   puts x  done else  piyo() do |x|   puts x  done end 上記例では冗長の思うのですが、ご指導のほど宜しくお願いいたします。

    • ベストアンサー
    • Ruby
  • 【ruby】【文法?】ブロックをbreakした時。。

    質問を見ていただいて有難うございます。 質問を一言で言うと、 「メソッドの中で呼び出し元がbreakを使った事を検知できるか?」 となるのでしょうか。。。以下に詳しく質問を記述いたします。 引数に配列を渡すと、その配列をブロックに一つずつ返してくれる メソッドhoge()があるとします。 以下の様に使います。 ------------------------------------- hoge([0,1,2]) do |x|  puts x end 実行結果 0 1 2 ------------------------------------- このhoge()は、実行中にエラーが発生した場合、トラップして falseを返す事とします。(何事もなければtrueを返します。) このhoge()を以下の様に書きました。 def hoge(arg)  begin   arg.each do |x|    yield x   end  rescue   false  else   true  end end 以下の様に使います。 ------------------------------------- ret=hoge([0,1,2]) do |x|  puts x end puts ret ? 'success' : 'fail' 実行結果 0 1 2 success ------------------------------------- ------------------------------------- ret=hoge(nil) do |x|  puts x end puts ret ? 'success' : 'fail' 実行結果 fail ------------------------------------- ここまでは、よかったのですが、hoge()のブロックの中で、breakを使うと hoge()の戻り値はnilになってしまいます。 ------------------------------------- ret=hoge([0,1,2]) do |x|  break if x==1  puts x end puts ret puts ret ? 'success' : 'fail' 実行結果 0 nil fail ------------------------------------- ここで質問です。 最後の例は、hoge()として異常系ではないので、retにtrueを与えたいのですが、どうしたらよいでしょうか? ご指導のほど、宜しくお願いいたします。

    • ベストアンサー
    • Ruby
  • Rubyのプログラム

    今、サンプルとしてこれがあるんですけど、これは交点を出力するものなんですが、線を出力するにはどこを変えればよいですか?? Xみたいに線を描きたいです。 class Ten def initialize(x, y) @x = x # インスタンス(@がついているもの)変数に代 入 @y = y # インスタンス(@がついているもの)変数に代入 end attr_accessor :x, :y end class Sen def initialize(p1, p2) @p1 = p1 @p2 = p2 @a = (p2.y - p1.y)/(p2.x - p1.x) @b = -@a * p1.x p1.y end attr_accessor :p1 # 始点 attr_accessor :p2 # 終点 attr_accessor :a # 傾き attr_accessor :b # Y軸との交点 end def koten(l1, l2) x = (l1.b - l2.b)/(l2.a - l1.a) y = (l1.a * l2.b - l1.b * l2.a)/(l1.a - l2.a) puts x, y end p1 = Ten.new(0.0, 0.0) p2 = Ten.new(6.0, 4.0) p3 = Ten.new(0.0, 4.0) p4 = Ten.new(6.0, 0.0) l1 = Sen.new(p1, p2) l2 = Sen.new(p3, p4) koten(l1, l2)

  • requireでエラーが起きてしまう

    requireでエラーが起きてしまう タイトルのとおり、requireの箇所でエラー(no such file to load)が起きてしまって先に進めない状態なので、どなたか解決策がわかるかたいらっしゃいましたら教えてください>< 今、 method_area.rb -- def triangle(x, y) return (x * y / 2) end def square(x) return x**2 end -- main.rb -- require 'method_area.rb' puts(triangle(8, 24)) puts(square(15)) -- の2つのファイルを同じディレクトリに置いています。

    • ベストアンサー
    • Ruby
  • Rubyのルンゲクッタ法がうまくいきません

    Rubyでルンゲクッタ法でy=exp(x**2)と比較しようという問題で、下のようなプログラムを組むとyの値がかなり大きくなってしまいます。どこが間違っているのでしょうか。教えてください。 #! ruby -Ks #ルンゲックッタ def df(a,b) z=2*a*b return z end n=10 x=Array.new(n) y=Array.new(n) x[0]=0.0 y[0]=1.0 h=1.0/n #任意で変更 fw=File.open("output2.txt","w") for i in 0..n k1=df(x[i],y[i])*h k2=df(x[i]+h/2,y[i]+k1/2)*h k3=df(x[i]+h/2,y[i]+k2/2)*h k4=df(x[i]+h,y[i]+k3) k=(k1+2*k2+2*k3+k4)/6 x[i+1]=x[i]+h y[i+1]=y[i]+k fw.print x[i]," ",y[i]," ","\n" end fw.close()

    • ベストアンサー
    • Ruby
  • Ruby RSSの文字置換について

    RubyでRSSリーダーを作成しているのですが、 読み込んだRSSのエンコード(utf-8)とプログラムのエンコード(cp932)が異なるため、 エラーが発生しました。 <プログラム> ------------------------------------------------------- # encoding: cp932 require 'open-uri' require 'rss' xml = nil open("http://codezine.jp/rss/new/20/index.xml") { |http|    xml = http.read } rss = RSS::Parser.parse(xml.force_encoding("utf-8")) puts "#{rss.channel.title.encode('cp932', 'utf-8')}"\ + "#{rss.channel.pubDate.strftime("%Y/%m/%d %X")}" rss.items.each do |item|    puts '--------------------------'    puts item.title.encode('cp932', 'utf-8')    puts item.pubDate.strftime("%Y/%m/%d %X")    puts item.description.encode('cp932', 'utf-8') end ------------------------------------------------------ <エラーメッセージ> ------------------------------------------------------ rss_r.rb:18:in `encode': U+2013 from UTF-8 to Windows-31J (Encoding::UndefinedConversionError) ------------------------------------------------------ 以下のサイトを参考にして文字を置換しようと思ったのですが、 http://qiita.com/yugo-yamamoto/items/0c12488447cb8c2fc018 rssはStringクラスではないため、trメソッドが使えません。 どのようにすれば文字を置換できるのか教えていただけますでしょうか。

    • ベストアンサー
    • Ruby
  • rubyでScalaのようにコールバックを変数として定義するには?

    rubyでScalaのようにコールバックを変数として定義するには? 最近Scalaの勉強を始めました。自分の一番好きな言語はrubyなので、比較しながら勉強してます。 参考書によると、関数をオブジェクトとして扱えるのがScalaの特徴との説明がありました。例えば、以下のような例なのですが、(行頭スペースは全角) class Kuku(a:Int,b:Int){  def calc(func:(Int,Int)=>String):String={   return func(a,b)+a*b  } } val f1=(a:Int,b:Int)=>a+"*"+b+"=" println(new Kuku(3,5).calc(f1)) これをrubyで書くと、(行頭スペースは全角) class Kuku  def initialize(x,y)   @x=x   @y=y  end  def calc   yield(@x,@y)+(@x*@y).to_s  end end puts Kuku.new(3,5).calc{|a,b| a.to_s+'*'+b.to_s+'='} とまでは書けたのですが、コールバック(ブロックパラメータの部分)、上記例だと |a,b| a.to_s+'*'+b.to_s+'=' の部分 を変数として定義(Scalaの例のf1として定義するところ)する方法を知りません。 このように関数を変数として定義する事ってrubyではできないと考えるのですが、そういった認識で間違いないでしょうか? ご指導の程、よろしくお願いいたします。

    • ベストアンサー
    • Ruby
  • Ruby 文法 ブロックの中で自クラスの定数を呼ぶ

    以前した質問(http://okwave.jp/qa/q8484921.html)と非常に似てる質問になります。 以前の質問ではブロックの中で、そのクラスのメソッドを呼ぶ時のことでしたが、今回はメソッドではなく、定数にアクセスする時の質問です。 class Hoge VAL=1 def piyo yield end end h=Hoge.new h.piyo do |this| puts Hoge::VAL #<ーココの話 end 上の様に書く時、ブロックの中で、そのクラスの中の定数を参照するとき、上の例だと Hoge::VAL と書いてますが、Hoge::と書くのがとても嫌なので、以下の様に書いてみました。 class Hoge VAL=1 def piyo yield self end def retVal VAL end end h=Hoge.new h.piyo do |this| puts this.retVal #<-Hoge::VALと書かなくてよくなった。 end が、しかし実はVALなどの定数は多数あり、その数の分だけ値を返すメソッドを書くなんて、もっと嫌です。 なんか良い書き方はあるのでしょうか? 以下、蛇足なのですが、そもそもクラスの中でしかアクセスしないマジックナンバー的な定数の定義の仕方が間違っているのでしょうか? moduleに定義して、クラスの中とブロック使うところでincludeするなどの方がスッキリするのかも。。

    • ベストアンサー
    • Ruby

専門家に質問してみよう