Ruby の binding オブジェクトと eval()

Ruby では現在のスコープのコンテキストを bindig オブジェクトとして扱う事が出来ます。

def func a, b
    add = a + b

    # このメソッド内のコンテキストを返す
    binding
end

p func 1, 2
# => #<Binding:0x00000000c0fe18>

[binding オブジェクトと #eval()]

bindig の利用方法はいくつかありますが、#eval() で使用するのがわかりやすいです。

def func a, b
    add = a + b

    # このメソッド内のコンテキストを返す
    binding
end

context = func 1, 2
# eval 内で func の変数などが参照できる
p eval("add + a + b", context)
# => 6

このように eval() 内で指定した binding のコンテキストを参照する事が出来ます。

Vim 8.0 で追加された関数 funcref()

Vim 8.0 で新しく funcref() という関数が追加されました。 これは function() と同じように関数参照を取得する関数になりますが、『関数が上書きされても元の関数を呼び出す』という違いがあります。

function! Test()
    return "Test"
endfunction

let Function = function("Test")
let Funcref  = funcref("Test")

echo Function()
" => "Test"
echo Funcref()
" => "Test"

" 関数を上書きする
function! Test()
    return "new Test"
endfunction

" function() は上書きした関数を呼び出す
echo Function()
" => "new Test"

" funcref() は元の関数を呼び出す
echo Funcref()
" => "Test"

上書きされる可能性がある関数の参照を取得したい場合は funcref() を使用したほうが安全かもしれません。

Vim 8.0 で追加された関数 exepath()

Vim 8.0 で新しく exepath() という関数が追加されました。 これは『絶対パス相対パス、または $PATH の中に存在する場合は、そのフルパスを返す』という関数になります。

echo exepath("gvim")
" => /usr/local/bin/gvim

echo exepath("ruby")
" => /usr/bin/ruby

たまに Vim 自身がどの実行ファイルを参照しているのかを確認したい時があるので、地味に便利です。

Vim 8.0 で追加された機能 closure

Vim 8.0 では関数に closure 機能が追加されました。 これは、関数外のローカル変数をキャプチャするための機能です。

function! s:init()
    let value = 0

   " closure キーワードを追加することで関数外のローカル変数をキャプチャできる
    function! s:count() closure
        let value += 1
        return value
    endfunction
endfunction

" 関数を定義する
call s:init()

echo s:count()
" => 1
echo s:count()
" => 2
echo s:count()
" => 3

Vim 8.0 で追加された機能 タイマーを一時停止する

前回書き忘れたので追記。 timer_pause() を使用します。

" 一時停止
call timer_pause(timer_id, 1)

" 再開
call timer_pause(timer_id, 0)

これで一時的にタイマーを停止したり、任意のタイミングで再開する事が出来ます。

Vim 8.0 で追加された機能 タイマー

Vim 8.0 ではついに Vim script にタイマー機能が追加されました!
この機能を使用することで処理をブロックしないで関数を呼び出すことが出来ます。

[基本的な使い方]

タイマーは timer_start() 関数を使用して任意の関数を n 時間後に呼び出す事が出来ます。

function! s:disp(timer)
    echo "callback"
endfunction

" 第一引数に時間、第二引数に呼び出す関数参照を渡す
" 1000ミリ秒後に s:disp をブロックしないで呼び出す
call timer_start(1000, function("s:disp"))

[タイマーを止める]

タイマーを止めたい場合、timer_start() が返した ID と timer_stop() を利用します。

" タイマーIDを受け取る
function! s:disp(timer)
    echo "callback"
endfunction

let s:id = timer_start(1000, function("s:disp"))

" ID のタイマーを止める
call timer_stop(s:id)

コールバック関数が受け取る timertimer_start() で生成された ID になります。 また、timer_stopall() ですべてのタイマーを止める事が出来ます。

[回数を指定する]

timer_start() の第三引数にリピートする回数を指定する事が出来ます。

let s:counter = { "value" : 0 }
function! s:counter.count(...)
    echo self.value
    let self.value += 1
endfunction

" 1000ms ごとに s:counter.count() を 3回呼び出す
call timer_start(1000, s:counter.count, { "repeat" : 3 })

また、-1 をすることでタイマーを止めるまで呼ばれ続けます。

let s:counter = { "value" : 0 }
function! s:counter.count(timer)
    echo self.value
    let self.value += 1

   " 5回呼ばれたら止める
    if self.value >= 5
        call timer_stop(a:timer)
    endif
endfunction

" 1000ms ごとに s:counter.count() を呼び出し続ける
call timer_start(1000, s:counter.count, { "repeat" : -1 })

かなり便利。