【一人 vimrc advent calendar 2017】vimrc で起動時にのみ実行するスクリプトを記述する【6日目】

一人 vimrc advent calendar 2017 6日目の記事になります。

vimrc環境変数などに追記する設定を記述する

さて、以前『vimrc で安全に autocmd を設定する』記事でも書いたのですが、vimrc は再読込される可能性を考慮する必要があると書きました。
同様に、例えば次のように『環境変数に追記するような設定』を行う場合も注意する必要があります。

" $PATH にパスを追記する
let $PATH = $PATH . ";" . "/path/to/hoge"

上記のように『任意の(環境)変数に対して値を追記する』というような場合、『vimrc を(再)読込する度』に値が追記されてしまいます。

has('vim_starting') を使用する

このような場合 has() 関数を利用します。
has() は『Vim で任意の機能がサポートされているかどうかを確認する』ための関数ですが、has('vim_starting') と呼び出すことで『Vim の初期化中(起動中)では真を返す』というような挙動になります。
これを利用することで以下のように『起動時にのみ処理を実行する』というようなことができます。

" has('vim_starting') は起動時にのみ真を返す
if has('vim_starting')
   " 起動時にのみ処理が行われる
    let $PATH = $PATH . ";" . "/path/to/hoge"
end

まとめ

  • has('vim_starting') を利用することで起動時にのみ実行される処理を記述できる
  • vimrc は再読込される可能性を考慮して設定しよう(2回目)
    autocmd もそうですが、このような意図しない挙動はエラーなど出るわけではないので注意して行う必要がありますね。

【一人 Ruby Advent Calendar 2017】Ruby で & を使ってブロック引数を渡す【5日目】

一人 Ruby Advent Calendar 2017 5日目の記事になります。
今回は前回書いた『ブロックについていろいろ【4日目】』の続きになります。

前回のおさらい

さてさて、Ruby では以下のようにしてブロック引数という特別な引数を使うことができます。

def func &block
    # block は必ず Proc オブジェクト
    p block

    # ブロックの処理を呼び出す
    block.call 1, 2
end

p func { |a, b| a + b }
# => 3

このようにブロック引数を {} で定義して渡している事が分かりますね。

& でブロック引数を渡す

Ruby では以下のように『& 含めた引数』を渡しているコードをよく見かけます。

p ["homu", "mami", "mado"].map &:upcase
# => ["HOMU", "MAMI", "MADO"]

実はこれも『&:upcase をブロック引数に渡している』というようなメソッド呼び出しになります。

& は何をやっているのか

では & は何をやっているのでしょか。
これは func &obj とした場合に『func メソッドに obj.to_proc をブロック引数に渡している』というような処理になります。
つまり、&:upcase というのは :upcase.to_proc の結果をブロック引数に渡していることになります。

def func &block
    # :hoge.to_proc をブロック引数として受け取る
    p block
    # => #<Proc:0x00000000019c4a18(&:hoge)>

    # :hoge.to_proc と同じ
    p :hoge.to_proc
    # => #<Proc:0x0000000001149960(&:hoge)>
end

func &:hoge

また、これは :upcase のような Symbol オブジェクトに限らず、#to_proc メソッドが定義されているオブジェクトであればなんでも渡すことができます。

class X
    def to_proc
        # Proc オブジェクトを返す
        proc {
            "X#to_proc"
        }
    end
end


def func &block
    p block
    # => #<Proc:0x0000000001328e48@/tmp/vXw2j9T/671:4>

    # X#to_proc が呼び出される
    p block.call
    # => "X#to_proc"
end

x = X.new

# x.to_proc をブロック引数として渡す
func &x

ただし、#to_proc メソッドは Proc オブジェクトを返す必要があるので注意してください。

class X
    def to_proc
        42
    end
end


def func &block
    # ...
end

x = X.new

# Error: can't convert X to Proc (X#to_proc gives Integer) (TypeError)
# to_proc は Proc オブジェクトを返す必要がある
func &x

