読者です 読者をやめる 読者になる 読者になる

Vim 7.4.2268 でインクリメンタルサーチ時に移動するキーが <C-p>/<C-n> から <C-t>/<C-g> に変更された

Vim

[関連]

secret-garden.hatenablog.com

Vim 7.4.2259 でインクリメンタルサーチ中にマッチ位置を起動する機能が追加されたんですが、既存のキーの挙動を破壊していたため別のキーに割り当てられました。

[Vim 7.4.2268 からの挙動]

Vim 7.4.2268 では、

  • <C-t> で前のマッチ
  • <C-g> で次のマッチ

というような挙動になり、<C-n><C-p> は以前の挙動(検索履歴の移動)に戻りました。

Vim 7.4.2259 でインクリメンタルサーチ中にマッチ位置を移動する機能が追加された

Vim

[追記]

Vim 7.4.2268 で挙動が変わりました。

secret-garden.hatenablog.com

Vim 7.4.2259 で 'incsearch' を有効にして検索(/)した際にマッチした単語の位置を移動する機能が実装されました。

github.com

これにより

  • <C-p> で前のマッチ
  • <C-n> で次のマッチ

にカーソルを移動することが出来ます。

[注意]

これにより <C-p>/<C-n> の挙動が変更されたため、それ以前の『検索履歴を移動する』という機能が使えなくなりました。
もし『検索履歴を移動する』という機能を使用したいのであれば 'incsearch' を無効にすることで以前のような挙動を行うことが出来ます。

set noincsearch

Ruby 2.3 の Numeric#positive? と Numeric#negative?

Ruby

Ruby 2.3 から Numeric#positive?Numeric#negative? が追加されました。
これはそれぞれ self > 0self < 0 を返します。

p  42.positive?
# => true
p -42.positive?
# => fasle
p  42.negative?
# => false
p -42.negative?
# => true
p 0.positive?
# => false
p 0.negative?
# => false

Rubocop では Ruby 2.3 を使ってると value > 0value.positive? を使えと怒られます。

Vim script の lambda で execute() を使うとローカル変数がキャプチャされない

Vim

さて、Vim script の lambda 内でローカル変数を使用すると自動的にキャプチャが行われます。

function! s:main()
    let value = 42
    let F = { -> value + value }

    echo F()
   " => 84

    let value = -4
    echo F()
   " => -8
endfunction
call s:main()

lambda 内で execute() を利用すると変数がキャプチャされない

例えば、次のように lambda 内で let を使用したい場合は execute("") を介してコマンドを呼び出します。

echo { -> [execute("let value = 42"), value + value][-1] }()
" => 84

しかし、execute() 内でローカル変数を使用した場合、キャプチャは行われません。

function! s:main()
    let value = 0
    let F = { -> execute("let value += 1") }

   " Error: E121: 未定義の変数です: value
    call F()
endfunction
call s:main()

これは execute() 内で『変数が使用されたかされてないか』とチェックしてないからぽいです。 例えば、次のようにして execute() 外でローカル変数を使用すれば問題なくキャプチャが行われます。

function! s:main()
    let value = 0
    let F = { -> [execute("let value += 1"), value] }

   " OK
    call F()
    call F()
    call F()

   " ローカル変数は書き換えれてる
    echo value
   " => 3
endfunction
call s:main()

意図しない動作ではあるけど、仕様ぽいなあ。

Ruby で -"42" としたら -42 が返ってきてほしい

Ruby
class String
    def -@
        -to_i
    end
end

p -"42"
# => -42

お気持ちです。

Vim script の funcref() を用いても関数の closure を使用するべきではない

Vim

タイトルがすでに結論なのですが、この前追加された funcref() を使用することで、以下のような問題を回避することが出来ます。

secret-garden.hatenablog.com

function! Foo()
  let x = 0
  function! Bar() closure
    let x += 1
    return x
  endfunction
"   return function('Bar')
  " function() ではなくて funcref() を使用することで
  " 関数が上書きされた場合の問題を回避する
  return funcref('Bar')
endfunction

let F = Foo()
echo F()
" => 1
echo F()
" => 2
echo F()
" => 3

" Bar() 関数が書き換えられても問題ない
let F2 = Foo()

" F() はそのまま動作する
echo F()
" => 4
echo F2()
" => 1
echo F()
" => 5

ではこれで問題ないのか?

確かに funcref() を使用することで問題になっていたコードは意図した動作をするようになりました。
しかし、 funcref() を利用しても依然として、

  • 関数外で参照できる関数を定義してしまう
  • 既存の関数を上書きしてしまう

と、いう問題は残ったままです。
斗くんい funcref() を使用しても『関数を上書きしてしまう』という問題は解決していません。
このような問題が解決しない限りは Vim script でこのような目的の closure は使用するべきではないと個人的に考えています。 Vim script で closure を利用したいのであれば素直にラムダを使うのがよいでしょう。

function! Foo()
  let x = 0
  return { -> [execute("let x += 1"), x][-1] }
endfunction

let F = Foo()
echo F()
" => 1

Vim 7.4.2137 で funcref() が追加された

Vim

少し前なんですが Vim 7.4.2137 で任意の関数の Funcref を返す funcref() という組み込み関数が追加されました。

github.com

これは、既存の function() と同じように使用することが出来ます。

function! s:plus(a, b)
    return a:a + a:b
endfunction

let s:plus3 = funcref("s:plus", [3])
echo s:plus3(2)
" => 5

[function()funcref() の違い]

function()funcref() の違いですが、function()対象の関数が上書きされた場合に上書きされた関数を呼び出すのに対して、funcref()上書きされる前の関数を呼び出すという違いがあります。

function! s:test()
    return "homu"
endfunction

let s:function = function("s:test")
let s:funcref  = funcref("s:test")

" 両方共 s:test() を呼び出す
echo s:function()
" => homu
echo s:funcref()
" => homu

" s:test() を書き換える
function! s:test()
    return "mami"
endfunction

" 上書きした s:test() を呼ぶ
echo s:function()
" => mami

" 上書きされる前の s:test() を呼ぶ
echo s:funcref()
" => homu

" s:test を削除
delfunction s:test

" Error
" echo s:function()

" OK
echo s:funcref()
" => homu

このように関数が書き換わったのに関わらず『funcref() を呼び出した時』の関数を呼び出します。
また funcref() は組み込み関数に対しては使用できません。