Vim Advent Calendar 2013 136日目:Vim script で関数のデフォルト引数を設定したい

この記事は Vim Advent Calendar 2013 136日目の記事になります。
本来は昨日の記事として書こうと思っていたんですが、諸事情により今日の記事に…。
そんなわけで「昨日のVAC書いてもいいよー」という人募集中です。
Advent Calendar とは一体…

[関数の引数にデフォルト値を渡したい]

さて、Vim script で次のように関数の引数にデフォルト値を渡したいことがあると思います。

function! s:func(command, name = command, mode = 0)
    " ...
endfunction

call s:func("ls")
call s:func("ls", "command ls")
call s:func("ls", "ls", 1)


しかし、Vim script ではこういう書き方はできません。

[可変長引数を利用する]

こういう場合、わたしは可変長引数を利用しています。
可変長引数を使用すると上記のコードは次のように書くことができます。

" デフォルト引数を受け取りたい引数は可変長引数で受け取る
function! s:func(command, ...)
    " a:{n} には可変長引数で受け取った引数が保持されている
    " a:1 があれば a:1 を返し、a:1 がなければ(引数が渡されなければ)a:command を返す
    let name = get(a:, 1, a:command)
    let mode = get(a:, 2, 0)

    " ちなみにこういう書き方もできる
    " a:000 は可変長引数が保持されているリスト
    " let name = get(a:000, 0, a:command)
    " let mode = get(a:000, 1, 0)

    echo [a:command, name, mode]
endfunction

call s:func("ls")
" => ['ls', 'ls', 0]
call s:func("ls", "command ls")
" => ['ls', 'command ls', 0]
call s:func("ls", "ls", 1)
" => ['ls', 'ls', 1]


ちょっとめんどくさいですが、こんな感じで利用する事ができます。

[おまけ]

ちなみに少し複雑ですが、次のようなコードも書くことができます。

function! s:func(command, ...)
    " デフォルト引数を定義した辞書をローカル変数(l:)に突っ込む
    call map(items({
\       "name" : a:command,
\       "mode" : 0,
\   }), "extend(l:, { v:val[0] : get(a:, v:key + 1, v:val[1]), })")

    echo [a:command, name, mode]
endfunction

call s:func("ls")
" => ['ls', 'ls', 0]
call s:func("ls", "command ls")
" => ['ls', 'command ls', 0]
call s:func("ls", "ls", 1)
" => ['ls', 'ls', 1]


get() を使用するよりもコードは複雑ですが、デフォルト引数を辞書で定義しているのでわかりやすいような気はしますね。
ちなみに毎回コードを打つのが手間であれば neosnippet.vim を利用すると便利です。

snippet default_args
options word
		call map(items({
	\		${1}
	\	}), "extend(l:, { v:val[0] : get(a:, v:key + 1, v:val[1]), })")


実用性があるかどうかは不明。