せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】Rubyベストプラクティス 学習の感想

■topic summary
study about Hash.new{}, memoization, "module A; extend self", etc.

 

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

 
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}

 
 

インスタンス変数に(メモ化)

@hash ||= Hash.new{|h, k| h[k]=k.map{|e| e*10}.join}
@hash[key]

 
 

メモ化したい関数に印をつける

 
嵌ったところ

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した時点でフックメソッドが呼ばれる。
  

inherited はインスタンス生成時点

class A
  def self.inherited(base)
    p base
  end
end
class B < A
end
B.new  #<= "B"

 
「<」キーワードはメソッドでないため、
定義時ではなく、インスタンス生成時に呼ばれる。


長い。。

【Ruby】【メタプログラミング】遅延評価のためのプロキシオブジェクト、作り方。

 
■topic summary
study about "lazy evaluation" using method_missing and call method.

 

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

勉強中。。


 

遅延評価のためのプロキシオブジェクト

定義時ではなく、呼び出し時に評価するオブジェクトの作成方法。

フラグを用いた方法は以前実装したけど、今回は
・任意のブロックを実行する
・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ベストプラクティス -プロフェッショナルによるコードとテクニック

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック

 
勉強しても勉強してもねー。。。

 

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':

ここが怪しい。

Why does installing Ruby on Rails generate error "size of array 'ruby_check_sizeof_voidp' is negative? - Stack Overflow
 

原因

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。


 
無理だよー。。と思うんだけど、見てみよう!

 

そもそもparse.yがない

Ruby200フォルダ下にparse.yがない。
よく分からないけど、githubにコードがあるので、こちらで代用。
ruby/parse.y at trunk · ruby/ruby · GitHub

ここの、parser_yylexがみるべき関数みたい。


 
どこにparse.yがあるのか、なかださんに教えて頂いた。

 
えーどういうことー。
 
ヒントにしたがってyaccの項目を読めば、理解できました。

(記号列だけを見て解析できるようにする)自動化ツールをパーサジェネレータ(parser generator)と言う。
UNIXで一番使われているパーサジェネレータがyaccだ。rubyの パーサも御多分に漏れずこのyaccを使って書かれている。parse.yがその 入力だ。・・・yaccにかけるとCのソースコードができるので、 あとは普段通りそれをコンパイルすればよい。

http://loveruby.net/ja/rhg/book/yacc.html

f:id:sekaiya:20131130204015j:plain
 
つまり、parse.yはコンパイルされてparse.o になっているってこと。
調べてみるとこのファイルはちゃんとあった。
 
Rubyソースコード」には、parse.yが存在している。
「Windows版Rubyバイナリ」をDLしていたから、手元には存在しなかったんだ。
ダウンロード
 
C言語のコンパイルの流れが分かってないからこんな質問しちゃったんだなー。。。

お礼のtweetも済ませて。


 
肝心の中身は。。
 

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 に変更している。
 

^ 行頭にマッチします。行頭とは、文字列の先頭もしくは改行の次を 意味します。
\A 文字列の先頭にマッチします。

http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2fregexp.html

ふむふむ。
 

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回も続いてる。。
すごい嬉しいけど、読むのが大変だ!
ちょっと疲れた。。