せかいや

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

【JavaScript 1.7】【Ruby】JavaScript とRuby、yield の違い

 
■topic summary
study about "yield" in JavaScript.
The function containing the yield keyword is a generator.

 
yieldはRubyにあるから楽勝ー。
と思っていたけれど、JavaScriptRubyでは考え方が違うみたい。

 

JavaScript1.7は「開発者向けツール」で試さないこと

こんなコードを書いて↓

function f(max){
 var cur=1;
 for(var n=1; n<=max; n++){
  cur *= n;
  yield(cur*10);
  console.log('cur= ' + cur);
 }
}
f(4);

 
開発者向けツールで実行。
f:id:sekaiya:20131106163335j:plain
 
あれれ。
本と挙動が違う。

fを普通の関数のように呼び出しても何も出力されません。

と書いてあるけど。

 
chrome上のletもコンソール上では認識できなかったから、
コンソールで試しているのが良くないのかもしれない。
 
ファイルに書いてブラウザに読み込ませよう。

JavaScript 1.7 の一部の新機能を使うためには、JavaScript 1.7 が使いたいという宣言が必要です。

https://developer.mozilla.org/ja/docs/Web/JavaScript/New_in_JavaScript/1.7

ふむふむ。

 
■aaajs.html

<script type="application/javascript;version=1.7">
function f(max){
 var cur=1;
 for(var n=1; n<=max; n++){
  cur *= n;
  yield(cur);
  console.log('cur= ' + cur);
 }
}
f(4);
console.log(f(4).next());
</script>

 
f:id:sekaiya:20131106163714j:plain

おー。
想定通りnext()メソッドで取得した値しか出力されていない。


 

Ruby

Rubyのyieldはイテレータから、
イテレータ呼び出しに付属しているブロックに制御を移すもの。

def f
  yield
  yield
end
f(){p "hoge"}

■実行結果

"hoge"
"hoge"


 

JavaScript

Rubyに対して、JavaScriptのyieldは処理の中断。

<script type="application/javascript;version=1.7">
function p(obj){
 console.log(obj);
}
function f(){
 yield("hoge1");
 yield("hoge2");
}
var g=f(4);
p(g.next());
</script>

 
■実行結果

hoge1


nextで処理が進むところは外部イテレータ的。。
外部イテレータに関しては前勉強したところ
 (zipメソッドを実装したりしている)

でも、処理を中断して呼び出し元に制御を戻すというのは、Fiberと同じ概念だ。


Rubyオライリーを読み返すと、やはり記載があった。

ここで注意しておきたいのは、Fiber.yieldの処理とyield文の処理とがまったく異なるということだ

なるほどね。
JavaScript はFiber.yieldと同じ概念なのか。
最初からFiber.yieldについて考えていれば良かったんだ!
 

 

Fiber.yield

JavaScript のコードをRubyで再現してみる

JavaScript(再掲)

function f(){
 yield("hoge1");
 yield("hoge2");
}
var g=f(4);
p(g.next());
p(g.next());

 
■実行結果

hoge1
hoge2

 

Ruby

f = Fiber.new do |_|
  Fiber.yield("hoge1")
  Fiber.yield("hoge2")
end
p(f.resume)
p(f.resume)

 
■実行結果

"hoge1"
"hoge2"

Rubyではresumeメソッドで中断していた処理に制御を引き戻す

 

JavaScriptRubyの違い

どちらも「呼び出し元」と「呼び出し先」が存在する。
どちらも「呼び出し元」に値を戻すことができる(next,resumeの戻り値)

加えてRubyのFiber.yieldは「呼び出し先」にも値を戻すことができる

f = Fiber.new do |_|
  response = Fiber.yield("hoge1")
  p(response)
  Fiber.yield("hoge2")
end
p(f.resume)  #<= 最初のresumeに引数を与えるとFiberのブロック引数となる
p(f.resume("omiyage"))

 
■実行結果

"hoge1"
"omiyage"
"hoge2"

 
なるほどね。