Vim: curryingもどき

2008-01-31T00:25:00 / vim / comment

Vimのプラグインを書いていてcurryingを使いたい場合が出てきたので、どうすれば実現できるかちょっとだけ考えてみました。

実装
function! Curry(f, x)
  let d = {}
  let d.f = a:f
  let d.x = a:x
  function! d.__call__(...)
    return call(self.f, [self.x] + a:000)
  endfunction
  return d
endfunction
function! Apply(func, args)
  if type(a:func) == type({})
    if has_key(a:func, '__call__')
      return call(a:func.__call__, a:args, a:func)
    else
      throw 'This dictionary is not callable:' string(a:func)
    endif
  else
    return call(a:func, a:args)
  endif
endfunction
使用例
function! Sub(x, y)
  return a:x - a:y
endfunction

let sub3 = Curry(function('Sub'), 3)
echo Apply(sub3, [4002])  " ==> 3339

難点は普通の関数と同様の形式で呼び出せないこと。Curryingされた関数(例ではsub3)を呼び出す場合、本来の関数(Sub)と事前に渡されていた引数(3)を何らかの方法でメモしておかなければなりません。このメモを行うこと自体はDictionary-functionを使えば簡単にできます。しかしDictionary-functionの呼び出しは用いるべきDictionaryを明示的に渡す必要があるため、通常の関数と同様の形式で呼び出すことができません(どうしてもdict.func(...)またはcall(func, args, dict)の形式になる)。

上記の実装ではApply()を使って誤魔化してますが、何だかなぁ、と言う感じ。

余談: Vim scriptではスコープの接頭辞のみの式(s:g:)でスコープに属する変数を管理するDictionaryが得られます。これを利用すればclosureもどきができそうな気がしたのですが、lexical scopeもどきの実現が面倒臭過ぎるので放棄気味。関数実行時のローカルスコープl:は実行終了と同時に破棄されることが面倒さに拍車をかけています。下手に扱うとVimがクラッシュします。というか今日は1回それでクラッシュして半泣きしました。

第3次脳内シスプリブーム

2008-01-27T21:26:00 / diary / comment

部屋のスペースがアレなので、アニメ版シスター・プリンセスのDVD全巻を処分しようかと思っていたのですが、今日になって何を思ったのか作業の合間に1巻を再生してみたところ:

1話、空から手を差し伸べる可憐を見て
うおぉぉぉぉぉぉおおぉおぉ! 可憐んんぅんんんん!
1話、写真を撮ろうかと誘う咲耶を見て
うおぉぉぉぉぉぉおおぉおぉ! 咲耶ぁぁぁぁあぁぁ!

よし、合間を見て続きを再生しよう。

Vim: 薄れた刻印、磨耗した突起

2008-01-24T03:08:00 / vim, diary / comment

長いこと愛用しているHappy Hacking Keyboardですが、気付けばFJにあるホームポジションを示す突起が、Jの方だけ異様に磨耗していました。

確かにVimでのカーソル移動以外にも、前後や上下などの何らかの移動や選択を伴う操作にはJKのペアを割り当てていたとはいえ、この磨耗具合はかなりのものです。ちゃんと触ればまだ分かるのですが、ちょっと触っただけでは返ってくる感触が今一なため、Jを押しているのかどうか分からなくなるときがあります。

よく見れば突起以外にも各キーの刻印も多少薄れていました。こちらはJよりもKSDENMが薄れています。他はそうでもないあたり、これは押した頻度よりも、押した際に刻印部分と指が接触する範囲の大きいものが薄れているようです。

さらに言えばキー側面の刻印(Fnと同時押しした際の意味)は、PrnSc(Fn+I)が薄れるどころかもうろくに残っていません。他は大して変化がないところを見ると、Kを押す際に爪がIの側面に当たっていたようです。

何だか、キーボード一つでも結構癖が出ますね。

Vim: tab pagesの用途

2008-01-24T02:30:00 / vim / comment

Vim7で追加されたtab pagesですが、どのように使ってますか? 私の場合は次のような感じです:

最初は今一tab pagesの用途が思い付かなかったのですが、作業内容のコンテキストを切り替えるという意味では悪くないと感じています。この使い方のせいもあってか、最近は一度起動したVimのセッションは滅多に終了しなくなりました。

ただ世間一般(どこだ)的にこの使い方はどうなんでしょうね? 自分が見聞きする範囲では1 tab pageに1 windowと、windowの代わりにtab pageを使っている例がほとんどなで、それは何だか凄く勿体無いと思うのですけど。

各種設定ファイルの管理・更新方法

2008-01-22T18:03:00 / prog / comment

各種設定ファイルの管理・更新方法の話。自分の取っている方法は次の通り:

管理
各種ファイルやディレクトリは一つのディレクトリにまとめて、適当なバージョン管理システムの管理下に置く(ここでは作業ディレクトリの名前をconfigとする)。
雑多なdot filesはconfig直下に置く。
ファイル/ディレクトリ名の先頭が"."の場合、名前の頭に"dot"を付ける(例: dot.bashrc, vim/dot.vimrc, ...)。そのままでは色々と扱いが不便なので。
ある程度まとまった規模のものは専用のディレクトリを作り、そこに置く(例: opera, samurize, vim, ...)。
更新
作業用ディレクトリからmakeで更新を行う(設定更新用Makefile。内容は一部の抜粋のみ。本物はユーザー名や環境に応じて適宜更新対象を取捨選択させている)。
シンボリックリンクを使う方法もあるが、一部ファイルが編集中の場合に不整合な状態を参照される可能性があるため、この方法を取る。

ptrace(2)とかgdb --pid={pid}とか

2008-01-22T00:50:00 / prog, hack / comment

Vim Hack: :suspend時にシェルのカレントディレクトリを変えたいの直接法の解決手段になりそうな話。実行中のプロセスにちょっかいを出すという点から、Binary Hackにそれっぽい内容があったような気がしたので読み返してみたところ、ptrace(2)というシステムコールがあるということを発見しました。これを利用すれば実現できそうな気がします。

ptrace(2)の簡易インターフェースとしてgdb --pid={pid}を使えば実行中のプロセスに対して簡単にちょっかいが出せるので、まずはそれで実験してみました。

$ echo $SHELL
/bin/bash

$ pwd
/tmp/foo

$ gdb --pid=$$  # gdbを起動したシェルにちょっかいを出すよ。
...
(gdb) p chdir("/")
$1 = 0
(gdb) The program is running.  Quit anyway (and detach it)? (y or n) y
Detaching from program: /bin/bash, process 3339

$ ls
bin  dev  etc  ...

$ /bin/pwd
/

$ pwd  # bashのpwdは内部コマンド
/tmp/foo

$ cd ..

$ /bin/pwd
/tmp

$ pwd
/tmp

以上のように、外部コマンドの実行結果からは確かにシェルのカレントディレクトリが変更されているように見えるのですが、内部コマンドの実行結果からは変更前のディレクトリを参照しているようにしか見えません。これはシェル側でカレントディレクトリについて何かしらのキャッシュを持っており、gdbからの予想外の変更のためそれに不整合が生じているのでしょう。と言う訳で直接chdir(2)するのは不味いということになります。

そうすると他の手段を考えなければなりません。と言っても、標準入力からcdのためのコマンドを送り込む形しか思いつかないのですが。でもこれはどうやって実現すればいいのやら。

Vim Hack: :suspend時にシェルのカレントディレクトリを変えたい - GNU screen編

2008-01-20T00:43:00 / vim, hack / comment

Vimで:suspend時にシェルのカレントディレクトリを変えたい件の解決編パート1: 間接法 = GNU screenを併用する方法について。

" Pseudo :suspend with automtic cd.
" Assumption: Use GNU screen.
" Assumption: There is a window with the title "another".
noremap <silent> <C-z>  :<C-u>call <SID>PseudoSuspendWithAutomaticCD()<Return>

if !exists('s:gnu_screen_availablep')
  " Check the existence of $WINDOW to avoid using GNU screen in Vim on
  " a remote machine (for example, "screen -t remote ssh example.com").
  let s:gnu_screen_availablep = len($WINDOW) != 0
