15
11

More than 5 years have passed since last update.

Vimの複数行処理をmap,filterに見立てて理解する

Posted at

今風?の言語でよくやるリスト処理とVimの複数行処理って似てるじゃん!
って思ってビルド待ち時間とかに書いてみた記事です

Vimの複数行処理(名前は今付けた)って、すっっっごい便利だし大して難しくないのにあまり知られていない気がする...
ので、ちょっと捉え方を変えて説明できるか試してみるよ

ここで言うリスト処理というのは、よくやる
対象選択(list) -> 条件で絞り込み(filter) -> 変換処理(map)
のことです

filterって?

複数の要素の中から条件に一致する要素のみを残す処理のこと

Javaだと

Streamで

Stream.of("foo", "bar", "baz").filter(s -> s.startswith("b")).collect(Collectors.toList()) // [bar, baz]

s -> s.startswith("b")の部分が条件

Pythonだと

内包表記で

[s for s in ["foo", "bar", "baz"] if s.startswith('b')] # ['bar', 'baz']

s.startswith('b')の部分が条件

じゃあVimだと?

Vimの複数の要素と条件とはなんだろうか
要素は行、条件は検索文字列(正規表現含む)と置き換えて考えるとうまくはまる


検索クエリの/^bが条件
(結果はハイライトされている2行)

mapって?

複数の要素それぞれに変換処理を適用する

Javaだと

やはりStreamで

Stream.of("foo", "bar", "baz").map(s -> s + "!").collect(Collectors.toList()) // [foo!, bar!, baz!]

s -> s + "!"の部分が変換処理

Pythonだと

こちらも内包表記で

[s + '!' for s in ["foo", "bar", "baz"]] # ['foo!', 'bar!', 'baz!']

s + '!'の部分が変換処理

じゃあVimだと?

Vimで言う変換処理は普段のキーボード操作だ(普段とはnormalモードのこと)
normalモードでA!とすると行末に移動して!を挿入するので、A!の部分が変換処理

filterとmapの合わせ技

JavaやPythonだと当然組み合わせて色々出来ちゃうが、Vimでも同じなのだ!

Javaだと

Stream.of("foo", "bar", "baz").filter(s -> s.startswith("b")).map(s -> s + "!").collect(Collectors.toList()) // [bar!, baz!]

Pythonだと

[s + '!' for s in ["foo", "bar", "baz"] if s.startswith('b')] # ['bar!', 'baz!']

で、Vimだと?

:%g/^b/normal A!

...!
見慣れないかもしれないが、なんのことはない!
/で分けてひとつずつ見てみると

%g

  • 対象選択だ
    • Javaで言うStream.of("foo", "bar", "baz")にあたる
  • %は全行を意味するVimの記号だ
  • Visualモードで:を押すと'<,'>となり、反転している行という意味になる
  • ほかにも.$という記号もあるが、%と範囲選択だけ覚えていれば問題はない
  • %vというのもあり、条件に一致しなかった行を対象とする
    • grepgrep -vから来ているのかな?
    • 下の方にサンプルがあるよ

^b

  • 条件での絞り込みだ
    • Javaで言う.filter(s -> s.startswith("b"))にあたる

normal A!

  • 変換処理だ
    • Javaで言う.map(s -> s + "!")にあたる
  • normalモードでA!をストロークしたことにする、という意味になる

だから

つなげて読むと一緒でしょ?
Stream.of("foo", "bar", "baz").filter(s -> s.startswith("b")).map(s -> s + "!")
:%g/^b/normal A!

これが

こうなる

で、何がうれしいのって? なんでもできるよ!!!

空行を削除したり


行末の;がない行にだけ;をつけたり


todo行を下に集めたり


Logのいらない部分を削ぎ落としたり


csvのカラムを入れ替えたり


コツ

  • カーソル位置に左右されないようなストロークにすること
    • 3文字消すxxx ではなく 次の空白まで消すdf とか
    • 繰り返す前に一度行頭に移動する^ とか
  • 慣れないうちはfilterは/でハイライトされる箇所を見ながら組み立てると良い
  • mapの方も普通に操作して試してみれば良い

Vim力の向上と共に飛躍的にできることが増えてもう戻れなくなる!
やってみよー!

(思いつきでテキトー書いたわりに破綻はしなかった...かな?)

15
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11