SSブログ

一週間に一回Rubyプログラミングねた6-ブロック周辺 [プログラミング]

関数型プログラミングを見ようとして、作り付けの関数でしかブロックを使っていなかった事が判明。そこまで気合入れてプログラミングしてないので、この際きちんと知っておこうと思った。正直知らないでもコーディングはできていたんだけど、知っておいた方がいいには違いない。スコープとかの問題って、意識していないと変なバグを作りこんでしまう理由になるからね。

それとC言語系統みたいに{ }でスコープが区切られる言語ならいいにしても、そうじゃない言語もわりとあるんだということをJavaScriptで痛感したので、Rubyでも有耶無耶にしたくない気はする。きちんと動けば知らないことがあってもいいとは思うし、言語の全部を知っている必要は言語を作る人やメンテする人だけでいいだろう。

でも、気になったら調べておいたほうがいいのはプログラミングに限ったことじゃないし。いい機会だからきちんと調べて整理しておく。案外、変数やオブジェクトのスコープってのは言語によって違うっぽいし、特に強調して書かれている言語の教科書は少なかったりする。まぁ似たようなノーテーションのくせに違うってこと自体、実装の仕方としてどうなのかと疑ってしまう所はあるけど、言語のイレギュラーさは特色でもあるわけだしね。

 


まず、ブロックとはなんぞやというところ。関数に渡す式を記述する場所、と言えるでしょう。普通は引数とかパラメータという名前で、変数やオブジェクトをコピーして値だけを渡したり、オブジェクトそのものを渡したりするわけですね。でも、両者を気にするのはC/C++ぐらいしかないのかもしれない。だから、あえてスコープがはっきりしていて、それをあえて自分で書くようにしているわけだが、建て増しで階層が深くなってくると困るところがないわけでもない。

Rubyではインデントがスペースが2である場合が多いんだけど、わりと深い構造になっているコードを見かける。C言語はTabで8インデントというのが、viやmore, lessのコマンドを見れば標準になっている。恐らく、C言語で関数の中での階層を深くすると読みづらくなるのを防ぐ意味もあるのではないか、と思う。それと他の言語でもそうだと思うけど、共通する処理は関数にまとめるのは当然のこととしてある。やっぱり階層が深かったり、ダラダラ長いコードは誰も見たくない。

Rubyは可読性が高いので、ある程度処理をネストしても読みやすくはある。元々短く書ける言語的な素養はあるので、制御的なコードが残ってしまい、階層が深くなる傾向があるのかもしれない。そのための2インデントなのかもしれない。8インデントだとRubyのコードではキツい。だから、RubyでTabでインデントしている人は少数派だと思う。色んな所で、あえて2スペースのインデントを推奨しているのは、他の言語から移行してくる人も多いからかもしれない。

ちなみにWindowsはVisual Studio系でTabが4インデントをデフォルトとしていた気がするので、他のエディタとかも4インデントを使っている事が多かった気がする。秀丸エディタとかSakuraエディタだと半角4スペース分だったと思う。そこの設定を変えることもできたと思うけど、わざわざ変える人は少ない気がした。それよりも気にするところがあるだろう、ということなんだろうね。秀丸とかはUNIXでEUCを使うために使っている人も多かったから、8タブにしている人はいるにはいたな。





インデントはとにかく、ブロックの話。書き方としては二通りあったね。do endと{ }でした。{ }だと一行で済ませる時に使うとか一般的に言われているらしい。役割は同じ。だけど厳密には結合の強さが違うらしい。それって並べて書くとかじゃない限りは表面化してこないことだから、普段は意識する必要はないと思われ。

def hoge x
  if block_given?
    puts "hoge #{yield}"
  else
    puts "hogeブロックなし"
  end
end

def huga
  if block_given?
    puts "huga #{yield}"
  else
    puts "hugaブロックなし"
  end
end


puts "\n*** do-endのブロック ***"
hoge huga do
  "ですよ"
end

puts "\n*** { } のブロック ***"
hoge huga { "でした" }


