Skip to content

Instantly share code, notes, and snippets.

@doitian
Created June 8, 2010 20:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save doitian/430615 to your computer and use it in GitHub Desktop.
Save doitian/430615 to your computer and use it in GitHub Desktop.

代码有点长,贴在gist了。

http://gist.github.com/430615#file_yield.js

主要思想就是把Enumerator里的程序分段存下来,然后手动控制执行。利用闭包保存状态。next()有负作用,懒得改了。

  • _s是一段不包含其它_开头函数的block.
  • _while, _if如字面
  • _yield 即yield

参数都需要函数。

run函数返回true时,表示yield了,这时next()返回。run返回false时,表示该段代码执行完毕。

其它语句如break, continue可以用类似方式加上。

灵感主要来自boost::lambda

好久没弄Javascript,不知道怎么样能去掉this前缀直接调用这些函数。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript" src="yield.js">
</script>
</body>
</html>
function Class(obj) {
var ctor = function () {
if (obj.initialize) {
obj.initialize.apply(this, arguments);
}
};
ctor.prototype = obj;
return ctor;
}
var WhileContext = Class({
initialize: function(condition, sequence) {
this.condition = condition;
this.sequence = sequence;
},
run: function() {
while (true) {
if (!this.sequence.started()) {
if (!this.condition()) {
return false;
}
}
if (this.sequence.run()) {
this.value = this.sequence.value;
return true;
}
this.sequence.reset();
}
}
});
var IfContext = Class({
initialize: function(condition, sequence) {
this.condition = condition;
this.sequence = sequence;
},
run: function() {
if (!this.sequence.started() && !this.condition())
{
return false;
}
if (this.sequence.run()) {
this.value = this.sequence.value;
return true;
}
this.sequence.reset();
return false;
}
});
var StatementsContext = Class({
initialize: function(proc) {
this.proc = proc;
},
run: function() {
this.proc();
return false;
}
});
var YieldContext = Class({
initialize: function(proc) {
this.proc = proc;
this.should_yield = true;
},
run: function() {
if (this.should_yield) {
this.value = this.proc();
this.should_yield = false;
return true;
}
else {
this.should_yield = true;
}
}
});
var SequenceContext = Class({
initialize: function(proc) {
this.context_list = [];
proc.call(this);
this.pc = -1;
},
run: function() {
if (this.pc == -1) {
this.pc = 0;
}
while (this.pc < this.context_list.length)
{
if (this.context_list[this.pc].run()) {
this.value = this.context_list[this.pc].value;
return true;
}
++this.pc;
}
return false;
},
reset: function() {
this.pc = -1;
},
started: function() {
return this.pc >= 0;
},
_while: function(condition) {
var context_list = this.context_list;
return function (proc) {
var sequence = new SequenceContext(proc);
context_list.push(new WhileContext(condition, sequence));
};
},
_if: function(condition) {
var context_list = this.context_list;
return function (proc) {
var sequence = new SequenceContext(proc);
context_list.push(new IfContext(condition, sequence));
};
},
_yield: function(proc) {
this.context_list.push(new YieldContext(proc));
},
_s: function(proc) {
this.context_list.push(new StatementsContext(proc));
}
});
var Enumerable = Class({
initialize: function(proc) {
this.sequence = new SequenceContext(proc);
if (this.sequence.run()) {
this.value = this.sequence.value;
return this;
}
return null;
},
next: function() {
if (this.sequence.run()) {
this.value = this.sequence.value;
return this;
}
return null;
},
takeWhile: function(condition) {
var base = this;
return new Enumerable(function() {
this._if(function(){return condition(base.value)})(function (){
this._yield(function(){return base.value});
this._while(function(){return base.next() && condition(base.value)})(function (){
this._yield(function(){return base.value});
});
});
});
}
});
function main() {
var oneTwoThree = new Enumerable(function(){
this._yield(function() {return 1});
this._yield(function() {return 2});
this._yield(function() {return 3});
});
document.write("<h1>One Two Three</h1><ul>");
for (; oneTwoThree; oneTwoThree = oneTwoThree.next()) {
document.write("<li>" + oneTwoThree.value + "</li>");
}
document.write("</ul>\n");
function Fib() {
var i = 0;
var j = 1;
return new Enumerable(function(){
this._while(function(){return true})(function(){
this._yield(function(){return i});
this._s(function(){
var next = i + j;
i = j;
j = next;
});
});
});
};
var orig = new Fib();
document.write("<h1>Fib take while < 100</h1><ul>");
var fib = orig.takeWhile(function(e){return e < 100;});
while (fib) {
document.write("<li>" + fib.value + "</li>");
fib = fib.next();
}
document.write("</ul>\n");
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment