Rubyで質問です

include Math
a = (:to_s).to_proc
puts a[123]
は"123"になるのに
b = (:sin).to_proc
puts b[1]
はエラーになります。
in `[]': private method `sin' called for 1:Fixnum (NoMethodError)

b=->x{sin(x)} 以外でsinをProc型にする簡単な記法はないでしょうか?

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2013/09/30 00:05:05
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答1件)

id:iwadon No.1

回答回数60ベストアンサー獲得回数13

ポイント100pt

以下のようにProcの代わりにMethodではダメでしょうか?

b = Math.method(:sin) # => #<Method: Math.sin>
b[123] # => -0.45990349068959124
id:garyo

ありがとうございます。使えそうですね。
http://ruby.g.hatena.ne.jp/garyo/20130922/p4
関数を渡して積分するサンプルを作ろうと思ったときにsin()は元々関数なので
b=sin みたいな形で関数を引数とする関数に渡せるといいのにと思い質問しました。

2013/09/23 02:02:20
id:garyo

質問者から

garyo2013/09/23 09:31:56

ruby 2.0.0p195 (2013-05-14) [i386-mswin32_100]

QSはWindows7です。

  • id:pmint
    puts b.class
    でProcと表示されますよ。
  • id:garyo
    >puts b.class
    >でProcと表示されますよ。

    確かにそうですが、
    p b[1.0]を実行すると
    NoMethodError: undefined method `sin' for 1.0:Float
    とエラーになります。
  • id:pmint
    メソッド定義がないというエラーなので、1(Fixnum)や1.0(Float)には(to_sはあるけど)sinは無いということでしょう。
    例えばsin定義を補えば実行可能なので。

    include Math

    class Fixnum
    def sin
    Math.sin self
    end
    end

    b = (:sin).to_proc
    p b[1] # 0.8414709848078965
    p 1.sin # 0.8414709848078965
  • id:garyo
    なるほど。
    しかし使用する関数を毎回追記するのは大変ですね。

    こんな感じのことがやってみたいのです。

    a = Math.method(:sin)
    b = ->x,y{x[y]}
    p b[a,1.0] #=> 0.8414709848078965
  • id:iwadon
    あとは、Procをcallする際にレシーバーを明示するとか。
    a = (:sin).to_proc # => #<Proc:0x007f96b9121d10>
    a[Math, 123] # => -0.45990349068959124

    この感じからすると、aがProc、bがレシーバー、cが引数だとして強引に変換すると
    a[b, c] は b.a(c) に相当する感じでしょうか。
    なので a[123] は 123.a になり、a[Math, 123] は Math.a(123) になる、と。
  • id:iwadon
    上記はたまたまやってみたら出来たのでコメントしたのですが、
    よくよく調べてみると:

    http://doc.ruby-lang.org/ja/1.9.3/class/Symbol.html#I_TO_PROC
    >>
    生成される Proc オブジェクトを呼びだす(Proc#call)と、 その第一引数の self という名前のメソッドを 残りの引数を渡して呼びだされます。
    <<

    ということなので、Symbol#to_procによって生成したProcをcallすると第1引数がレシーバーとして使われるのは仕様のようですね。

    proc {} や Proc.new でProcを生成した場合、生成した時点でのselfがレシーバーとしてProcに固定される(言い方が変?)のでProc#callに渡された引数はすべてProcに渡る(レシーバーを明示する必要がない)けど、Symbol#to_procは完全にレシーバーから切り離された状態になるためProc#callの際には引数にレシーバーが必要、という感じでしょうか(まだはっきりとは理解できていない)。
  • id:garyo
    >あとは、Procをcallする際にレシーバーを明示するとか。
    >a = (:sin).to_proc # => #<Proc:0x007f96b9121d10>
    >a[Math, 123] # => -0.45990349068959124
    これはさすがに冗長な気がします。a[sin,123]とかa[:sin,123]とできると楽しそうですが。
    http://melborne.github.io/2011/01/24/Ruby/
    上記で「Rubyにおいてorとandは予約語なのでそのままの形では引数として渡すことができない。ここではこの問題を回避するため、orとandをMethodオブジェクト化して渡している。」
    というのがあるので Math.method(:sin)というのが一番簡単なのかも知れませんね。


    >|ruby|
    class Array
    def any_true
    reduce method(:or), false
    end
    def all_true
    reduce method(:and), true
    end
    private
    def or(a,b)
    a or b
    end
    def and(a,b)
    a and b
    end
    end
    ||<
  • id:iwadon
    > これはさすがに冗長な気がします。a[sin,123]とかa[:sin,123]とできると楽しそうですが。

    それって send のような気がします。

    >||
    a = Math
    a[:sin, 123] # => NoMethodError
    class Object; def [](*args); __send__(*args); end; end
    a[:sin, 123] # => -0.45990349068959124
    ||<

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません