16978

[code]

    p (0..10).map{|n| 1 == 1}
    
[/code]

の結果は  
[true, true, true, true, true, true, true, true, true, true, true]  
ですが、eachの中でのpメソッドの使用した

①

[code]

    (3..8).each{|p|
      p (0..10).map{|n| 1 == 1}
    }
    
[/code]

の結果は  
0..10  
0..10  
0..10  
0..10  
0..10  
0..10  
となってしまいます。  
このことを回避するために、

②

[code]

    (3..8).each{|p|
      p ((0..10).map{|n| 1 == 1})
    }
    
[/code]

としたのですが、どうして上記のようなことが起こるのか教えてください。  
また、修正の仕方は②で良かったのか教えていただければ幸いです。

（追記）  
1.（②以外の修正を行う場合の）要件  
質問の都合上条件の式を 1==1 としていますが、  
本当は 変数pに関する数式が入るので、  
**できれば変数はpのままにしておきたい。**  
2.背景  
プログラミングの話でなく、数学の話になって恐縮ですが、  
p進数（<https://ja.wikipedia.org/wiki/P%E9%80%B2%E6%95%B0>）  
というものがあります。  
pという記号を使うのが慣例のようなものです。  
よって、プログラミングではiでもpでもどちらでもよいのですが、  
数学でpを使用している以上なるべ‌​くpを使いたい。


CC BY-SA 3.0
2015-09-26T15:12:08.157
16966
2015-09-27T12:41:14.437
2015-09-26T16:32:21.013
5363
5363
question
ruby
ブロックの中でのpメソッドの使用について


BLUEPIXYさんのコメントの回答にあるように名前がかぶらなければ良いだけですので

[code]

    require 'pp'
    (3..8).each{|p|
      pp (0..10).map{|n| 1 == 1}
    }
    
[/code]

なども良いんじゃないでしょうか。打つ量がすくなくてすみますし。`pry`ならrequireをする必要もありません。


CC BY-SA 3.0
2015-09-26T17:07:25.177
16971
2015-09-26T17:07:25.177
3271
16966
answer


どうして、についてですが、

[code]

    p (0..10).map{|n| 1 == 1}
    
[/code]

が

[code]

    (p(0..10)).map{|n| 1 == 1}
    
[/code]

と解釈されてしまったためです。通常は以下の様に `map` の処理の結果が `p` メソッドの引数として渡されるのですが、

[code]

    p (0..10).map{|n| 1 == 1 }
    [true, true, true, true, true, true, true, true, true, true, true]
    => [true, true, true, true, true, true, true, true, true, true, true]
    
[/code]

p に何らかの値をバインドすると、

[code]

    p = 1
    => 1
    p (0..10).map{|n| 1 == 1 }
    0..10
    => [true, true, true, true, true, true, true, true, true, true, true]
    
[/code]

となってしまいます。詳しくはソースコードで確認する必要がありますが、眠いのでここまでとします。

なお、どうしても `p` という symbol を同時に使いたいのであれば、以下の様にしても良いかもしれません。

[code]

    (3..8).each{|p|
      p Range.new(0, 10).map{|n| 1 == 1}
    }
    
[/code]

でもまぁ、これなら Manyama さんの `p ((0..10).map{|n| 1 == 1})` の方が良いでしょうね。

**追記**

Ruby のソースコードの parse.y ファイルに [tokenize_ident
関数](https://github.com/ruby/ruby/blob/trunk/parse.y#L7600)
があって、この関数での処理が原因の様です。

[code]

    static void
    tokenize_ident(struct parser_params *parser, const enum lex_state_e last_state)
    {
      ID ident = TOK_INTERN();
    
      set_yylval_name(ident);
      if (!IS_lex_state_for(last_state, EXPR_DOT|EXPR_FNAME) &&
        is_local_id(ident) && lvar_defined(ident)) {
        lex_state = EXPR_END;
      }
    }
    
[/code]

`is_local_id(ident) && lvar_defined(ident))`
となっていて、シンボルがローカル変数として定義されている場合はパースの仕方を変えています。

[code]

    irb> defined? p
    => "method"
    irb> p (1..3).map{|n| n}
    [1, 2, 3]
    => [1, 2, 3]
    
    irb> p = 1
    irb> defined? p
    => "local-variable"
    irb> p (1..3).map{|n| n}
    1..3
    => [1, 2, 3]
    
[/code]

そこで if 文をコメントアウトして再ビルドしてみました。

[code]

    set_yylval_name(ident);
    /*
    if (!IS_lex_state_for(last_state, EXPR_DOT|EXPR_FNAME) &&
      is_local_id(ident) && lvar_defined(ident)) {
      lex_state = EXPR_END;
    }
    */
    
[/code]

実行結果です。

[code]

    irb> (3..8).each{|p|
    irb*   p (0..10).map{|n| 1 == 1}
    irb> }
    [true, true, true, true, true, true, true, true, true, true, true]
    [true, true, true, true, true, true, true, true, true, true, true]
                    :
    
[/code]

期待通りの結果になりましたが、なぜにあの様な処理になっているのかについては不明です。VCS のログを漁れば何か判るかもしれませんが…。


CC BY-SA 3.0
2015-09-26T19:18:08.677
16978
2015-09-27T12:41:14.437
2015-09-27T12:41:14.437
16966
answer


argusさんも書いているとおり、次のようなコードを書くと、同じ構文なのに出力される結果が変わりますね。

[code]

    p (1..3).map{|x| x*10}
    p = 1
    p (1..3).map{|x| x*10}
    
    # 出力結果
    [10, 20, 30]
    1..3
    
[/code]

なぜ変わってしまうのかは僕もよくわかりません。  
言語仕様の話に関わってきそうなので、Rubyの言語開発者の人たちに尋ねた方が確実かもしれません。

ところで、既存のメソッドと同じ名前のローカル変数を定義して元の実装を上書きしてしまうことは「シャドーイング」と呼ばれ、一般的にあまり好ましくないプラクティスです。

ご質問のケースであれば、完全にシャドーイングされてしまうわけではなく、次のように書けば確かに元のpメソッドを呼び出すことはできます。

[code]

    (3..8).each{|p|
      p((0..10).map{|n| n == p})
    }
    
[/code]

しかし、これだとコードの読み手が毎回「これはメソッドのpなのか、それとも変数のpなのか」を頭の中で切り替えなければいけないので、コードの可読性においてはBADです。

また、書き方を「ついうっかり」間違えると、予期せぬバグを生む恐れもあります。

なので、メソッドのpと変数のpを同時に存在させないアプローチを模索した方が望ましいと思います。  
そこでいくつか候補を挙げてみます。

## 変数名にpを使わない

どうしても変数名はpが良い、とのことですが、一歩妥協して _p にするのはどうでしょうか？  
これであれば名前の衝突を避けることができます。

[code]

    (3..8).each{|_p|
      p (0..10).map{|n| n == _p}
    }
    
[/code]

## pメソッド以外のprint系メソッドを使う

ironsandさんと同じ意見になりますが、pの代わりにppを使う、といった方法をとることもできます。  
引数として渡すオブジェクトがシンプルであれば、表示結果は同じになることが多いです。

[code]

    require "pp"
    (3..8).each{|p|
      pp (0..10).map{|n| n == p}
    }
    
[/code]

## 計算と出力を分ける

メソッドのpと変数名のpが同時に出現しないように、計算処理と出力処理を分ける、というアプローチです。

[code]

    data = (3..8).map{|p|
      (0..10).map{|n| n == p}
    }
    data.each{|d| p d }
    # 出力形式が多少変わっても良いなら
    p data
    
[/code]

ちょっとカッコよくいうと、MVCモデルでいうところの「ModelとViewを分離した形」とも言えます。  
計算と出力を分けることで、dataはコンソールに出力させるだけでなく、CSV形式に変換したり、HTMLテーブルに出力させたりして、出力方法を柔軟に変えることができます。

## pメソッドのエイリアスを定義し、pメソッドを無効化する

ちょっと過激かもしれませんが、pではない別のメソッド名（たとえばwなど）でpメソッドを呼び出せるようにして、元のpメソッドは使えなくする、というアプローチです。

「pは変数として使いたい」と考えるケースが多いのであれば、この方法で徹底できます。

[code]

    # wはwriteのw
    alias w p
    undef p
    (3..8).each{|p|
      w (0..10).map{|n| n == p}
    }
    
[/code]

ただし、自分の書いたコード以外でpメソッドが使われているとエラーが起きるかもしれないので、そういうエラーが怖い場合は `undef p`
しない方がいいかもしれません。

・・・などなど、いろんな方法で可読性やコードの安全性を維持することができます。  
自分にとって（そして、そのコードを読み書きする第三者や未来の自分にとって）一番適切な実装方法を検討してみてください。


CC BY-SA 3.0
2015-09-27T07:35:59.697
16991
2015-09-27T08:03:40.263
2015-09-27T08:03:40.263
85
85
16966
answer