Rubyの勉強。Part1　yield、Procとlambdaの関係について。


自作のRubyのコードを利用して勉強します。

タイトルの通りPart続きですのでコードを少しずつ修正、改良して
Rubyの動作を見ていくつもりです。
この記事の対象者は初級プログラマやRuby初心者程度です。
一緒に理解していければ嬉しいです。

まずはコードを見てみましょう。
https://gist.github.com/hiroshiro/8962833/120929e2f6c6277847531bbfe1ea51431084f883
動作
*URLにあるページ上のjpg画像をファイルに保存します。*

```ruby:getHttpJpg.rb
##
# getHttpJpg.rb
#
# 使い方
#
# 例 
# ruby getHttpJpg.rb http://himasoku.com/archives/51824048.html
# JPGフォルダにjpgがダウンロードされる。
#
##
 
##
# hpricotを使う。
# gem install hpricot
##
require 'hpricot'
require 'open-uri'
 
##
# 引数をurlにする。
##
$url = ARGV[0]
doc = Hpricot( open($url).read)
 
##
# 空の保存ファイルを作る。
##
 
Dir.mkdir("./JPG") unless Dir.exist?("./JPG")
 
##
# カレントディレクトリをJPGファイルにする。
##
Dir.chdir("./JPG")
 
 
##
# ファイルをセーブする。
##
def save_file(url)
  filename = File.basename(url)
  open(filename, 'wb') do |file|
    open(url) do |data|
      file.write(data.read)
    end
  end
end
 
##
# jpgファイルがあればファイルをセーブ
##
def extract_jpg(doc)
  (doc/:img).each do |link|
    save_file(link[:src].to_s) if link[:src] =~ /.jpg/
  end
end
 
extract_jpg(doc)
```


経験を積んでいるプログラマの方は
コードの書き方にとてもこだわりがあると思います。
私はRubyのコードの書き方についてまだ良くわかっていません。
コメントの文章一つ見ても良い文章には見えません。
なのでまた別のPartで直しながら勉強していきます。

ここではこの２つのメソッドに注目します。

```ruby
##
# ファイルをセーブする。
##
def save_file(url)
  filename = File.basename(url)
  open(filename, 'wb') do |file|
    open(url) do |data|
      file.write(data.read)
    end
  end
end
 
##
# jpgファイルがあればファイルをセーブ
##
def extract_jpg(doc)
  (doc/:img).each do |link|
    save_file(link[:src].to_s) if link[:src] =~ /.jpg/
  end
end
```

この２つのメソッドは動作には何も問題ありません。
しかし、`extract_jpg`メソッドの方が`save_file`メソッドに依存しています。
当前ですが、もし`save_file`メソッドがなければ`extract_jpg`メソッドは使えません。


そもそもextract_jpgというメソッド名は間違っています。
保存しているのでメソッド名にsaveを入れるべきです。（メソッド名についてはまた別のPartで）

ここでタイトルの通り`yield`を使って`extract_jpg`からURLを渡すコードに
変えてみましょう。

```ruby
##
# jpgファイルがあれば, URLを渡す。
##
def extract_jpg(doc)
  (doc/:img).each do |link|
    yield link[:src].to_s if link[:src] =~ /.jpg/
  end
end
```

`save_file`を`yield`に変えるだけです簡単ですね。
これで依存はなくなりました。
しかしURLを渡すだけなので、渡して保存するメソッドを作らなくてはいけません。

```ruby
##
# URLにあるjpgをセーブする
##
def save_url_files(doc)
  extract_jpg(doc) {|x| save_file(x)}
end

save_url_files(doc)
```

これで動作します。コードはこちら
https://gist.github.com/hiroshiro/8962833/b9847db29664a820b0fb3be2ab14f0847c875ff9

##yieldの動作
`yield`はブロックを作り

>method do
`yield` #ここにある要素を（順に）取り出せる様になる。ブロックを作る。
end


そのメソッド{ブロック}から要素を取り出しができる
>`method`{|要素| →　取り出す}

といったイメージでしょうか。ここでは`extract_jpg`メソッドから
要素を変数xにして順にURLを取り出しています。
##yieldとreturn
yieldはreturnとは全然違います。

試しに`yield`を`return`にしてみるとわかります。その動作だと
`extract_jpg`メソッドの`link`のイテレーターから最初のURLだけを返します。
そしてメソッドを終了してしまいます。

returnの場合
>method do
`return` #ここにある要素を一つ返します。メソッドもすぐに終了。
end

当然ながら、ブロックを作りません。
> 変数 ⇚ `method`　変数に返す。  

returnを使う場合ではメソッド内で文字列の配列などの塊をそのまま帰す方法になります。

```ruby
##
# jpgファイルの, URLの配列を返す。
##
def extract_jpg(doc)
  urls = []
  (doc/:img).each do |link|
    urls = link[:src].to_s if link[:src] =~ /.jpg/
  end
  urls #returnの記述は省略できる。
end

extract_jpg.each do |url|
  save_file(url)
end

```