実行結果
$ ruby -Ku block.rb

*** do-endのブロック ***
hugaブロックなし
hoge ですよ

*** { } のブロック ***
huga でした
hogeブロックなし


ん~まだRuby1.9の環境ってのもアレなんですが、UTF-8を指定して動かさないといけないのは面倒だな。大体、使っているライブラリに吸収されちゃうことも多いので、#!でわざわざ示すのもいちいちご苦労なことです。

上のコードは、本に書いてあるのを分かりやすく書いてみたんだけど、最後の関数の呼び出し方は変態でもない限りは普通しない。結局、関数の中で入れ子になっている感じなのかな。Rubyでは関数の( )は省略できるので、イマイチ分かりづらい。とはいえ、do-endのほうが直前との結合が弱いということらしい。

だが、後から書いたhugaの方が早く呼ばれるのも少し気持ちが悪いかな。そういう書き方したことないので、そっちの方が気になっちゃうけど、とにかく一応違いがあることは知っておいたほうがいいのかもしれない。




Rubyのオブジェクトのスコープについて。Rubyの変数はみんなオブジェクトと考えていいはずでしょう。細かい実装は考えたことも、Cのソースを見たこともないですが、みんなオブジェクトでいいと思います。そういう所は鵜呑みだなぁと思うけど、いちいちメンテナじゃないのに見ているわけにもいかないしね~。

C言語系だとfor, while,if文などのスコープは{ }で囲まれているので、大体はその場のスコープができると思うんだけど、Rubyでは特にスコープができないらしい。気にしてなかった。スコープを意識して使うためにも、繰り返しにはイテレータを使いましょうなんてある本には書いてあるけど、それは付随的な事項の一つなんだろうと思ったり。

とにかく{ }とかdo-endの中に入ってる処理にだけある変数は外からアクセス出来ない、ということ。まぁそれがスコープってもんだし、当たり前のことです。プログラミングの初心者はわりとそういうのを気にしないで、動くソースがあればいいと思うのでつっこんで考えることがないかもしれない。

更に、ブロックの外側で宣言した変数を渡した場合、参照渡しになってブロックの中で使うと、外側にある変数も変更される。何でこんなことを言うかといえば、ブロックじゃなくて普通に引数で渡すと、内部で変数を使われても関数の外側では変更されないのでした。C/C++で言うと、関数へ変数の値をコピーして外側に影響を与えない値渡しであって、外側の変数に関数で処理される変更の影響を与える場合は、ポインタを渡して実体のアドレスを参照させて、内部の値を直接変更させるのでした。

んなもんで、外から関数の中身の処理を参照渡しさせるためには、ブロックの中に関数の外の変数を渡すやり方しかないらしい。そりゃ否が応でも使わないとできないことが出てくるのだろう。それが嫌なら返り値を元々の変数に入れればいいかもしれないが、新しく入れこむ処理が増えるしRubyとしては一般的ではないのだろう。そもそも新しいオブジェクトとして生成されるみたいじゃ処理的に意味無いし。また別のオブジェクトを返すんだったら話は別なんだろうけど。

関数の中身である手続き的な処理を、ブロックとして渡せるわけでしたが、それだけじゃなくて、そこでスコープを生成するのでその性質がうんぬんとか少しややこしい。ブロック付き関数の外から、ブロックの中にぶち込んで使用すると、参照渡しが確定するわけだけど、処理の生成時の環境を束縛するものがクロージャと言うらしい。イマイチ分かりづらい。図示したほうが良さそうだね。

クロージャの使い方として、遅延評価や高階関数とか使えるみたいなんだけど、どちらもきちんと説明できるほど理解できてないし、私みたいなライブラリを使う人間としては作れなくても使えればいいので、深く理解できてないのだ。いい機会なので、実装方法を探して納得できるまで調べたいと思う。

すぐにできそうもないので、クロージャの話は次に持ち越します。

タグ:Ruby
コメント(0) 
共通テーマ:資格・学び

コメント 0