Vim script でコルーチンな処理を考えてみた
元々 Vim script でスレッド処理が行いたかったのですが、残念ながらどうあがいてもそれは無理なので
『それならばコルーチンを使って細かく処理を分ければいいのでは?』
といったことが事がきっかけ。
まぁコルーチン自体は使ったことがないんですが。
で、いろいろと構文を考えてみたんですけど、現状はこんな感じ。
[使い方]
function! s:func() Coroutine Echo "hello" Yield Echo "," Yield Echo "world" Yield Echo "end" endfunction function! s:test_func() call s:func() call s:func() call s:func() call s:func() call s:func() endfunction call s:test_func() function! s:factorial() Coroutine Let n = 1 Let val = 1 while 1 Let val = val * n Let n += 1 Yield val endwhile endfunction function! s:test_fact() for n in range(10) echo s:factorial() endfor endfunction call s:test_fact()
[出力]
hello , world end end 1 2 6 24 120 720 5040 40320 362880 3628800
Vim script だとコンテキストの保存や復元は簡単なんですけど、スキップ命令がないので全部 if 文で囲っています。
Let や Echo を使用しているのはその為。
ここら辺もどうにか簡単に出来ないか考えてみたんですけど、これが一番マシかなーと。
ちなみにこんな案もありました。
function! s:func() Coroutine echo "hello" en|Yield" echo "," en|Yield echo "," en|Yield en endfunction function! s:factorial() Coroutine let n = 1 let val = 1 en | Yield while 1 let val = val * n let n += 1 YieldLoop val endwhile en endfunction
こっちは広い範囲を if 文で囲っているんですが、en とか余計なものを書く必要が…。
今見るとやっぱり酷い。
とりあえず、せっかく考えてみたのでどこかで使ってみたい。
あと for 文は未対応(ってか、対応している構文の方が少ない気が。
[ソース]
let s:context = {} function! g:context_reset() let s:context = {} endfunction function! s:context_save(l, id) let s:context[a:id] = deepcopy(a:l) endfunction function! s:context_load(l, id) if has_key(s:context, a:id) call extend(a:l, s:context[a:id]) endif endfunction function! s:context_reset(id) if has_key(s:context, a:id) unlet s:context[a:id] endif endfunction command! -bar ContextSave :call s:context_save(l:, expand("<sfile>")) command! -bar ContextLoad :call s:context_load(l:, expand("<sfile>")) command! -bar ContextReset :call s:context_reset(expand("<sfile>")) command! -bar Coroutine \ let suspend_line = 0 \| ContextLoad command! -nargs=* Suspend \ let suspend_line = expand("<slnum>") \| ContextSave \| return <args> command! -nargs=* Skipper \ if expand("<slnum>") > (suspend_line+1) \| execute <q-args> \| endif command! -nargs=* Yield \ execute "Skipper Suspend ".<q-args> \| if expand("<slnum>") == (suspend_line) \| let suspend_line = 0 \| endif command! -nargs=* Echo \ Skipper echo <args> command! -nargs=* Let \ Skipper let <args>
最近、関数定義するよりもコマンド定義するほうが楽に感じる今日この頃