endif
function! s:PseudoSuspendWithAutomaticCD()
  if s:gnu_screen_availablep
    " \015 = <C-m>
    silent execute '!screen -X eval'
         \         '''select another'''
         \         '''stuff "cd \"'.getcwd().'\"  \#\#,vim-auto-cd\015"'''
    redraw!
    let s:gnu_screen_availablep = (v:shell_error == 0)
  endif

  if !s:gnu_screen_availablep
    suspend
  endif
endfunction

こんな感じでどうでしょう。GNU screenが使用できない場合は普通に:suspendします。

問題点は、シェル側のウィンドウ"another"が存在しない場合、本来そちらに送られるcdのためのコマンドがVim側のウィンドウに送られてしまうことです。どうにかしてウィンドウ切り替えの失敗を検出できればパーフェクトなんですけどね。

取り敢えずはこれを試用してみます。上記問題点の解決や直接法による解決についてはまた次回。

s:gnu_screen_availablepの初期値を修正しました。

最初は「取り敢えずGNU screenの使用を試みて、失敗したら:suspendを使う」という風にしていたのですが、それだとSSH等で接続したリモートの環境でVimを使った場合、ローカルとリモートで二重にGNU screenが起動することになり、凄まじく混乱することになります。また、GNU screenが起動されていない状態で使用した場合にも混乱を招きます。

と言う訳で、$WINDOWの存在をチェックしてGNU screenを使用するかどうかを判定するようにしました。

ドメイン取得しようかなと思ったり思わなかったり

2008-01-19T03:09:00 / site / comment

何となく独自ドメイン取得の誘惑に駆られてきました。XREAには長いことお世話になっているので、ついでにXREA+も検討してしまおうかしら、という誘惑にも駆られてます。

ただ取るにしてもどういう名前にするかで非常に悩みます。ユニークでそこそこ短い方が良いのですけど、なかなか難しい。こういうときはxcezxさんが凄く羨ましくなります。むぅ。

Vim Hack: :suspend時にシェルのカレントディレクトリを変えたい

2008-01-19T02:13:00 / vim, hack, prog / comment

Vim使用時、一時的にシェルを使いたい場合、^Z:suspendしてシェルに降り、^@^@fgしてVimに戻る、というパターンが確立しています。

ここで問題なのが、一時的にシェルに降りる場合、シェルのカレントディレクトリとVimのカレントディレクトリが異なるということです。

特に、最近は作業内容毎に専用のtab pageを分けており(1番はTODO管理、2番は各種設定編集、3番はこのサイト関係、4番はVimのソースコード、5番以降は一時的な作業時に現れたり消えたり)、各tab pageに独自のカレントディレクトリを持たせ、tab pageの切り替えに応じて自動的に変更するようにしています。この使い方ではVimのカレントディレクトリがコロコロ変化するため、シェルに降りたときに一々cdすることが非常に面倒臭くなります。

と言う訳で:suspendでシェルに降りた際、シェル側のカレントディレクトリを自動的にVim側のカレントディレクトリに合わせたいのですが、これ、どうやればいいんでしょう? プロセス間を跨ぐので、ちょっとやそっとじゃ実現できなさそうな気がします。取り敢えず思いついた方法は次の3通り:

直接法: :suspend時にシェルの標準入力にコマンドを送る
どこから手を付ければいいのかさっぱり分からないので非常に難易度が高い。
間接法: :suspendではなくGNU screenを併用して代替
Vimの起動しているウィンドウ(仮にmainとする)の他に作業用のウィンドウ(仮にanotherとする)を用意。:suspendの代わりにanotherに切り替え、その際にcdを自動的に行う。という方法。
GNU screenが使えないと場合の対処をどうするかということと、そもそもこの方法が実現できるのかということが問題。
消極的解決法: :suspendでなく:shellを使う
速度的に多少の難があります(特にCygwinはひどい。bash completionを使っているせいもありますが)。

うーん、どうしましょう。取り敢えず間接法でやってみましょうか。直接法はBinary Hack的なノリになりそうな気がします。

GTDについて調べてみたら既にGTDライクなことを実践していた

2008-01-18T02:47:00 / diary / comment

最近、巷でよく聞くGTD (Getting This Done)について、ちょっと気になったので調べてみたところ、実は自分のやってたTODOメモとその管理方法が既にGTDライクなものだと判明して吹きました。

