8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vim script から標準出力に出す

Last updated at Posted at 2015-12-15

この記事は Vim script Advent Calendar 2015 の 16日目の記事です。

Shell から特定の target.vim を実行した時に何かしらの出力を得たい。

実行自体は次のようにすることで可能です。

$ vim -S target.vim -c 'qa!'

あるいは、

$ vim -c 'so target.vim | qa!'

-N-u NONE が必要かは場合によります。
詳細はヘルプを見ましょう。(--cmd という選択肢もある 1)

:h -N
:h -u
:h -S
:h -c
:h --cmd
:h :so
:h :bar

さて、おもむろに次のヘルプを見ましょう。

:h Q
:h gQ
:h -E
:h -s-ex
:h :put
:h :print
:h [range]

簡単に説明すると、Q, gQ というのは Ex モード に入るキーバインドです。あまり使う機会がないので別のマッピングをしている人も多いと思います2。そのような場合は vim -E などで試すことができます(-s をつけると試す分には辛い)。

Vim_exmode.png

編集用のコマンドは :h inserting-ex, :h change.txt などで /^: を探すのが参考になります。
なお、途中で :redraw を用いることでバッファの表示を更新することができます。

さて、上記のスクリーンショットでは Vim のコマンドラインウィンドウによる対話環境で実行されていますが -s オプションをつけると Shell で実行されるようになります。その際に Vim は標準出力を用いるわけです。

実践

つまり、こうです。

hello.vim
put ='Hello, world!'
watiko@local $ vim -NEsS hello.vim -c '2,$print | qa!'
Hello, world!

これを次のスクリプトで試してみます。

fib.vim
let s:fib = {
\   'memo' : [0, 1]
\ }

fu! s:fib.calc(to)
  let l:from = len(self.memo)
  for i in range(l:from, a:to)
    let l:next = self.memo[i - 1] + self.memo[i - 2]
    call add(self.memo, l:next)
  endfor
endfu

fu! s:fib.get(n)
  let l:last_index = len(self.memo) - 1
  if l:last_index < a:n
    call s:fib.calc(a:n)
  endif
  return self.memo[a:n]
endfu

put =s:fib.get(10)
put =s:fib.get(45)
put =s:fib.get(46)
put =s:fib.get(47)
watiko@local $ vim -NEsS fib.vim -c '2,$print | qa!'
55
1134903170
1836311903
-1323752223

?!3

fib_big.vim
let s:save_cpo = &cpo
let v:errors = []
set cpo&vim

let s:BASE = 10

fu! s:add_positives(r0, r1)
  let l:i = len(a:r0) - 1
  let l:j = len(a:r1) - 1

  let l:carry = 0
  let l:r2 = ''

  while l:i >= 0
    if l:j < 0
      let l:sum = a:r0[l:i] + l:carry
    else
      let l:sum = a:r0[l:i] + a:r1[l:j] + l:carry
    endif

    if l:sum >= s:BASE
      let l:carry = 1
      let l:sum -= s:BASE
    else
      let l:carry = 0
    endif

    let l:r2 = l:sum . l:r2

    let l:i -= 1
    let l:j -= 1
  endw

  if l:carry != 1
    return l:r2
  else
    return l:carry . l:r2
  endif
endfu

fu! s:fib(n)
  let l:a = '0'
  let l:b = '1'

  let l:count = 0

  while l:count < a:n
    let l:count += 1
    let l:tmp = s:add_positives(l:b, l:a)
    let l:a = l:b
    let l:b = l:tmp
  endw

  return l:a
endfu

call assert_equal('200', s:add_positives('191', '9'))
call assert_equal('55', s:fib(10))

put =string(v:errors)
put =s:fib(45)
put =s:fib(46)
put =s:fib(47)
put =s:fib(1000)

let &cpo = s:save_cpo
watiko@local $ vim -NEsS fib_big.vim -c '2,$print | qa!'
[]
1134903170
1836311903
2971215073
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875

注: 普段は QuickRun を使っています。技術的興味があっただけで、あまりおすすめしません(面倒なので)。

  1. http://www.slideshare.net/rbtnn/vimconf2015-55367256

  2. :verbose map Q, :verbose map gQ

  3. :h Number 手元の環境では :echo 0xffffffff(32bit) が -1 だった。びっくりした勢いのまま書いた。

8
8
0

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
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?