私は恥ずかしながら、`yield`の存在を知らなかった時にこういう方法を取っていました。

##ブロック引数&とProcオブジェクトの関係。
ブロック引数&を手前に付けることで明示的にブロックを作ることができます。

```ruby
##
# jpgファイルがあれば, URLを渡す。
##
def extract_jpg(doc,&block)
  (doc/:img).each do |link|
    block.call link[:src].to_s if link[:src] =~ /.jpg/
  end
end
```

上記では&block引数を作り、blockが要素を呼び出せる様にしている。
&引数は最後の引数（一番右側）しなければならない。

これは先ほどのyieldと同じ動作をしています。
>method do &ブロック
>`ブロック.call` ##ここにある要素を（順に）取り出せる様になる。yieldと同様。
>end

この`block`は実はProcオブジェクトです。
つまり、`&`を付けない場合は下記と同じ動作です。

```ruby
def extract_jpg(doc,block = Proc.new)
```
>`&b` は　`b = Proc.new`　と同様。

それか直接`call`もできる。これも同じ動作。

```ruby
Proc.new.call link[:src].to_s if link[:src] =~ /.jpg/

#Proc.newはグローバル変数procなので実はnewする必要はない。
proc.call link[:src].to_s if link[:src] =~ /.jpg/ 
```
ruby1.9以上はグローバル変数procを使っても良い。`Proc.new`は`proc`と同様

##procとlambdaの関係
procとlambdaは似ていますが違います。
rubyの文法から見ると使い方が違うという感じでしょうか。
簡単にいうとprocはオブジェクトを`new`から作るclassObjectです。
http://www.ruby-doc.org/core-2.1.0/Proc.html
>例
>`block = Proc.new {|x| puts x}`
>`block.call("hoge")`
>=>`hoge`

対してlambdaはオブジェクトを作るメソッドのようなものです。
実際、lambdaはkernelモジュールのメソッドです。
http://www.ruby-doc.org/core-2.1.0/Kernel.html#method-i-lambda
>例
>`block = lambda{|x| puts x}` #ruby1.8の場合
>`block = ->(x){ puts x}` #ruby1.9以上
>`block.call("hoge")`
>=>`hoge`

ではlambdaとprocのオブジェクトは同じなのでしょうか。
pryを使ってlambda?メソッドをテストしてみます。2.1.0はRubyのバージョンです

```pry
2.1.0 (main)> lambda {}.lambda?
=> true
2.1.0 (main)> proc {}.lambda?
=> false
```

上記を見ると２つは違うオブジェクトじゃないかと思ってしまいますね。

lambdaを実行するKernelのCソースを見ます。
下記ではlambdaはprocオブジェクトを作っているように見える。やはり同じprocオブジェクトなのか。

```c
  VALUE
rb_block_lambda(void)
{
    return proc_new(rb_cProc, TRUE);
}
```


ではlambda?はprocとlamdbaをどうやって判定しているのでしょうか。


procを実行するKernelのCソースも見てみましょう。

```c
VALUE
rb_block_proc(void)
{
    return proc_new(rb_cProc, FALSE);
}
``` 

`proc_new`の右の引数が`False`ですね。
下記の`Proc.new`のソースも`False`です。

```c
  static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
    VALUE block = proc_new(klass, FALSE);

    rb_obj_call_init(block, argc, argv);
    return block;
}
```       


ついでに`lambda?`メソッドのソースも見ておきます。

```c
               VALUE
rb_proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return proc->is_lambda ? Qtrue : Qfalse;
}         
```

つまり

| Rubyコード  | C コード     |            |
|:-----------|------------:|:----------:|
| lambda     | proc_new    |    TRUE    |
| proc       | proc_new    |    FALSE   |
| Proc.new   | proc_new    |    FALSE   |

lambdaを実行するのみTRUEを渡しています。


##lambdaを使ってみる。
lambdaを使う場合はどういう時に使う時が最善なのでしょうか。
直感的には矢印をさして式を書き変換するというイメージでしょうか。
実際にコードを書いてみましょう。
https://gist.github.com/hiroshiro/8962833/9ace1980002cf5a72e37921633bb1479b6633032

```ruby
##
# jpgがあればURLをセーブする。
##
def save_extract_jpg(doc)
  (doc/:img).each do |link|
    ->(url){ save_file(url) }.call(link[:src].to_s) if link[:src] =~ /.jpg/
  end
end

save_extract_jpg(doc)
```

`save_extract_jpg`のイテレーターの内部でlambda`->`を使って保存するようにしました。
しかしこれだとlambdaを使う意味がないような気がします。
lambdaは関数プログラミングを代表するメソッドなので
この場面で使うとしたら、jpgの大きさを計算するプログラムとかで活躍するかもしれません。
それは機会があればやってみたいです。

ということで今回はここまで
次回はクラス、モジュール、例外を勉強します。