READMEはこれからだ
npm登録した。複数の引数の引き渡しをするようにしてテストして、とか適当にやっていたら、ずいぶんかかった。exception handlingが、変なハマリ方した。ブラウザから利用できるようにしようと色々やってたら、なおのことハマった。なのでREADMEはこれからだ!→書いた
原理や使ったときの見た目は、【保存版】制御構造別非同期プログラミング完全制覇(サーバサイドJavascript・CoffeeScript)を参照。このページにもおいおい書いてく。
機能一覧
simple continuation
- _(firstargs,func1,func2,...)
- 引数に取った非同期関数を順次実行します
- firstargs
- func1に渡す引数を配列に納めたものです [arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます この配列の要素数は可変です
- func1(next,...)
- 最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。 next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます この引数の要素数は可変です
- func2(next,...)
- 以下、同様に好きな回数だけ続けられます。
用例はこちら。直前のブロックでのnextへの引数は、直後のブロックの追加引数になります。(この仕様は、他の関数も同じです)
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._ ['arg1','arg2']
,(next,str1,str2) ->
PRINT '[1st block]',->
PRINT 'str1:'+str1,->
PRINT 'str2:'+str2,->
next 'err'
,(next,errstr) ->
PRINT '[2nd block]',->
PRINT 'errstr:'+errstr,->
next()
,(next) ->
PRINT '[last block]',->
next()
if,switch後の合流もできます。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._ ['arg1','arg2']
,(next,str1,str2) ->
PRINT '[1st block]',->
if str1 == str2
PRINT 'str1 and str2:'+str1,->
next()
else
PRINT 'str1:'+str1,->
PRINT 'str2:'+str2,->
next()
,(next) ->
PRINT '[2nd block]',->
next()
Each (並行実行)
- \_each(obj, applyargs_array, applyfunc, endfunc)
- ArrayまたはObjectの各要素に、並行して同じ関数を適用します
- obj
- 処理するArrayまたはObject
- applyargs_array
- applyfuncに渡すその他の引数を配列にして指定します。 [arg1,arg2]を指定すると、applyfunc(val,index,next,arg1,arg2)が呼ばれます。
- applyfunc(val,index,next,...)
- 各要素に適用する関数です。objがArrayの場合、valは要素、indexは序数です。 処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて endfuncに渡されます。
- applyfunc(val,index,next,...)
- 各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。 処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて endfuncに渡されます。
- endfunc(...)
- 結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、 endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の 終了順です。弁別する必要のある場合、indexやkeyをnextの引数に加えて下さい。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._each [2,3,5,7,11]
,['arg1']
,(val,i,next,str1) ->
PRINT 'apply',->
next(val,i)
,(array_of_args_from_next1,array_of_args_from_next2) ->
for num in array_of_args_from_next1
console.log 'value: '+num.toString()
for num in array_of_args_from_next2
console.log 'counter: '+num.toString()
next()
for Loop
- \_for(index, f_judge, f_iter, firstarg_array, loopfunc, endfunc)
- 初期値・条件・イテレータを用いて、loopfuncを繰り返します。
- index
- カウンタの初期値です。
- f_judge(n) 返値=true,false
- カウンタに対する条件です。falseが返るとループが終了します。 ※この関数は同期関数でなければなりません。
- f_iter(n) 返値=カウンタ値
- カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、 カウンタに代入されます。 ※この関数は同期関数でなければなりません。
- firstarg_array
- loopfuncへの初回の引数を、配列で示します。 [arg1,arg2]を渡すと、loopfunc(n,next,arg1,arg2)が呼ばれます。
- loopfunc(n,\_break,\_next,...)
- 繰り返し実行する内容です。nはカウンタの現在値です。 続行する場合、\_next(...)を呼び出します。\_nextの引数は、次回のloopfuncの 呼び出しの際、引き渡されます。たとえば、\_next(arg3,arg4)を呼び出せば、 loopfunc(n,next,arg3,arg4)が呼ばれます。 繰り返しを中断する場合、\_break(...)を呼び出します。すると、 endfunc(n,...)が呼び出されます。
- endfunc(n,...)
- 繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._for 0,((n)-> n<10),((n)-> n+1)
,[]
,(n,_break,_next) ->
PRINT 'counter: '+n.toString(),->
_next()
,(n) ->
PRINT 'last counter: '+n.toString(),->
return
for in Array or Object
- \_for_in(obj, firstargs, loopfunc, endfunc)
- 配列またはオブジェクトの各要素に順々に関数を適用します。
- firstargs
- 初回のloopfuncの引数を配列にして示します。 [arg1,arg2]が渡されると、loopfunc(key,val,\_break,\_next,arg1,arg2)が呼ばれます。
- loopfunc(key,val,\_break,\_next,...)
- 繰り返し実行する関数です。Objectの場合、key,valが得られます 続行する場合\_next(...)、中断する場合\_break(...)を呼び出します。 \_nextの引数は、次回のloopfuncに渡されます。 \_breakの引数は、endfuncに渡されます。
- endfunc(...)
- 繰り返しの終了時に呼ばれます。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._for_in {bro:'ani',sis:'imo',dad:'tousan',mom:'kaasan'}
,[] #first args
,(key,val,_break,_next) ->
PRINT key+': '+val,->
_next()
,->
PRINT 'end',->
return
while Loop
- \_while(firstarg_array, loopfunc, endfunc)
- 繰り返しを行います。リトライループなどです。loopfuncの処理の終了時、\_breakと\_nextを呼び分ければ良いです。冒頭で分岐すれば普通のwhileのように、末尾(最奥)で分岐すればdo...whileのように振る舞います。
- firstarg_array
- 初回のf_judge, loopfuncの引数を配列にして示します。 [arg1,arg2]が渡されると、f_judge(arg1,arg2)が評価され、 trueならば、loopfunc(\_break,\_next,arg1,arg2)が呼ばれ、 falseならば、endfunc(arg1,arg2)が呼ばれます。
- loopfunc(\_break,\_next,...)
- 繰り返し行う非同期処理です。 繰り返しを中断するときは\_break(...)を呼ぶと、endfunc(...)が呼ばれます。 繰り返しを続行するときは\_next(...)を呼ぶと、f_judge(...)が評価され、 trueならば、loopfunc(\_break,\_next,arg1,arg2)が呼ばれ、 falseならば、endfunc(arg1,arg2)が呼ばれます。
- endfunc(...)
- 次の処理です。上記の通り、引数を受け取ることができます。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
cs._while [25*25, 25*88]
,(_break,_next,arg1,arg2) ->
PRINT arg1.toString(),->
PRINT arg2.toString(),->
if (arg2 % arg1) == 0
_break(arg1)
else
_next(arg2 % arg1,arg1)
,(arg) ->
PRINT 'result: '+arg.toString(),->
return
exception handling
- myexc = new exc
- 例外管理オブジェクトを生成します。ここではmyexcとします。
- myexc.\_try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)
- 例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
終了処理はf_finally内で行い、f_nextへと進みます。
現在のところ、f_catch,f_finally内で\_throwすると、終了処理は完了しません
- block_args_array
- blockに渡す引数を、配列にして指定します。 [arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。
- f_block(...)
- 実処理を行うスコープです。このexc例外を発生しうる関数を 呼び出すときはmyexcを渡して下さい。次へ進むときは myexc.\_finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。
- e_array
- f_catchで処理する例外の種類を列挙した配列です。 可読性のある文字列をお勧めします。
- f_catch(_e,...)
- \_throwに渡された例外の種類と、その他の\_throwに渡された引数が 得られます。e_arrayで列挙した例外は全て処理して下さい。 処理を終えたらmyexc.\_finally(...)を呼んで下さい。f_blockと同様です。
- f_finally(next,...)
- 終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。 f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。
- f_next(...)
- 次の処理を行う関数です。
- myexc.\_throw(\_e,...)
- 例外を発生させます。引数はf_catchに渡されます。
- myexc.\_finally(...)
- 例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。 引数はf_finallyに渡されます。
PRINT = (str,next) -> console.log str; setTimeout(next, 200)
myexc = new cs.exc
myexc._try([]
,->
#block
PRINT 'NEST1-1',->
PRINT 'NEST1-2',->
myexc._try([]
,->
#block
PRINT 'NEST2-1',->
PRINT 'NEST2-2',->
if true
myexc._throw 'err1'
else
PRINT 'NEST2-3',->
myexc._finally()
,['err2','err3']
,(_e)->
#catch
console.log _e
PRINT 'NEST2-CATCH',->
myexc._finally()
,(fnext)->
#finally
PRINT 'NEST2-FINALLY',->
fnext()
,->
PRINT 'NEST1-3',->
myexc._finally()
)
,['err1']
,(_e)->
#catch
console.log _e
PRINT 'NEST1-CATCH',->
myexc._finally()
,(fnext)->
#finally
PRINT 'NEST1-FINALLY',
fnext()
,next)
y combinator
- y(closure)
- closureの引数にclosure自身を渡します=再帰関数にします
無名クロージャを再帰関数にすることができます。
dummy = null
index = 10
(cs.y (func) ->
return (dummy)->
index -= 1
A ->
PRINT index.toString(),->
if index == 0
return next()
else
func(dummy)
)(dummy)
Todo
- 引数チェック
- firstarg_arrayが配列でないor(null)のときの処理など 関数を取る場合のチェック&nullチェック(devのみ?)
- 返値チェック
- イテレータや終了条件など、同期関数の返値チェック
- ループ関数に同期関数を入れられたとき
- stack overflowを避ける方法が有ればやる。さもなくば分かりやすくあぼん
- Javascript版の無意味な返値を消去
- 楽勝なはず?
CoffeeScript版ソースコード
ん?引用だけだと右の目次に出ない?
control-structures.coffee
((root, factory) ->
if FORCE_CLIENTSIDE?
root.cs = factory()
else if (typeof define == 'function' && define.amd)
define([], factory)
else if (typeof exports == 'object')
module.exports = factory()
else
root.cs = factory()
)(this, () ->
###
y combinator
###
y: (func) ->
return ((p) ->
return ->
return func(p(p)).apply(this,arguments)
)((p) ->
return ->
return func(p(p)).apply(this,arguments)
)
###
simple continuation
(継続のネストが深くなりすぎるのを防ぐため、
または、if,switchの後、容易に合流するために用います)
_(firstargs,func1,func2,...)
引数に取った非同期関数を順次実行します
firstargs
func1に渡す引数を配列に納めたものです
[arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます
この配列の要素数は可変です
func1(next,...)
最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。
next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます
この引数の要素数は可変です
func2(next,...)
以下、同様に好きな回数だけ続けられます。
###
_: ->
args = [].slice.call(arguments)
firstargs = args.shift()
(@.y (func) ->
return ->
arg = args.shift()
if arg?
passargs = [].slice.call(arguments)
passargs.unshift(func)
arg.apply(this,passargs)
).apply(this,firstargs)
###
Each (simultaneous)
_each(obj, applyargs_array, applyfunc, endfunc)
ArrayまたはObjectの各要素に、並行して同じ関数を適用します
obj
処理するArrayまたはObject
applyargs_array
applyfuncに渡すその他の引数を配列にして指定します。
[arg1,arg2]を指定すると、applyfunc(val,i,next,arg1,arg2)が呼ばれます。
applyfunc(val,i,next,...)
各要素に適用する関数です。objがArrayの場合、valは要素、iは序数です。
処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
endfuncに渡されます。
applyfunc(val,i,next,...)
各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。
処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
endfuncに渡されます。
endfunc(...)
結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、
endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の
終了順です。弁別する必要のある場合、iやkeyをnextの引数に加えて下さい。
###
_each: (obj, applyargs_array, applyfunc, endfunc) ->
((nextc) ->
isarr = ('Array' == Object.prototype.toString.call(obj).slice(8, -1))
num = 0
next = nextc()
if isarr
for val,i in obj
num++
applyfunc.apply this,[val,i,->
next.apply this,[num,endfunc].concat([].slice.call(arguments))
].concat(applyargs_array)
else
for key,val of obj
num++
applyfunc.apply this,[key,val,->
next.apply this,[num,endfunc].concat([].slice.call(arguments))
].concat(applyargs_array)
)(->
count = 0
result = []
return (num,next) ->
count++
if arguments.length > 0
args = [].slice.call(arguments).shift().shift()
for arg,i in args
if result.length <= i
result.push([])
result[i].push(arg)
if count == num
next.apply this,result
)
###
for Loop
_for(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc)
初期値・条件・イテレータを用いて、loopfuncを繰り返します。
i
カウンタの初期値です。
f_judge(n) 返値=true,false
カウンタに対する条件です。falseが返るとループが終了します。
※この関数は同期関数でなければなりません。
f_iter(n) 返値=カウンタ値
カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、
カウンタに代入されます。
※この関数は同期関数でなければなりません。
firstarg_array
loopfuncへの初回の引数を、配列で示します。
[arg1,arg2]を渡すと、loopfunc(n,break,next,arg1,arg2)が呼ばれます。
loopfunc(n,_break,_next,...)
繰り返し実行する内容です。nはカウンタの現在値です。
続行する場合、_next(...)を呼び出します。_nextの引数は、次回のloopfuncの
呼び出しの際、引き渡されます。たとえば、_next(arg3,arg4)を呼び出せば、
loopfunc(n,next,arg3,arg4)が呼ばれます。
繰り返しを中断する場合、_break(...)を呼び出します。すると、
endfunc(n,...)が呼び出されます。
endfunc(n,...)
繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。
###
_for: (i, f_judge, f_iter, firstarg_array, loopfunc, endfunc) ->
if firstarg_array?
firstarg_array = []
(@.y (func) ->
return ->
if f_judge i
loopfunc.apply this,[i,->
#_break
endfunc.apply this,[i].concat([].slice.call(arguments))
,->
#_next
i = f_iter i
func.apply this,arguments
].concat([].slice.call(arguments))
else
endfunc.apply this,[i].concat([].slice.call(arguments))
).apply this,firstarg_array
###
for in Array or Object
_for_in(obj, firstargs, loopfunc, endfunc)
firstargs
初回のloopfuncの引数を配列にして示します。
[arg1,arg2]が渡されると、loopfunc(key,val,_break,_next,arg1,arg2)が呼ばれます。
loopfunc(key,val,_break,_next,...)
繰り返し実行する関数です。Objectの場合、key,valが得られます
続行する場合_next(...)、中断する場合_break(...)を呼び出します。
_nextの引数は、次回のloopfuncに渡されます。
_breakの引数は、endfuncに渡されます。
endfunc(...)
繰り返しの終了時に呼ばれます。
###
_for_in: (obj, firstargs, loopfunc, endfunc) ->
i = 0
isarr = ('Array' == Object.prototype.toString.call(obj).slice(8, -1))
if isarr
indexlimit = obj.length
else
indexlimit = Object.keys(obj).length
(@.y (func) ->
return ->
if i < indexlimit
if isarr
loopfunc.apply this,[obj[i],i, ->
endfunc.apply this,arguments
,->
func.apply this,arguments
].concat([].slice.call(arguments))
else
key = Object.keys(obj)[i]
value = obj[key]
loopfunc.apply this,[key, value, ->
endfunc.apply this,arguments
,->
func.apply this,arguments
].concat([].slice.call(arguments))
else
endfunc.apply this,arguments
)(firstargs)
###
while Loop
_while(firstarg_array, loopfunc, endfunc)
終了条件のみを指定した繰り返しを行います。リトライループなどです。
firstarg_array
初回のf_judge, loopfuncの引数を配列にして示します。
[arg1,arg2]が渡されると、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
loopfunc(_break,_next,...)
繰り返し行う非同期処理です。
繰り返しを中断するときは_break(...)を呼ぶと、endfunc(...)が呼ばれます。
繰り返しを続行するときは_next(...)を呼ぶと、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
endfunc(...)
次の処理です。上記の通り、引数を受け取ることができます。
###
_while: (firstarg_array, loopfunc, endfunc) ->
if firstarg_array?
firstarg_array = []
(@.y (func) ->
return ->
loopfunc.apply this,[->
#_break
endfunc.apply this,[].slice.call(arguments)
,->
#_next
func.apply this,arguments
].concat([].slice.call(arguments))
).apply this,firstarg_array
###
exception handling
myexc = new exc
例外管理オブジェクトを生成します。ここではmyexcとします。
myexc._try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)
例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
終了処理はf_finally内で行い、f_nextへと進みます。
現在のところ、f_catch,f_finally内で_throwすると、終了処理は完了しません
block_args_array
blockに渡す引数を、配列にして指定します。
[arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。
f_block(myexc,...)
実処理を行うスコープです。このexc例外を発生しうる関数を
呼び出すときはmyexcを渡して下さい。次へ進むときは
myexc._finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。
e_array
f_catchで処理する例外の種類を列挙した配列です。
可読性のある文字列をお勧めします。
f_catch(myexc,_e,...)
_throwに渡された例外の種類と、その他の_throwに渡された引数が
得られます。e_arrayで列挙した例外は全て処理して下さい。
処理を終えたらmyexc._finally(...)を呼んで下さい。f_blockと同様です。
f_finally(myexc,next,...)
終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。
f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。
f_next(...)
次の処理を行う関数です。
myexc._throw(_e,...)
例外を発生させます。引数はf_catchに渡されます。
myexc._finally(...)
例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。
引数はf_finallyに渡されます。
###
exc: class exc
_stack = []
constructor: ->
_try: (block_args_array,block,e_array,arg_catch,arg_finally,arg_next) ->
_stack.push({e_array:e_array,_catch:arg_catch,_finally:arg_finally,next:arg_next})
block.apply this,block_args_array
fa = ->
stackpop = _stack.pop()
stackpop._finally.apply this,[].slice.call(arguments)
_finally: ->
stackpop = _stack.pop()
stackpop._finally.apply this,[stackpop.next].concat([].slice.call(arguments))
y = (func) ->
return ((p) -> return -> return func(p(p)).apply(this,arguments)
)((p) -> return -> return func(p(p)).apply(this,arguments)
)
_w = (firstarg_array, loopfunc, endfunc) ->
if firstarg_array?
firstarg_array = []
(y (func) ->
return ->
loopfunc.apply this,[->
#_break
endfunc.apply this,[].slice.call(arguments)
,->
#_next
func.apply this,arguments
].concat([].slice.call(arguments))
).apply this,firstarg_array
_throw: (_e) =>
catchset = {}
_w [],(_break,_next)=>
catchset = _stack.pop()
if catchset?
_stack.push catchset
result = catchset.e_array.indexOf _e
if result != -1
catchset._catch.apply this,[_e].concat([].slice.call(arguments))
else
if arguments.length <= 2
fa.apply this,[_next]
else
fa.apply this,[].slice.call(arguments).shift().shift().unshift(_next)
else
_break()
,->
console.log 'uncaught exception: '+_e.toString()
process.exit 1
)
javascript版ソースコード
CoffeeScript版が安全な感じになったらこっち特有の変更もする。(余計な返値の抑制)
control-structures.js
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
(function(root, factory) {
if (typeof FORCE_CLIENTSIDE !== "undefined" && FORCE_CLIENTSIDE !== null) {
return root.cs = factory();
} else if (typeof define === 'function' && define.amd) {
return define([], factory);
} else if (typeof exports === 'object') {
return module.exports = factory();
} else {
return root.cs = factory();
}
})(this, function() {
var exc;
return {
/*
y combinator
*/
y: function(func) {
return (function(p) {
return function() {
return func(p(p)).apply(this, arguments);
};
})(function(p) {
return function() {
return func(p(p)).apply(this, arguments);
};
});
},
/*
simple continuation
(継続のネストが深くなりすぎるのを防ぐため、
または、if,switchの後、容易に合流するために用います)
_(firstargs,func1,func2,...)
引数に取った非同期関数を順次実行します
firstargs
func1に渡す引数を配列に納めたものです
[arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます
この配列の要素数は可変です
func1(next,...)
最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。
next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます
この引数の要素数は可変です
func2(next,...)
以下、同様に好きな回数だけ続けられます。
*/
_: function() {
var args, firstargs;
args = [].slice.call(arguments);
firstargs = args.shift();
return (this.y(function(func) {
return function() {
var arg, passargs;
arg = args.shift();
if (arg != null) {
passargs = [].slice.call(arguments);
passargs.unshift(func);
return arg.apply(this, passargs);
}
};
})).apply(this, firstargs);
},
/*
Each (simultaneous)
_each(obj, applyargs_array, applyfunc, endfunc)
ArrayまたはObjectの各要素に、並行して同じ関数を適用します
obj
処理するArrayまたはObject
applyargs_array
applyfuncに渡すその他の引数を配列にして指定します。
[arg1,arg2]を指定すると、applyfunc(val,i,next,arg1,arg2)が呼ばれます。
applyfunc(val,i,next,...)
各要素に適用する関数です。objがArrayの場合、valは要素、iは序数です。
処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
endfuncに渡されます。
applyfunc(val,i,next,...)
各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。
処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
endfuncに渡されます。
endfunc(...)
結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、
endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の
終了順です。弁別する必要のある場合、iやkeyをnextの引数に加えて下さい。
*/
_each: function(obj, applyargs_array, applyfunc, endfunc) {
return (function(nextc) {
var i, isarr, j, key, len, next, num, results, results1, val;
isarr = 'Array' === Object.prototype.toString.call(obj).slice(8, -1);
num = 0;
next = nextc();
if (isarr) {
results = [];
for (i = j = 0, len = obj.length; j < len; i = ++j) {
val = obj[i];
num++;
results.push(applyfunc.apply(this, [
val, i, function() {
return next.apply(this, [num, endfunc].concat([].slice.call(arguments)));
}
].concat(applyargs_array)));
}
return results;
} else {
results1 = [];
for (key in obj) {
val = obj[key];
num++;
results1.push(applyfunc.apply(this, [
key, val, function() {
return next.apply(this, [num, endfunc].concat([].slice.call(arguments)));
}
].concat(applyargs_array)));
}
return results1;
}
})(function() {
var count, result;
count = 0;
result = [];
return function(num, next) {
var arg, args, i, j, len;
count++;
if (arguments.length > 0) {
args = [].slice.call(arguments).shift().shift();
for (i = j = 0, len = args.length; j < len; i = ++j) {
arg = args[i];
if (result.length <= i) {
result.push([]);
}
result[i].push(arg);
}
}
if (count === num) {
return next.apply(this, result);
}
};
});
},
/*
for Loop
_for(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc)
初期値・条件・イテレータを用いて、loopfuncを繰り返します。
i
カウンタの初期値です。
f_judge(n) 返値=true,false
カウンタに対する条件です。falseが返るとループが終了します。
※この関数は同期関数でなければなりません。
f_iter(n) 返値=カウンタ値
カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、
カウンタに代入されます。
※この関数は同期関数でなければなりません。
firstarg_array
loopfuncへの初回の引数を、配列で示します。
[arg1,arg2]を渡すと、loopfunc(n,break,next,arg1,arg2)が呼ばれます。
loopfunc(n,_break,_next,...)
繰り返し実行する内容です。nはカウンタの現在値です。
続行する場合、_next(...)を呼び出します。_nextの引数は、次回のloopfuncの
呼び出しの際、引き渡されます。たとえば、_next(arg3,arg4)を呼び出せば、
loopfunc(n,next,arg3,arg4)が呼ばれます。
繰り返しを中断する場合、_break(...)を呼び出します。すると、
endfunc(n,...)が呼び出されます。
endfunc(n,...)
繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。
*/
_for: function(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc) {
if (firstarg_array != null) {
firstarg_array = [];
}
return (this.y(function(func) {
return function() {
if (f_judge(i)) {
return loopfunc.apply(this, [
i, function() {
return endfunc.apply(this, [i].concat([].slice.call(arguments)));
}, function() {
i = f_iter(i);
return func.apply(this, arguments);
}
].concat([].slice.call(arguments)));
} else {
return endfunc.apply(this, [i].concat([].slice.call(arguments)));
}
};
})).apply(this, firstarg_array);
},
/*
for in Array or Object
_for_in(obj, firstargs, loopfunc, endfunc)
firstargs
初回のloopfuncの引数を配列にして示します。
[arg1,arg2]が渡されると、loopfunc(key,val,_break,_next,arg1,arg2)が呼ばれます。
loopfunc(key,val,_break,_next,...)
繰り返し実行する関数です。Objectの場合、key,valが得られます
続行する場合_next(...)、中断する場合_break(...)を呼び出します。
_nextの引数は、次回のloopfuncに渡されます。
_breakの引数は、endfuncに渡されます。
endfunc(...)
繰り返しの終了時に呼ばれます。
*/
_for_in: function(obj, firstargs, loopfunc, endfunc) {
var i, indexlimit, isarr;
i = 0;
isarr = 'Array' === Object.prototype.toString.call(obj).slice(8, -1);
if (isarr) {
indexlimit = obj.length;
} else {
indexlimit = Object.keys(obj).length;
}
return (this.y(function(func) {
return function() {
var key, value;
if (i < indexlimit) {
if (isarr) {
return loopfunc.apply(this, [
obj[i], i, function() {
return endfunc.apply(this, arguments);
}, function() {
return func.apply(this, arguments);
}
].concat([].slice.call(arguments)));
} else {
key = Object.keys(obj)[i];
value = obj[key];
return loopfunc.apply(this, [
key, value, function() {
return endfunc.apply(this, arguments);
}, function() {
return func.apply(this, arguments);
}
].concat([].slice.call(arguments)));
}
} else {
return endfunc.apply(this, arguments);
}
};
}))(firstargs);
},
/*
while Loop
_while(firstarg_array, loopfunc, endfunc)
終了条件のみを指定した繰り返しを行います。リトライループなどです。
firstarg_array
初回のf_judge, loopfuncの引数を配列にして示します。
[arg1,arg2]が渡されると、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
loopfunc(_break,_next,...)
繰り返し行う非同期処理です。
繰り返しを中断するときは_break(...)を呼ぶと、endfunc(...)が呼ばれます。
繰り返しを続行するときは_next(...)を呼ぶと、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
endfunc(...)
次の処理です。上記の通り、引数を受け取ることができます。
*/
_while: function(firstarg_array, loopfunc, endfunc) {
if (firstarg_array != null) {
firstarg_array = [];
}
return (this.y(function(func) {
return function() {
return loopfunc.apply(this, [
function() {
return endfunc.apply(this, [].slice.call(arguments));
}, function() {
return func.apply(this, arguments);
}
].concat([].slice.call(arguments)));
};
})).apply(this, firstarg_array);
},
/*
exception handling
myexc = new exc
例外管理オブジェクトを生成します。ここではmyexcとします。
myexc._try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)
例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
終了処理はf_finally内で行い、f_nextへと進みます。
現在のところ、f_catch,f_finally内で_throwすると、終了処理は完了しません
block_args_array
blockに渡す引数を、配列にして指定します。
[arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。
f_block(myexc,...)
実処理を行うスコープです。このexc例外を発生しうる関数を
呼び出すときはmyexcを渡して下さい。次へ進むときは
myexc._finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。
e_array
f_catchで処理する例外の種類を列挙した配列です。
可読性のある文字列をお勧めします。
f_catch(myexc,_e,...)
_throwに渡された例外の種類と、その他の_throwに渡された引数が
得られます。e_arrayで列挙した例外は全て処理して下さい。
処理を終えたらmyexc._finally(...)を呼んで下さい。f_blockと同様です。
f_finally(myexc,next,...)
終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。
f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。
f_next(...)
次の処理を行う関数です。
myexc._throw(_e,...)
例外を発生させます。引数はf_catchに渡されます。
myexc._finally(...)
例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。
引数はf_finallyに渡されます。
*/
exc: exc = (function() {
var _stack, _w, fa, y;
_stack = [];
function exc() {
this._throw = bind(this._throw, this);
}
exc.prototype._try = function(block_args_array, block, e_array, arg_catch, arg_finally, arg_next) {
_stack.push({
e_array: e_array,
_catch: arg_catch,
_finally: arg_finally,
next: arg_next
});
return block.apply(this, block_args_array);
};
fa = function() {
var stackpop;
stackpop = _stack.pop();
return stackpop._finally.apply(this, [].slice.call(arguments));
};
exc.prototype._finally = function() {
var stackpop;
stackpop = _stack.pop();
return stackpop._finally.apply(this, [stackpop.next].concat([].slice.call(arguments)));
};
y = function(func) {
return (function(p) {
return function() {
return func(p(p)).apply(this, arguments);
};
})(function(p) {
return function() {
return func(p(p)).apply(this, arguments);
};
});
};
_w = function(firstarg_array, loopfunc, endfunc) {
if (firstarg_array != null) {
firstarg_array = [];
}
return (y(function(func) {
return function() {
return loopfunc.apply(this, [
function() {
return endfunc.apply(this, [].slice.call(arguments));
}, function() {
return func.apply(this, arguments);
}
].concat([].slice.call(arguments)));
};
})).apply(this, firstarg_array);
};
exc.prototype._throw = function(_e) {
var catchset;
catchset = {};
return _w([], (function(_this) {
return function(_break, _next) {
var result;
catchset = _stack.pop();
if (catchset != null) {
_stack.push(catchset);
result = catchset.e_array.indexOf(_e);
if (result !== -1) {
return catchset._catch.apply(_this, [_e].concat([].slice.call(arguments)));
} else {
if (arguments.length <= 2) {
return fa.apply(_this, [_next]);
} else {
return fa.apply(_this, [].slice.call(arguments).shift().shift().unshift(_next));
}
}
} else {
return _break();
}
};
})(this), function() {
console.log('uncaught exception: ' + _e.toString());
return process.exit(1);
});
};
return exc;
})()
};
});