Symbol#to_proc は何をやっているのか

さて、&:upcase:upcase.to_proc を呼び出していることはわかったと思います。 では、:upcase.to_proc 自体は何をやっているのでしょうか。 Symbol#to_proc は『引数に対してそのシンボル名のメソッドを呼び出す Proc オブジェクトを返す』という処理になります。 どういうことかというと

:upcase.to_proc.call "homu"

"homu".send(:upcase)
# つまり
# "hoge".upcase

というような呼び出しと同等になります。 また、to_proc.call に対して引数が複数ある場合は、『第二引数以降は呼び出すメソッドの引数』として渡されます。

# このように複数の引数がある場合は
p :+.to_proc.call 1, 2
# => 3

# 以下と同じ
p 1.send(:+, 2)
# => 3

# 以下と同じ
p 1.+ 2
# => 3

まあ、要約すると func &:upcasefunc { |it| it.upcase } と同じということですね。

いろいろと使ってみる

上記を踏まえると次のようなコードは & を使ってより簡潔に記述することができます。

p ["homu", "mami", "mado"].map { |it| it.capitalize }
# => ["Homu", "Mami", "Mado"]

p (1..10).select { |it| it.even? }
# => [2, 4, 6, 8, 10]

p (1..10).inject { |a, b| a + b }
# => 55

これらを & + Symbol で記述すると以下のようになります。

p ["homu", "mami", "mado"].map &:capitalize
# => ["Homu", "Mami", "Mado"]

p (1..10).select &:even?
# => [2, 4, 6, 8, 10]

p (1..10).inject &:+
# => 55

こんな感じでだいぶ簡潔に記述することができました。

#method を活用する

最後に Symbol ではなくて #method メソッドをブロック引数として渡してみましょう。
#method メソッドは『レシーバのメソッドをオブジェクトとして返す』メソッドになります。

plus3 = 3.method(:+)
p plus3
# => #<Method: Integer#+>

p plus3.to_proc.call 4
# => 7

# plus3.call と直接 call を呼び出すことも出来る
p plus3.call 4
# => 7

このように Method オブジェクトを返します。
また、このオブジェクトも自身を呼び出す Method#to_proc を返すので以下のようにブロック引数として使用することもできます。

p (1..5).map &2.method(:+)
# => [3, 4, 5, 6, 7]

# 以下のように記述するのと同じ
p (1..5).map { |it| 2 + it }
# => [3, 4, 5, 6, 7]

このように &2.method(:+){ |it| 2 + it } と同じ処理になります。
これを利用して次のようにしてトップレベルや Kernel のメソッドに引数として渡すこともできます。

def fizzbuzz n
      n % 15 == 0 ? "FizzBuzz"
    : n %  3 == 0 ? "Buzz"
    : n %  5 == 0 ? "Fizz"
    : n
end

p method(:fizzbuzz).call 5
# => Fizz

# 要素を fizzbuzz に引数として渡す
p (1..20).map &method(:fizzbuzz)
# => [1, 2, "Buzz", 4, "Fizz", "Buzz", 7, 8, "Buzz", "Fizz", 11, "Buzz", 13, 14, "FizzBuzz", 16, 17, "Buzz", 19, "Fizz"]

# puts メソッドを呼び出したり
["homu", "mami", "mado"].each &method(:puts)
# => homu
# mami
# mado

このように &method(:fizzbuzz){ |it| fizzbuzz it } と同じ処理になります。

まとめ

  • &obj はブロック渡しの1つ
  • &objobj.to_proc がブロック引数として渡される
  • & を利用することでブロックを記述することなく簡潔にブロックを記述することが出来る
  • &:upcase{ |it| it.upcase } と同等。
  • #method を利用してトップレベルや Kernel のメソッドに引数として渡すことも出来る


Ruby を使い始めた頃は & という記法が何をやっているのかよくわからないで混乱することが多かったですが、今では & がないと Ruby のコードが書けないぐらいには依存しています。
{} 記法でブロックの処理を記述するのもいいですが、&:hoge のように『& + Symbol』を利用することでブロックをより簡潔に記述する事が出来るので利用してみるとよいと思います。

参照

【一人 vimrc advent calendar 2017】Windows ライクなキーマッピングを使う【5日目】

一人 vimrc advent calendar 2017 5日目の記事になります。
今回は Windows ライクなキーマッピングのお話。

Vim でも Windows ライクなキーマッピングを行いたい

さてさて、Windows ユーザが Vim を使うとお馴染みの

  • Ctrl + x:切り取り
  • Ctrl + c:コピー
  • Ctrl + v:貼り付け
  • Ctrl + z:アンドゥ
  • Ctrl + s:保存
  • Ctrl + a:全選択
  • etc...

みたいなショートカットを使いたくなりますよね。
わたしもそうでした。
このような Windows ライクなショートカットを Vim で使用したい場合 mswin.vim というスクリプトファイルが利用できます。

mswin.vim を使う

mswin.vimVim のランタイムに付属されているので以下のように読み込むことですぐに使うことができます。

source $VIMRUNTIME/mswin.vim

これにより

  • Ctrl + x:切り取り
  • Ctrl + c:コピー
  • Ctrl + v:貼り付け
  • Ctrl + z:アンドゥ
  • Ctrl + s:保存
  • Ctrl + a:全選択
  • etc...

のような機能がノーマルモードやインサートモード、ビジュアルモード、コマンドラインモードなどで利用することができます。
詳細は :help mswin.vim:e $VIMRUNTIME/mswin.vim などして直接スクリプトファイルを見てみるとよいでしょう。

mswin.vim を調整する

やってね!!これで Vim でも Windows のショートカットを使えるようになったよ!!!
とはいえ、Vim<C-v>(ブロック選択) や <C-c>(処理の中断)、<C-a>(文字のインクリメント)、<C-x>(文字のデクリメント) などを潰してしまうのはちょっとつらいです。
そこで以下のように『ノーマルモードでは元のキーマッピングを使うよう』に調整してみましょう。

source $VIMRUNTIME/mswin.vim

" ノーマルモードでは元のキーマッピングを利用する
nnoremap <C-x> <C-x>
nnoremap <C-v> <C-v>
nnoremap <C-a> <C-a>
nnoremap <C-c> <C-c>

" この辺りは便利そうなので残して置いたり
" nnoremap <C-z> <C-z>
" nnoremap <C-s> <C-s>

他にも『元の Vim のキーマッピングを使いたい!』みたいなのがあれば上記のように設定を追加していくとよいでしょう。

まとめ

  • VimWindows ライクなショートカットを使いたい場合は mswin.vim が利用できる
  • mswin.vim では重要なキーマッピングが潰されるので使用する場合は調整する必要がある

Vim なら Vim のショートカット(キーマッピング)を使うべきだ』という人もいるかと思いますが、最初から全ての Vim のキーマッピングを覚えるのはとても大変です。
なので Vim を使い始めた方や Vim の操作に慣れない方は、まずはこのような便利スクリプトを利用して徐々に慣れていくとよいのではないでしょうか。
Vim の操作がわからなくてストレスがたまるよりも、このように自分の使い勝手のよい設定を行い『ストレスフリーな環境を構築していくこと』が Vim を使用する第一歩でもあると思います。
まあ、とはいえ <C-v><C-c> などの重要なキーマッピングを潰してしまうのも事実なのでその辺りは細かく調整していく必要はあるとは思います。
そんな感じで『Vim を使い始めたけど操作が難しい…』という方は mswin.vim を利用してみるのはどうでしょか。

【一人 Ruby Advent Calendar 2017】ブロックについていろいろ【4日目】

一人 Ruby Advent Calendar 2017 4日目の記事になります。
ちょっと遅れてしまい申し訳ありません。
さて、今回は Ruby といえばブロック、ブロックといえば Ruby ぐらいに Ruby の特徴的な機能の1つであるブロックについて書いてみます。

ブロックとは

Ruby ではメソッドに対して『任意の処理』を渡す手段とし『ブロック引数』という特別な引数があります。
メソッドでブロック引数を受け取る場合、最期の変数名に & を追加して受け取ることができます。

# a, b は通常の引数
# &block はブロック引数
def func a, b, &block
end

また、ブロック引数は必ず Proc オブジェクトになります。

ブロック引数の渡し方

ブロック引数は通常 do 〜 end もしくは {} でブロックした『処理』を『Procオブジェクトとして』渡します。

def func &block
    # block は Proc オブジェクト
    p block
    # => #<Proc:0x0000000000b55888@/tmp/vtKnROY/351:10>

    # Proc#call で処理を呼び出す
    block.call 1, 2
end


# func 内の block.call で {} 内の処理が呼び出される
# a, b, は block.call の引数
p func { |a, b| a + b }
# => 3

# 以下のような渡し方でもおk
# func do |a, b|
#  a + b
# end

このようにブロック引数を使用することでコールバック関数のような処理を実現することができます。
以下のように Proc オブジェクトを引数として渡すこともできますが、

def func block
    # Proc#call で処理を呼び出す
    block.call 1, 2
end

# proc {} をコールバック関数として渡しているが…
p func(proc { |a, b| a + b })
# => 3

