VimScript その後

最終的にこんな感じになりました。
本当に誰得……。

[使い方]

" 実装部は下記参照

function! s:minus(a, b)
	return a:a - a:b
endfunction

" s:apply で関数を評価する
echo s:apply("s:minus", 5, 2)
" 3

let joint = {}
function! joint.apply(a, b)
	return a:a.a:b
endfunction

" 辞書が apply 関数を持っていれば、それを評価する
echo s:apply(joint, "hoge", "foo")
" hogefoo

" 無名関数を作成
" a:1 と a:2 は引数の順番
let plus = s:lambda("a:1 + a:2")
" s:apply で評価
echo s:apply(plus, 2, 5)
" 7

" s:bind で、部分適用出来る
" _1 は、第一引数が渡される
let twice = s:bind(plus, _1, _1)
" こっちも s:apply で評価
echo s:apply(twice, 10)
echo s:apply(twice, [1, 2], [3, 4])
" 20
" [1, 2, 1, 2]

" こんな使い方も
let equal_type = s:lambda("type(a:1) == type(a:2)")
echo s:apply(equal_type, 10, 4)
echo s:apply(equal_type, [1, 2, 3], {"one": 1, "two" : 2, "three" : 3})
" 1
" 0

" がんばれば再帰も出来る
" 自分を評価するには self を使用する
let fact = s:lambda("(a:1 == 1) ? 1 : s:apply(self, a:1-1) * a:1")
echo s:apply(fact, 4)
" 24
let sum = s:lambda("(len(a:1) == 1) ? a:1[0] : s:apply(self, a:1[1:-1]) + a:1[0]")
echo s:apply(sum, range(1, 10))
" 55

" ちょっと複雑ですがこんな事も…!!
function! s:var(var)
    let func = { "var" : a:var }
    function! func.apply(...) dict
        return self.var
    endfunction
    retur func
endfunction

function! s:if(pred, then, else)
    let func = { 
        \ "pred" : a:pred,
        \ "then" : a:then,
        \ "else" : a:else
    \}
    function! func.apply(...) dict
        if s:apply_args_list(self.pred, a:000)
            return s:apply_args_list(self.then, a:000)
        else
            return s:apply_args_list(self.else, a:000)
        endif
    endfunction
    return func
endfunction

let is_dict = s:bind(equal_type, _1, {})
let get_dict = s:if(is_dict, _1, s:var("nothing"))
echo s:apply(get_dict, {"dict" : 10})
echo s:apply(get_dict, "hoge")
" {'dict': 10}
" nothing

[出力]

3
hogefoo
7
20
[1, 2, 1, 2]
1
0
24
55
{'dict': 10}
nothing

まぁ概ねやりたかったことは出来たかな。
組み込み関数の call と eval が強力ですね……。
動的言語は書くのが楽です。
しかし、再帰とかマジ誰得。


さて、切りがいいので、そろそろ github 辺りにまとめたい所。
次は、range 回りの処理を書く予定。


上記のソースの実装は以下から

function! s:apply(func,...)
    if type(a:func) == type({}) && has_key(a:func, "apply")
        return call(a:func.apply, a:000, a:func)
    elseif(type(a:func) == type(function("function")))
        return call(a:func, a:000)
    else
        return call(function(a:func), a:000)
    endif
endfunction

function! s:apply_args_list(func, args)
    return call("s:apply", [a:func] + a:args)
endfunction

function! s:dict_func(func)
    let dict = { "func" : a:func }
    function! dict.apply(...) dict
        return call(self.func, a:000[1:-1], a:1)
    endfunction
    return dict
endfunction

function! s:placeholders(args_no)
    let func = {"args_no" : a:args_no}
    function! func.apply(...) dict
        return a:000[self.args_no]
    endfunction
    return func
endfunction
let _1 = s:placeholders(0)
let _2 = s:placeholders(1)
let _3 = s:placeholders(2)

function! s:bind(func, ...)
    let func = {"func" : a:func, "args" : a:000}
    function! func.apply(...) dict
        let args=[]
        for var in self.args
            if type(var) == type({}) && has_key(var, "apply")
                call add(args, call(var.apply, a:000, var))
            else
                call add(args, var)
            endif
            unlet var
        endfor
        return s:apply_args_list(self.func, args)
    endfunction
    return func
endfunction

function! s:lambda(lambda)
    let func = { "lambda" : a:lambda }
    function! func.apply(...) dict
        return eval(self.lambda)
    endfunction
    return func
endfunction

function! s:minus(a, b)
    return a:a - a:b
endfunction

" s:apply で関数を評価する
echo s:apply("s:minus", 5, 2)

let joint = {}
function! joint.apply(a, b)
    return a:a.a:b
endfunction

" 辞書が apply 関数を持っていれば、それを評価する
echo s:apply(joint, "hoge", "foo")

" 無名関数を作成
" a:1 と a:2 は引数の順番
let plus = s:lambda("a:1 + a:2")
" s:apply で評価
echo s:apply(plus, 2, 5)

" s:bind で、部分適用出来る
" _1 は、第一引数が渡される
let twice = s:bind(plus, _1, _1)
" こっちも s:apply で評価
echo s:apply(twice, 10)
echo s:apply(twice, [1, 2], [3, 4])

" こんな使い方も
let equal_type = s:lambda("type(a:1) == type(a:2)")
echo s:apply(equal_type, 10, 4)
echo s:apply(equal_type, [1, 2, 3], {"one": 1, "two" : 2, "three" : 3})

" がんばれば再帰も出来る
" 自分を評価するには self を使用する
let fact = s:lambda("(a:1 == 1) ? 1 : s:apply(self, a:1-1) * a:1")
echo s:apply(fact, 4)

let sum = s:lambda("(len(a:1) == 1) ? a:1[0] : s:apply(self, a:1[1:-1]) + a:1[0]")
echo s:apply(sum, range(1, 10))


" ちょっと複雑ですがこんな事も…!!
function! s:var(var)
    let func = { "var" : a:var }
    function! func.apply(...) dict
        return self.var
    endfunction
    retur func
endfunction

function! s:if(pred, then, else)
    let func = { 
        \ "pred" : a:pred,
        \ "then" : a:then,
        \ "else" : a:else
    \}
    function! func.apply(...) dict
        if s:apply_args_list(self.pred, a:000)
            return s:apply_args_list(self.then, a:000)
        else
            return s:apply_args_list(self.else, a:000)
        endif
    endfunction
    return func
endfunction

let is_dict = s:bind(equal_type, _1, {})
let get_dict = s:if(is_dict, _1, s:var("nothing"))
echo s:apply(get_dict, {"dict" : 10})
echo s:apply(get_dict, "hoge")