ただGTDに比べると、Organizeでの分類が大雑把(「まだやってない」「要見直し」「終わった」「メモ」)なこと、Reviewの習慣がない(気が向いたらやる)こと、Processの手順が恣意的なことくらいでしょうか。とはいえ、色々と参考にできるところはあったので、徐々に改善していこうかな(ということを「まだやってない」に入れた)。

Vim Hack: ハッシュテーブルの斬新な最適化

2008-01-16T06:07:00 / vim, hack / comment

Vimのソースコードを読んでいて気付いたのですが、ハッシュテーブルの最適化が斬新で吹きました。普通、ハッシュテーブルを実装しようとしたら、ハッシュテーブルそのものを表す構造体(Vimではhashtab_T)と、ハッシュテーブルに格納される要素を表す構造体(Vimではhashitem_T)を定義します。そして後者にはキーと値に関する情報が含まれます。

ところがhashitem_Tのメンバーはhi_key(キー)とhi_hash(キーのハッシュ値のキャッシュ)のみ、つまりキーに対応する値に関するメンバーが存在しないのです。

何故このようなことができるかというと、

  1. 1つのハッシュテーブルに格納する値は大抵の場合、同じ型だろう。
  2. 格納したい値は大抵の場合、何らかの構造体だろう。
  3. その構造体にはキーとなりうる値(へのポインタ)を含むだろう。
  4. ならキーは構造体内部への参照とすれば、キーからそれを含む構造体そのもの(=キーに対応する値)への参照は逆算できる。

ということらしいです。こんな考えは思いついたこともありませんでしたよ。これを最初に考えた人、誰なんでしょうね。

Vim Hack: 組み込み関数の追加手順

2008-01-16T05:32:00 / vim, hack / comment

Vimスクリプトの組み込み関数を追加する手順は次の通り:

  1. src/eval.cに組み込み関数に対応するCの関数を追加する。なお、Vimレベルでの名前がgettabvar()ならばCレベルでの名前はf_gettabvar()とすることが望ましい。
  2. src/eval.cのfunctions[]に追加したい関数のエントリー(Vimレベルでの関数名、引数の最小個数、引数の最大個数、Cレベルでの関数へのポインタ)を追加する。なお、エントリーは関数名についての辞書式順で適切な位置に記述しなければならない。
  3. src/eval.cのfunctions[]より前に、追加したCレベルの関数のプロトタイプ宣言を追加する。
  4. runtime/doc/eval.txtの内容を適宜追加する。

なお、ファイル内の各内容は基本的に辞書式順に並べられているため、適切な位置に記述することが望ましい。

組み込み関数を追加する上での注意点:

Vim Hack: gettabvar()/settabvar()を実装した

2008-01-16T05:26:00 / vim, hack / comment

Vimにgettabvar()/settabvar()を追加するパッチを書いてみました(書き終えるまでのメモ)。ベースはhttp://vim.svn.sourceforge.net/svnroot/vim/trunk/のr805です。

Vim: オプションのグローバルな値とローカルな値

2008-01-14T01:16:00 / vim / comment

割と知らない人が多い(と感じる)ので書いておきます。

Vimのオプションのほとんどはグローバルなオプションですが、バッファまたはウィンドウに対してローカルな値を持つオプションもあります。例えば'encoding'はグローバル、'tabstop'はバッファに対してローカル、'number'はウィンドウに対してローカルです。

これだけなら話は簡単なのですが、実はローカルな値を持つオプションは同時にグローバルな値も持っています。このグローバルな値は新たなバッファやウィンドウが作成されたときにローカルな値の初期値として用いられます

普通、オプションの設定には:setを使いますが、:setlocal:setglobalという派生系も存在します。各々の動作の違いは次の表の通りです。

コマンド グローバルな値 ローカルな値
:set option=value 値はvalueに設定される 値はvalueに設定される
:setlocal option=value 値は変化しない 値はvalueに設定される
:setglobal option=value 値はvalueに設定される 値は変化しない

つまり、ローカルな値を設定するときは:setlocalを使うべきです。そうしないと新たなバッファやウィンドウを開いたときに予期しない値が設定されることになります

