【Ruby】Rubyベストプラクティス 学習の感想
■topic summary
study about Hash.new{}, memoization, "module A; extend self", etc.
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (48件) を見る
5章が終わったぞー。
以下、気づいたことのメモ。
Hashのブロック
おさらい。
hash = Hash.new{|h, k|p "here"; h[k] = [] } hash["a"] << "aaa" hash["a"] << "bbb" p hash
■実行結果
"here" {"a"=>["aaa", "bbb"]}
Hashのブロックはキーが存在しない時だけ実行される。
キーをぐるぐる回す
hash = Hash.new{|h, k|p "here"; h[k] = 0 } [:a, :a, :b, :a].map{|x| hash[x] += 1 } p hash #<= {:a=>3, :b=>1}
メモ化したい関数に印をつける
嵌ったところ
alias の引数はメソッド 呼び出し等の一切の評価は行われません。メソッドの定義内で別名を付けるにはModuleクラスのメソッド Module#alias_method を利用して下さい。
http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fdef.html#alias
以下では、alias_methodを使っているところに注目。
class Test def self.fibo(int) return int if int==1 || int==0 fibo(int-1) + fibo(int-2) end def self.memorize(fnc) cache = Hash.new original = "__unmemoized_#{fnc}__" singleton = class << self;self;end singleton.class_eval do alias_method original, fnc define_method(fnc){|*args|cache[args] ||= send(original, *args)} end end memorize :fibo end p Test.fibo(3)
module A; extend self の意味。
たとえばこんなコード。
module Hoge def foo1; p "foo1"; end end class Huga extend Hoge foo1 #<= "foo1" end
extend することで
⇒Class内でextend先のメソッドがプライベート呼出し可能
だから、
自分をextendすることで、
⇒自分内で自分のメソッドがプライベート呼出し可能
なるほど。
module Hoge extend self def foo1 p "foo1" end foo1 #<= extend selfによって 自分のメソッドが呼べる end
ふむふむ。
included と inherited の違い。
include はプライベートメソッド
module A def self.included(clazz) p clazz end end class B include A #<= "B"が表示 end
includeした時点でフックメソッドが呼ばれる。
【Ruby】【メタプログラミング】遅延評価のためのプロキシオブジェクト、作り方。
■topic summary
study about "lazy evaluation" using method_missing and call method.
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (48件) を見る
遅延評価のためのプロキシオブジェクト
定義時ではなく、呼び出し時に評価するオブジェクトの作成方法。
フラグを用いた方法は以前実装したけど、今回は
・任意のブロックを実行する
・method_missingを利用して任意のメソッド呼び出しに対応する
が、異なる点。
module NaiveLazy class Promise < BasicObject def initialize(&b) @computation = b end def __result__ if @computation @result = @computation.call @computation = nil end @result end def method_missing(*a, &b) __result__.send(*a, &b) end end end class Cell def initialize(text) @text = text @width = NaiveLazy::Promise.new{ calculate_width } end attr_accessor :text, :width def calculate_width p "creating----- " + (@text * 10) @text * 10 end end cell = Cell.new("h") p "---- create cell ----" cell.width #<= inspectメソッドがmissing cell.width + "dayo" #<= +メソッドがmissing
■実行結果
"---- create cell ----" "creating----- hhhhhhhhhh"
calculate_widthは、定義時に実行されていないのが分かる。
inspectメソッドは独自に定義する
inspectメソッドを独自に定義することで、評価前/後か判断できる。
def inspect return "<NaiveLazy::Promise computation=#{@computation.inspect}" if @computation return @result.inspect end cell = Cell.new("h") p cell.width #<= inspectメソッドが呼び出される cell.width + "dayo" #<= @resultが生成される p cell.width #<= inspectメソッドが呼び出される
■実行結果
<NaiveLazy::Promise computation=#<Proc:0x26b34e8@C:/Users/_ADMIN/Desktop/test/arrays.rb:26> "hhhhhhhhhh"
inspect自体では、
newメソッドで渡したブロックの評価が行われないようになった。
全文
module NaiveLazy class Promise < BasicObject def initialize(&b) @computation = b end def __result__ if @computation @result = @computation.call @computation = nil end @result end def inspect return "<NaiveLazy::Promise computation=#{@computation.inspect}" if @computation return @result.inspect end def method_missing(*a, &b) __result__.send(*a, &b) end end end class Cell def initialize(text) @text = text @width = NaiveLazy::Promise.new{ calculate_width } end attr_accessor :text, :width def calculate_width @text * 10 end end cell = Cell.new("h") p cell.width #<= inspectメソッドが呼び出される cell.width + "dayo" #<= +メソッドがmissing p cell.width #<= inspectメソッドが呼び出される
ポイント
BasicObject(白紙のオブジェクト)を使う。
Objectを使うと、煩雑なメソッドを一個一個消していったのは前にやったところ
class TraceObject #TraceObjectの既存インスタンスメソッドを消す instance_methods.each do |m| next if m == :inspect || m == :object_id || m == :__id__ || m == :__send__ undef_method m end ・・・
ふむー。。
【Ruby】send, class_evalを使ったモックオブジェクト
■topic summary
study about "send"
この本を読んでます。
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (48件) を見る
勉強しても勉強してもねー。。。
send を使ったプライベート呼び出し
class User; end user = User.new singleton = class << user; self; end singleton.send(:define_method, :hoge){"bingo"} p user.hoge #<= "bingo"
本には
define_method で定義したメソッドは特異クラス上でプライベートになるので、利用するためにはsend()が必要になる
とあるけど、
class_eval でも実現できるよね?
class User; end user = User.new singleton = class << user; self; end singleton.send(:define_method, :hoge){"bingo"} singleton.class_eval{define_method(:foo){"bar"}} p user.hoge #<= "bingo" p user.foo #<= "bar"
うむうむ。
どっちでもいいのかな。
define_methodの引数
class String send(:define_method, :hoge) do |a| p a*3 end end "".hoge("c") #<= "ccc"
send で渡したブロックはそのまま引き渡される。
オブジェクトのメソッド name を args を引数に して呼び出し、メソッドの実行結果を返します。
http://docs.ruby-lang.org/ja/1.9.3/method/Object/i/send.html
ブロック付きで呼ばれたときはブロックもそのまま引き渡します。
ちょっとややこしかったのでメモ。
【Ruby】【Rails】gem install railsがコケる
■topic summary
Error installing rails X(
はまった。。。
gem install rails
がなぜかコケる。
■ログ
$ gem install rails Fetching: atomic-1.1.14.gem (100%) Temporarily enhancing PATH to include DevKit... Building native extensions. This could take a while... ERROR: Error installing rails: ERROR: Failed to build gem native extension. c:/Ruby200/bin/ruby.exe extconf.rb *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --without-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=c:/Ruby200/bin/ruby --with-atomic_reference-dir --without-atomic_reference-dir --with-atomic_reference-include --without-atomic_reference-include=${atomic_reference-dir}/include --with-atomic_reference-lib --without-atomic_reference-lib=${atomic_reference-dir}/ c:/Ruby200/lib/ruby/2.0.0/mkmf.rb:430:in `try_do': The compiler failed to generate an executable file. (RuntimeError) You have to install development tools first. from c:/Ruby200/lib/ruby/2.0.0/mkmf.rb:515:in `try_link0' from c:/Ruby200/lib/ruby/2.0.0/mkmf.rb:813:in `try_run' from extconf.rb:26:in `<main>' Gem files will remain installed in c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14 for inspection. Results logged to c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/gem_make.out
mkmf.logを見ても、エラーは載っていない。
C:\Ruby200\lib\ruby\gems\2.0.0\gems\atomic-1.1.14\ext\mkmf.log
解決方法
ここに書いてある通り、
http://stackoverflow.com/questions/18777079/cant-install-atomic-ruby-atomic-gem-in-rails-4-0
atomic-1.1.14 のインストールでこけているようなので、
atomicだけ単体インストールする。
gem install atomic -V
■ログ
当然エラーになる
$ gem install atomic -V HEAD https://rubygems.org/latest_specs.4.8.gz 302 Moved Temporarily HEAD https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz 200 OK GET https://rubygems.org/latest_specs.4.8.gz 302 Moved Temporarily GET https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz 200 OK Installing gem atomic-1.1.14 Temporarily enhancing PATH to include DevKit... c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/test/test_atomic.rb
ログを見る。
c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/
In file included from c:/Ruby200/include/ruby-2.0.0/ruby.h:33:0, from conftest.c:1: c:/Ruby200/include/ruby-2.0.0/ruby/ruby.h:125:14: error: size of array 'ruby_check_sizeof_voidp' is negative In file included from c:/Ruby200/include/ruby-2.0.0/ruby.h:33:0, from conftest.c:1: c:/Ruby200/include/ruby-2.0.0/ruby/ruby.h: In function 'rb_float_value':
ここが怪しい。
原因
Rubyが32ビット用、
DevKitが64ビット用だったのがエラーの原因
orz
ここを参考にDevKitを再インストール
おもっことをまったりと Devkit削除
$ ruby dk.rb install [INFO] Skipping existing gem override for 'C:/Heroku/ruby-1.9.2' [WARN] Skipping existing DevKit helper library for 'C:/Heroku/ruby-1.9.2' [INFO] Updating existing gem override for 'C:/Ruby200' [INFO] Installing 'C:/Ruby200/lib/ruby/site_ruby/devkit.rb'
よし、再インストールされた。
mkmf.log は閉じましょう
再度インストールするも、エラー。
なんでやねん。
$ gem install atomic -V --platform=ruby HEAD https://rubygems.org/latest_specs.4.8.gz 302 Moved Temporarily HEAD https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz 200 OK GET https://rubygems.org/latest_specs.4.8.gz 302 Moved Temporarily GET https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz 200 OK Installing gem atomic-1.1.14 Temporarily enhancing PATH to include DevKit... c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/test/test_atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/extconf.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/.gitignore c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/.travis.yml c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/LICENSE c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/README.md c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/Rakefile c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/atomic.gemspec c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/atomic_example.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/bench_atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/bench_atomic_1.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/graph_atomic_bench.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/AtomicReferenceService.java c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/atomic_reference.c c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/concurrent_update_error.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/delegated_update.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/direct_update.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/fallback.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/jruby.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/numeric_cas_wrapper.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/rbx.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/ruby.rb Building native extensions. This could take a while... c:/Ruby200/bin/ruby.exe extconf.rb *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options.
C:\Ruby200\lib\ruby\gems\2.0.0\gems\atomic-1.1.14\ext
を見ると、更新されるべきログファイル
mkmf.log
が更新されていない。
メモ帳で開いていたから。
もしやと思って、閉じて、再インストール。
成功しました。
orz
$ gem install atomic -V --platform=ruby HEAD https://rubygems.org/latest_specs.4.8.gz 302 Moved Temporarily HEAD https://s3.amazonaws.com/production.s3.rubygems.org/latest_specs.4.8.gz 304 Not Modified Installing gem atomic-1.1.14 Temporarily enhancing PATH to include DevKit... c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/test/test_atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/extconf.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/.gitignore c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/.travis.yml c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/LICENSE c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/README.md c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/Rakefile c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/atomic.gemspec c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/atomic_example.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/bench_atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/bench_atomic_1.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/examples/graph_atomic_bench.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/AtomicReferenceService.java c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/atomic_reference.c c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/concurrent_update_error.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/delegated_update.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/direct_update.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/fallback.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/jruby.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/numeric_cas_wrapper.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/rbx.rb c:/Ruby200/lib/ruby/gems/2.0.0/gems/atomic-1.1.14/lib/atomic/ruby.rb Building native extensions. This could take a while... c:/Ruby200/bin/ruby.exe extconf.rb creating Makefile make "DESTDIR=" generating atomic_reference-i386-mingw32.def compiling atomic_reference.c linking shared-object atomic_reference.so make "DESTDIR=" install /usr/bin/install -c -m 0755 atomic_reference.so ./.gem.20131201-13740-8jc703 installing default atomic_reference libraries Successfully installed atomic-1.1.14 Parsing documentation for atomic-1.1.14 Parsing sources... unable to convert "\x90" from ASCII-8BIT to UTF-8 for lib/atomic_reference.so, skipping Installing ri documentation for atomic-1.1.14 1 gem installed
本題も解決
$ gem install rails Installing ri documentation for rails-4.0.1 14 gems installed
長かったorz
【Ruby】Rubyソースコード完全解説
■topic summary
study about this site -> Rubyソースコード完全解説
ちょっと難しかった。。
以下、学んだことなど。
Stringのインスタンス変数
現実にruby 1.2まではgeneric_iv_tblが導入されておらず、従って StringやArrayではインスタンス変数を使うことはできなかったのだが、それで もたいして問題にはなっていなかった。
http://loveruby.net/ja/rhg/book/object.html
Stringクラスのインスタンス変数?
やってみよう。
class String alias org_initialize initialize def initialize(str, num) super() self << str @num = num end def howmany @num end end a = String.new("hoge", 1) p a p a.howmany #<= 1
なるほど。
Rubyに++(インクリメンタル)がない理由
+=があるなら++もあるのかな、と思いきや++はない。なぜだろうか。 Rubyでは代入は言語処理系が扱うものである。一方メソッドはライブラリが実 行するものである。この二つ、即ち変数世界とオブジェクト世界、をきっ ぱり分けるというのはRubyの重要な特徴である。++を導入するとその区別が 壊れてしまいかねない。というのが++のない理由だ。
へー。
パーサーから見た「ぶら下がりelse問題」
ぶらさがり elseもシフトしておけばうまくいく。そういうわけでyaccもその流れに従い shift/reduce conflictが起きたときにはデフォルトでシフトを選ぶようになっ ている。
http://loveruby.net/ja/rhg/book/yacc.html
以前にelse if問題を考えたことがあった。
少し違う(あの問題はシンタックシュガーなので)けど、
改めてパーサーで何が行われているのかをみてみよう。
ふむふむ。
パーサーでは還元・が行われている。
「ぶら下がりelse問題」は、
この2つの動作がどちらも行える可能性があるとき
エラーにするのか、どちらかを優先するのか、という問題。
ここで用語の整理。
シフト(shift)
⇒「かたまり」に詰めていく
還元(reduce)
⇒現在の「かたまり」を終端記号に変換し「かたまり」を空にする。
もう一度再掲すると、
ぶらさがり elseもシフトしておけばうまくいく。そういうわけでyaccもその流れに従い shift/reduce conflictが起きたときにはデフォルトでシフトを選ぶようになっている。
http://loveruby.net/ja/rhg/book/yacc.html
「かたまり」につめるほうを優先する。
なるほど。
「非結合」
nonassocの代表格は比較演算子だろう。 a == b == c # パースエラー a <= b <= c # パースエラー
なるほどー。
優先順位が同じ==演算子を複数書くとエラーになるのは
非結合だからなのか。
【Ruby】パーサーから見た「a???」が構文エラーとならない理由
topic
study about parse.y ...a little.
ここの記事を読んでいます。
Rubyソースコード完全解説
パーサーの仕組みが詳しく載っている。
ちょっと難易度が高め!
ごめんカッコつけた。。。かなり高め
以前「どうしてこれが構文エラーにならないの」問題を考えたことがあったけど、
折角だから、パーサーからも探ってみよう。
なかださんのTweet。
@sora_h A. parse.y読了すれば自明
— なかだ の (@n0kada) 2013, 11月 14
無理だよー。。と思うんだけど、見てみよう!
そもそもparse.yがない
Ruby200フォルダ下にparse.yがない。
よく分からないけど、githubにコードがあるので、こちらで代用。
ruby/parse.y at trunk · ruby/ruby · GitHub
ここの、parser_yylexがみるべき関数みたい。
どこにparse.yがあるのか、なかださんに教えて頂いた。
@haisekai parse.yは実行時には不要なのでインストールされません。 https://t.co/cgJhBkMuKn
— なかだ の (@n0kada) 2013, 11月 30
えーどういうことー。
ヒントにしたがってyaccの項目を読めば、理解できました。
(記号列だけを見て解析できるようにする)自動化ツールをパーサジェネレータ(parser generator)と言う。
http://loveruby.net/ja/rhg/book/yacc.html
UNIXで一番使われているパーサジェネレータがyaccだ。rubyの パーサも御多分に漏れずこのyaccを使って書かれている。parse.yがその 入力だ。・・・yaccにかけるとCのソースコードができるので、 あとは普段通りそれをコンパイルすればよい。
つまり、parse.yはコンパイルされてparse.o になっているってこと。
調べてみるとこのファイルはちゃんとあった。
「Rubyのソースコード」には、parse.yが存在している。
「Windows版Rubyバイナリ」をDLしていたから、手元には存在しなかったんだ。
ダウンロード
C言語のコンパイルの流れが分かってないからこんな質問しちゃったんだなー。。。
お礼のtweetも済ませて。
分かりました!パーサジェネレータ(yacc)を使って書かれたものだからコンパイルされたものが配布されているのですね!コンパイル後のparse.oファイルの存在が確認できました。理解できました。ありがとうございました。。!>@n0kada
— sekai (@haisekai) 2013, 11月 30
肝心の中身は。。
switch (tok()[0]) { case '@': case '$': pushback(c); break; default: if ((c == '!' || c == '?') && !peek('=')) { tokadd(c); }
この箇所で、最初の"?"を識別子としてシフトする。
case '?': c = nextc(); if (!parser_isascii()) { } else { tokadd(c); #<= (ア) }
?の次の文字を取って、
エラーや特殊パターンに当てはまらなかったら(そのまま文字列に)加える。
だから、
a???
はa?メソッド(識別子)の、引数「?」です、ということになる。
位しか分からなかった。ちょっと貧しい。。
そもそも当たっているのか良く分からない。
また再び力がついたら見返してみよう。
【Ruby】あなたの Ruby コードを添削します 【第 1 回】 pukipa.rb
■topic summary
study about this page
->Rubyist Magazine - あなたの Ruby コードを添削します 【第 1 回】 pukipa.rb
るびまのちょっと前の記事、Rubyコード添削しますを読みました。
るびまのリファクタリング記事は、読みやすくて楽しいね。
前にもお勧めした、
せきさん・須藤さんのリファクタ合戦とか。
今回はボリュームが多いけど。。
いつかは私も添削してほしいなー。
やばい!
以下、学んだこと
unlessに慣れましょう
あるていど Ruby に慣れれば unless のほうが文を解釈する負担が少なくなると思います。
うーん、、そっかー。
まだ使い慣れないなー。unless, until 。
Exceptionの使いどころ
この例外はプログラムにバグがあることを報告するために投げているわけですから、そう簡単に捕まえられたくはないでしょう
こういうときは、Exceptionを使う。
(クラスを指定しない)rescue で捕獲されることがなくなるから。
引数に渡す変数の寿命
寿命も由来も違うわけで、両者を一緒に渡すよう要求してしまうと使いづらくなることうけあいです
newする際に要求するものの寿命を意識する。
正規表現の配列もち
re_table = [ [/^aaa/, 3], [/^aa/, 2], [/^a/, 1] ] data = "aab" p re_table.detect{|re, _| re =~ data }
■実行結果
[/^aa/, 2]
なるほど。Hashよりシンプル。
文字列先頭を意味する正規表現
添削の過程で、青木さんは ^ を \A に変更している。
^ 行頭にマッチします。行頭とは、文字列の先頭もしくは改行の次を 意味します。
http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2fregexp.html
\A 文字列の先頭にマッチします。
ふむふむ。
a = <<EOS hogehoge hhh EOS p /^hhh/ =~ a p /\Ahhh/ =~ a p /^hoge/ =~ a p /\Ahoge/ =~ a
■実行結果
9 nil 0 0
なるほど。
\Aのほうがキツくなるんだ。
when節で代入
case when htmlchar = $1 then escape(htmlchar) when bracket = $2 then a_href($3, bracket, 'outlink') when pagename = $4 then a_href(page_uri(pagename), pagename, 'pagelink') when uri = $5 then a_href(uri, uri, 'outlink') else
とあるところ、
when htmlchar == $1 then escape(htmlchar)
の間違い?と思ったけど違うのね。
代入して、値がnilでなかったらthenを実行、というロジック。
代入だから、この時点で変数(htmlchar)が存在しなくてよい。
こういうイメージ。
case when a = nil then p "aaa" when b = "" then p "bbb" end
■実行結果
"bbb"
なるほど。
正規表現インスタンス生成はコストが高い
ruby コマンドに -r profile とオプションを付けてプログラムを実行します。 するとプログラム終了後、標準エラー出力にプロファイルが出力されます
知らなかった。
C:\>ruby -r profile test.rb "bbb" % cumulative self self total time seconds seconds calls ms/call ms/call name 0.00 0.00 0.00 1 0.00 0.00 TracePoint#enable 0.00 0.00 0.00 2 0.00 0.00 IO#set_encoding 0.00 0.00 0.00 1 0.00 0.00 Kernel#hash 0.00 0.00 0.00 1 0.00 0.00 String#inspect 0.00 0.00 0.00 1 0.00 0.00 Kernel#p 0.00 0.00 0.00 1 0.00 0.00 TracePoint#disable 0.00 0.01 0.00 1 0.00 10.00 #toplevel
へー。
gsubへのブロック渡し
文字列中で pattern にマッチした部分を順番にブロックに渡し、 その実行結果で置き換えます。
http://docs.ruby-lang.org/ja/1.9.2/class/String.html
ふむふむ。
data={ "aaa" => 3, "aa" => 2, "a" => 1 } str = "aaabaabbbabbbaaa" p str.gsub(/a*/){|s| data[s]}
■実行結果
"3b2bbb1bbb3"
なるほど。
うわーこの連載、5回も続いてる。。
すごい嬉しいけど、読むのが大変だ!
ちょっと疲れた。。