Ruby ではブロックを利用したほうが簡潔に記述することができますね(まあ proc 自体も引数としてブロックを受け取っているのですが。
また、ブロック引数は1つだけしか受け取ることができません。

# Error
def func &block1, &block2
    #
end

ブロックの使用例

Ruby ではブロックがあちこちで使われていますが、例えば次のようなイテレーションメソッドでも利用されています。

# 配列の要素を選択する処理をブロックで記述する
p [1, 2, 3, 4, 5, 6].select { |it|
    # 偶数のみ選択する
    it % 2 == 0
}
# => [2, 4, 6]

# 配列の要素を変更する処理をブロックで記述する
p [1, 2, 3, 4, 5, 6].map { |it|
    # 要素を2倍にする
    it + it
}
# => [2, 4, 6, 8, 10, 12]

このようにしてメソッドに対して簡単に処理を渡すことができます。

まとめ

  • Ruby のブロックは処理をメソッドに渡すための機能
  • メソッドでコールバック引数みたいなのを受け取りたい場合はブロック引数として受け取ると Ruby らしい
    Ruby をやり始めるとまず目に付くのがブロックの記述だと思いますが、実際はそこまで難しい概念ではないと思うので Ruby を学ぶのであればまず最初に覚えておきたいですね。

【一人 vimrc Advent Calendar 2017】vimrc から別のスクリプトファイルを読み込む【4日目】

一人 vimrc Advent Calendar 2017 4日目の記事になります。
今回は vimrc から別のスクリプトファイルを安全に読み込みます。

任意のスクリプトファイルを読み込む

さて、vimrc が少し大きくなってくると

みたいに設定をファイル単位で分割したいと思うことがあると思います。
こういう場合は :source コマンドを使って任意のスクリプトファイルを読み込むことができます。

" :soure {file}
source /path/to/vimfiles/plugin.vim
source /path/to/vimfiles/keymapping.vim
source /path/to/vimfiles/command.vim
source /path/to/vimfiles/filetype.vim

読み込むスクリプトファイルを絶対パスにする

さて、:source を使うことで任意のスクリプトファイルを読み込むことができました。
しかし、以下のように『読み込む vimrc からの相対パス』で :source を読み込む場合は上手く動作しない可能性があります。

" vimrc と同ディレクトリにあるファイルを読み込む
" しかし、これだと上手く読み込まれない可能性がある
source ./plugin.vim
source ./keymapping.vim
source ./command.vim
source ./filetype.vim

これは :source が『:source しているファイルのパスから』ではなくて『そのファイルを読み込んでいるカレントディレクトリから』のパスになるためです。
例えば、カレントディレクトリが /hoge/foo/ の場合に上記vimrc を読み込むと /hoge/foo/plugin.vim などを読み込もうとしてしまいます。
なので、vimrc:source を行う場合は『相対パス』ではなくて『絶対パス』にしておいたほうが安全です。

:source相対パスっぽくスクリプトファイルを読み込む

とはいえ、絶対パスにしてしまうとそれはそれで vimrc のパスを変更した場合や、他の環境で同じ vimrc を使う場合に不便です。
そこで <sfile> を利用します。

" <sfile>:h + ファイルパスとする
" 詳しくは
" :help E498
" を参照してください。  
source <sfile>:h/plugin.vim
source <sfile>:h/keymapping.vim
source <sfile>:h/command.vim
source <sfile>:h/filetype.vim

<sfile>:source の実行時に『読み込まれているファイルのパス』に置き換えられます。
そして :h を付けることで『読み込まれているファイルのディレクトリパス』へと置き換えられます。
つまり、

source <sfile>:h/plugin.vim

というのは

source {vimrc のディレクトリパス}/plugin.vim

というような感じに置き換えられるので安全にスクリプトファイルを読み込むことができます。

まとめ

  • 外部のスクリプトファイルを読み込むのには :source コマンドを利用する
  • vimrc:source を使用する場合は絶対パスを指定する
  • ファイルパスの先頭に <sfile>:h を追加することで『読み込んでいる vimrcディレクトリ』に置き換える事が出来る
    vimrc を分割したい場合は読み込んでくるスクリプトファイルのパスに注意してください。

【一人 Ruby Advent Calendar 2017】case-when と === 演算子について【3日目】

一人 Ruby Advent Calendar 2017 3日目の記事になります。
さて、今回は case 式の when 節と #=== 演算子について簡単に解説してみたいと思います。

case

さてさて、Rubycase 式は以下のようにして when 節と組み合わせて使います。

def func n
    case n
    # n の値が when に渡した値であればその式を評価して返す
    when 1
        "one"
    when 2
        "two"
    when 3
        "three"
    else
        "other"
    end
end

p (1..5).map &method(:func)
# => ["one", "two", "three", "other", "other"]

簡単な例だとこんな感じですね。

また、when 節には複数の引数を渡すことができます。

def func n
    case n
    # 引数のいずれかとマッチすれば OK
    when 1, 2, 3
        "low"
    when 4, 5, 6
        "middle"
    when 7, 8, 9
        "high"
    end
end

p (1..10).map &method(:func)
# => ["low", "low", "low", "middle", "middle", "middle", "high", "high", "high", "other"]

ちなみに Rubycase は式なので該当する when 節を評価した値を返します。

when 節はどのようにして判定しているのか

さて、では case-when がどのように処理されるのか見てみましょう。
例えば、以下のような case-when があった場合、

case n
when 1, 2
    "one, two"
when 3
    "three"
else
    "other"
end

これは以下のようなコードとほぼ等価です。

_tmp = n
if 1 === _tmp || 2 === _tmp
    "one, two"
elsif 3 === _tmp
    "three"
else
    "other"
end

こんな感じで複数の if 式で条件分岐しているのと同じですね。
注目すべきは 1 === _tmp のように比較演算子に『#=== 演算子を使っている点』です。

Ruby における #=== 演算子とは

では、Ruby における #=== 演算子とはどういう役割なんでしょうか。
Rubyリファレンスマニュアルでは Object#=== について以下のように記述されています。

メソッド Object#== の別名です。 case 式で使用されます。このメソッドは case 式での振る舞いを考慮して、 各クラスの性質に合わせて再定義すべきです。

一般的に所属性のチェックを実現するため適宜再定義されます。

when 節の式をレシーバーとして === を呼び出すことに注意してください。

また Enumerable#grep でも使用されます。
https://docs.ruby-lang.org/ja/latest/class/Object.html#I_--3D--3D--3D

このように #===caseEnumerable#grep のような『特定の構文やメソッド』で使用されることを想定しています。
そのため caseEnumerable#grep 等で振る舞いを変更したい場合はそのクラスの性質に合わせて #=== 再定義します(されています)。

Ruby 以外の言語、例えば JavaScript では #=== 演算子は『厳密等価演算子』と呼ばれその名の通り『== よりも厳密に値の比較を行う』という目的で使用されます。
しかし、このように Ruby では全く違う目的で使用されるので混同しないように注意しておく必要があります。
ちなみにデフォルトの #=== 演算子== 演算子と同等になります。

#=== が定義されているクラスいろいろ

Ruby では主に以下のようなクラスで #=== 演算子が定義されています。

オブジェクト obj === other の挙動
Range otherが範囲内に含まれているかどうか。 obj.include? other と等価
Regexp 文字列 other正規表現マッチを行う
Class other がそのクラス(obj)のインスタンスであるかどうか
Proc other を引数とした obj.call を呼び出す

なので上記のようなオブジェクトであれば when 節でいい感じに使うことができます。

Range の場合

p (1..10) === 3
# => true

p (1..10) === 25
# => false

p (1..10) === -5
# => false

def level n
    case n
    # 1〜3の範囲にマッチする場合みたいに記述することが出来る
    when (1..3)
        "low"
    when (4..6)
        "middle"
    when (7..9)
        "high"
    else
        "other"
    end
end

p (1..10).map &method(:level)
# => ["low", "low", "low", "middle", "middle", "middle", "high", "high", "high", "other"]

Regexp の場合

p /^\d+/ === "234"
# => true
p /^\d+/ === "homu"
# => false


def check str
    case str
    # 数値の場合
    when /^\d+$/
        "#{str} is number"
    # 英字の場合
    when /^\a+$/
        "#{str} is string"
    else
        "#{str} is other"
    end
end

p check "1234"
# => "1234 is number"
p check "homu"
# => "homu is other"
p check "homu1234"
# => "homu1234 is other"

Class の場合

p String === "homu"
# => true
p Numeric === "homu"
# => false
p Numeric === 1234
# => true
p Numeric === 3.14
# => true


def twice x
    case x
    # x が各クラスのインスタンスだった場合
    when Numeric
        x + x
    when String
        x.to_i + x.to_i
    when Symbol
        x.to_s.to_i + x.to_s.to_i
    else
        "???"
    end
end


p twice 42
# => 84
p twice "1234"
# => 2468
p twice :"5678"
# => 11356
p twice [1, 2, 3]
# => "???"

Proc の場合

p proc { |a| a + a } === 5
# => 10


def level n
    case n
    # Proc によりより詳細な条件を記述することが出来る
    when proc { |n| n < 4 }
        "low"
    when proc { |n| n < 7 }
        "middle"
    when proc { |n| n < 10 }
        "high"
    else
        "other"
    end
end

p (1..10).map &method(:level)
# => ["low", "low", "low", "middle", "middle", "middle", "high", "high", "high", "other"]

このように #=== 演算子が再定義されているクラスは case-when でただ比較するだけではないような使い方ができます。

応用

さて、case-when では #=== 演算子を再定義することで柔軟に挙動を変更することが出来ることがわかったと思います。
これを応用して例えば Array#=== を再定義すると次のようにコードを書くことが出来るようになります。

# Array#=== を定義
using Module.new {
    refine Array do
        def === other, &block
            size == other.size && zip(other).all? { |a, b| a.=== b, &block }
        end
    end
}

# 必ず true を返す proc を返す
def _
    proc { true }
end


def fizzbuzz n
    case [n % 3, n % 5]
    # n % 3 === 0 && n % 5 === 0
    when [0, 0]
        "FizzBuzz"
    # n % 3 === 0
    when [0, _]
        "Fizz"
    # n % 5 === 0
    when [_, 0]
        "Buzz"
    else
        n
    end
end

p (1..20).map &method(:fizzbuzz)

こんな感じでパターンマッチっぽく FizzBuzz を実装することができます。

まとめ

  • case-when ではオブジェクトを比較する時に #=== 演算子を使用する
  • Ruby#=== 演算子case-when などで使用されることを前提としている
  • #=== 演算子を再定義することで case-when などで使い勝手がよくなる


と、いうことで簡単にまとめてみました。
思ったよりも Rubycase-when は柔軟性が高くて応用が利きそうな感じがしますね。
Rubycase-whenC言語のような switch-case として考えるとあまり使う気かはないかもしれませんが、#=== 演算子を意識すると、もしかしたら case-when を使ったほうが簡単にコードを書くことが出来るかもしれませんね。
また、case-when 以外でも通常の条件分岐として #=== 演算子を使うことはできるので、覚えておくと意外なところで利用できるかもしれません。

参照

【一人 vimrc Advent Calendar 2017】vimrc で安全に autocmd を設定する【3日目】

一人 vimrc Advent Calendar 2017 3日目の記事になります。
今回は autocmd について書きます。

autocmd とは

autocmd とは『任意のタイミング』に処理をフックするための機能です。
例えば、 『バッファに入る度にバッファ名をフルパスで表示する』 みたいな機能を実装する場合は以下のような感じになります。

" フックするタイミング:BufEnter(バッファに入った後)
" フックを実行する対象: *(全て)
" 実行する処理:echo expand("%:p")
autocmd BufEnter * echo expand("%:p")

フック可能なタイミングは :help autocmd-events-abc を参照してください。

vimrcautocmd を設定する場合の問題点

さて、Vim を使っていると『Vim を起動している時に vimrc を書き換えて vimrc を再読込する』みたいなことをすることが多いです。
しかし、先ほどのような autocmd の設定を vimrc に記述していると『vimrc を再読込する度に autocmd が新しく追加されてしまう』という問題が発生します。

" vimrc を読み込む度にこの autocmd が追加されてしまう!
autocmd BufEnter * echo expand("%:p")

なので vimrc などで autocmd を記述する場合は少し工夫する必要があります。

augroup を使って autocmd をまとめる

このような場合に autocmd をグループ化する augroup を使います。

" autocmd を "vimrc" という名前でグループ化
augroup my_vimrc
   " グループ内の autocmd をリセットする
    autocmd!

   " グループ内で autocmd を定義する
    autocmd BufEnter * echo expand("%:p")
augroup END

上記のように augroup を使用することで『そのグループで設定した autocmdを一括で削除すること』ができます。
これにより『最初に一括で autocmd を削除すること』で autocmd が重複することを防ぐことができます。
また、これは以下のようにグループ名を指定して autocmd を定義することもできます。

augroup my_vimrc
    autocmd!
augroup END

" autocmd にグループ名を指定する
autocmd my_vimrc BufEnter * echo expand("%:p")

前者のように augroup 内で autocmd の設定を完結するか、後者のように autocmd に対してグループ名を記述するかは好みによって使い分けるとよいと思います。

vimrcautocmd を設定しやすくする

先ほどの後者のように『autocmd にグループ名を指定して定義する』場合、次のようなヘルパコマンドを定義しておくと autocmd が設定しやすくなります。

augroup vimrc
    autocmd!
augroup END

" グループ名を一緒にして autocmd を定義する
command! -nargs=*
\   MyAutocmd
\   autocmd my_vimrc <args>

" 定義したコマンドで autocmd を定義する
MyAutocmd BufEnter * echo expand("%:p")

こんな感じで同じようなコマンドを何回も定義する必要がある場合は、自前でコマンドを定義しておくと記述が簡単になりますし、安全になります。

まとめ

  • autocmd は重複して設定される可能性があるので注意して使おう
  • 何回も使うような記述は自分でコマンドを定義しておくと簡単になる
  • また vimrc は再読込される可能性を考慮して設定しよう

autocmd は他の設定とは違い上書きするような挙動ではないので注意して使いましょう。