例えば特定のバッファのタブ幅を変えようとしてset tabstop=20とした場合、新たなバッファを開いたときのタブ幅のデフォルトは20になります。これでは元に戻すためにset tabstop&としなければなりません。実際、私がVimを使い始めた頃はこの挙動でかなり混乱させられました。自分でインタラクティブに設定した分には許せるのですが、プラグイン等で似たようなことをされるとかなりアレです。

なお、ローカルな値を持つオプションの一部には「普段はグローバルなオプションとして振舞う。ローカルな値が設定されればそちらを使う」という派生系が存在します(:help global-local参照。ヘルプには「global or local to {x}」と記述されているオプション)。この種類のオプションについては、:setで値を設定するとグローバルな値のみが設定されます(上の表の動作と異なり、ローカルな値は設定されません)。ややこしいですが、この辺は知らなくても普通に使う分には困らないので忘れても構いません。

ちなみにVim 7.1 (パッチ1-221適用済み)でのオプションの数と種類は次の通りです。

種類 割合
グローバル 246 71%
ローカル 85 25%
global-local 14 4%
合計 345 100%

なお、一部のオプションは上記の原則に沿わない場合があります。特にローカルな値を持つオプションの一部は新規バッファ作成時にグローバルな値をコピーされません(詳細はsrc/option.cのbuf_copy_options()参照)。例えば'filetype''readonly'はコピーされません。これはコピーされる方が不便になるからだと思われます。

Ruby派に転向しそう

2008-01-05T03:44:00 / prog, ruby / comment

Rubyを極めると言ったりruby教えて貰おうと言われた手前、取り敢えずFAQ等を読んでRubyについての概要を適当にメモしていたのですが、なんかもう、Ruby派に転向しそうな勢いになってきました。何で今まで敬遠してたんでしょう。

ただ、Rubyが最初から今の姿であったという訳ではないですからね。Pythonも1.xの頃はアレだったと聞きますし。しかし、その辺りの変化を差し引いても、Rubyを初めて知った当時(約7年前)の自分のレベルがいかに低かったかを思い知らされました。これに気付けただけでもそれなりの収穫だと思います。精進しないと駄目だ。

取り敢えず眠くなったので今日はこの辺で終了。言語の概要は把握できたので、後は適当に何か書いて細かいところを補強します。とはいえ、何を書こうかしら。

Re: RSSリーダのグループわけ。 - Lazy Programmer :-p

2008-01-04T23:07:00 / web / comment

皆さんどうやってるんですか。

普通、グループで分割しようとしても綺麗に分けられるはずがありません。なので、グループ以外の分類方法(例: Google Readerのtags)が使えるならそれを使い、使えないなら次のような基準で分けています。

id:tomyheroさんのようなグループ分けだと「知り合いの面白いPerl hacker」をどこに入れるかで困りますね。

2008年の抱負

2008-01-01T00:50:00 / diary, vim, ruby, rubik, bm2dx / comment
Vimをハックする
ユーザーとしては大分極めてきたので(参考資料: 2007年初期の.vimrc / 2007年末期の.vimrcと関連ファイル)、今度はソースコードのレベルに降りて色々できるようになる。取り敢えずの目標は、Gauche interafece (+gauche)を作る。
Rubyを極める
Rubyの存在自体は7年程前に知ったのですが、初対面(?)の印象が何故か物凄く悪かった記憶があり、敬遠していました。しかし最近になってリファレンス等を軽く見てみたところ、そこまで悪いものではない感じだったので、極めてみることにします。
スピードキュービング: Sub40安定
全然回してなかったらSub50が怪しくなるレベルになっていたので、ここは一つ踏ん張ってSub40を目指してみる(Sub40くらいなら宴会芸に使えると思うし)。
beatmaniaIIDX: SP八段
ある程度修行したら取れる気がしてきた、かも。最近はLv8でも割とHARDでクリアできることが判明しましたし。今は一定速度以上になると乱打と階段がぐだぐだになるので、体力・速度の面で指を鍛えれば割といけるような、いけないような。
番外編
xcezxさんのような斬新な発想力を養う。例: (2007-11-09) 俺もrubyやろ - (2008-01-03) ruby教えて貰おう