89117


rubyのクロージャでprocを変数に入れる理由を知りたいです。

[code]

    def a
        n = 0
        proc do 
            n += 1
        end
    end
    
[/code]

例えば上のような実行するたびに1を増やすクロージャを返すメソッドで

[code]

    a.call # => 1
    a.call # => 1
    b = a
    b.call # => 1
    b.call # => 2
    
[/code]

aメソッドに直接callをしても1ずつ増えていかないが、

**b** に **a** メソッドをいれてから **call** することでちゃんと機能する理由がわかりません

**a.call** だと、メソッド **a** を起動してしまい、 **n = 0** が実行されているからでしょうか?  
これが、一度変数に入れることで回避されるのは、

[code]

    b = a
    b.call 
    
[/code]

の場合  
**a** メソッド内の **proc** 部分だけが呼ばれているという解釈でよろしいでしょうか?


CC BY-SA 4.0
2022-05-29T08:09:46.700
89116
2022-05-29T22:32:02.137
2022-05-29T08:14:52.647
52832
52832
question
ruby
rubyのクロージャをcallするために一度変数に入れる理由を知りたい


合ってます。詳しくいうと次のようになってます。

次の3行では a が3回呼ばれ、  
つまり n = 0 が3回実行され、  
新しい Proc が3回作られ、  
それぞれの Proc に対して1回ずつ call を呼んでいます。

a.call  
a.call  
a.call

これに対して次の4行では a は1回だけ呼ばれ、  
つまり n = 0 は1回だけ実行され、  
新しい Proc は1回だけ作られ、  
その1個だけの Proc に3回 call を呼んでいます。

b = a  
b.call  
b.call  
b.call

言い換えると b = a は  
a というメソッドに b という別名をつけているのではなく、  
a というメソッドの返した値を b に代入しています。


CC BY-SA 4.0
2022-05-29T08:54:22.250
89117
2022-05-29T08:54:22.250
292
89116
answer


`b =
a`が実行されたとき、`a`と`b`は同じものが入っているわけではありません。`a`はメソッド呼び出しであり、`b`には`a`メソッドの返り値（この場合には`Proc`）が入っています。  
この状態では、`a`は実行されるたびに`n`の値を`0`で初期化するのに対し、`b`は初期化をせずに`Proc`を実行しています。

例えば、

[code]

    def a
        n = 0
        p = proc do 
            n += 1
        end
        puts 'Method a is called'
        p
    end
    
[/code]

のようにすると、`a`は実行するたびに標準出力に"Method a is
called"と表示するのに対し、`b`を実行しても標準出力には何も表示されないことがわかるかと思います。


CC BY-SA 4.0
2022-05-29T09:01:48.320
89118
2022-05-29T09:01:48.320
442
89116
answer


メソッド `a` は実行される度に新規の closure を返します。

[code]

    irb(main)> a == a
    => false
    irb(main)> a == a
    => false
    irb(main)> a == a
    => false
    
[/code]

一方、`b = a` とした場合の `b` は `a` の実行によって作成された closure になります。

[code]

    irb(main)> b = a
    => #<Proc:0x000055562ac72510 (irb):3>
    irb(main)> b == b
    => true
    
    irb(main)> c = a
    => #<Proc:0x000055562ac89788 (irb):3>
    irb(main)> c == b
    => false
    
[/code]


CC BY-SA 4.0
2022-05-29T09:02:48.850
89119
2022-05-29T09:02:48.850
47127
89116
answer

[code]

    def a()
        n = 0
        l = proc do 
            n += 1
        end
    
        return l
    end
    
    a().call
    b = a()
    b.call
    
[/code]

ですね。メソッド `a` は Proc オブジェクトを返します。

他の方の回答がすばらしいので別の観点から。

おそらく混乱の一つの理由は、メソッドは関数ではないため変数に格納できないことです。メソッドをオブジェクトとして取り出したい場合は、

[code]

    b = method(:a)
    # => #<Method: Object#a()>
    
[/code]

とすれば取り出すことができます。methodオブジェクトも `call` で呼び出すことができるので。

[code]

    b.call # メソッドaが実行される
    b.call.call # 1
    b.call.call # 1
    b.call.call # 1
    
[/code]

とすれば常に1が出てくることになります。

余談ですが、`.call` は `.()` と書きかえることもできるので、

[code]

    b = method(:a)
    b.call.call
    b.().call
    b.().()
    
[/code]

などと書くことにより、さらにそれらしくする（混乱させる）ことも可能です。


CC BY-SA 4.0
2022-05-29T22:25:43.510
89126
2022-05-29T22:32:02.137
2022-05-29T22:32:02.137
32772
32772
89116
answer