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
endfunctionfunction! 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
endfunctionfunction! 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回それでクラッシュして半泣きしました。
部屋のスペースがアレなので、アニメ版シスター・プリンセスのDVD全巻を処分しようかと思っていたのですが、今日になって何を思ったのか作業の合間に1巻を再生してみたところ:
よし、合間を見て続きを再生しよう。
長いこと愛用しているHappy Hacking Keyboardですが、気付けばFとJにあるホームポジションを示す突起が、Jの方だけ異様に磨耗していました。
確かにVimでのカーソル移動以外にも、前後や上下などの何らかの移動や選択を伴う操作にはJとKのペアを割り当てていたとはいえ、この磨耗具合はかなりのものです。ちゃんと触ればまだ分かるのですが、ちょっと触っただけでは返ってくる感触が今一なため、Jを押しているのかどうか分からなくなるときがあります。
よく見れば突起以外にも各キーの刻印も多少薄れていました。こちらはJよりもK、S、D、E、N、Mが薄れています。他はそうでもないあたり、これは押した頻度よりも、押した際に刻印部分と指が接触する範囲の大きいものが薄れているようです。
さらに言えばキー側面の刻印(Fnと同時押しした際の意味)は、PrnSc(Fn+I)が薄れるどころかもうろくに残っていません。他は大して変化がないところを見ると、Kを押す際に爪がIの側面に当たっていたようです。
何だか、キーボード一つでも結構癖が出ますね。
Vim7で追加されたtab pagesですが、どのように使ってますか? 私の場合は次のような感じです:
最初は今一tab pagesの用途が思い付かなかったのですが、作業内容のコンテキストを切り替えるという意味では悪くないと感じています。この使い方のせいもあってか、最近は一度起動したVimのセッションは滅多に終了しなくなりました。
ただ世間一般(どこだ)的にこの使い方はどうなんでしょうね? 自分が見聞きする範囲では1 tab pageに1 windowと、windowの代わりにtab pageを使っている例がほとんどなで、それは何だか凄く勿体無いと思うのですけど。
各種設定ファイルの管理・更新方法の話。自分の取っている方法は次の通り:
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で: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を使用するかどうかを判定するようにしました。
何となく独自ドメイン取得の誘惑に駆られてきました。XREAには長いことお世話になっているので、ついでにXREA+も検討してしまおうかしら、という誘惑にも駆られてます。
ただ取るにしてもどういう名前にするかで非常に悩みます。ユニークでそこそこ短い方が良いのですけど、なかなか難しい。こういうときはxcezxさんが凄く羨ましくなります。むぅ。
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を併用して代替:suspendの代わりにanotherに切り替え、その際にcdを自動的に行う。という方法。:suspendでなく:shellを使ううーん、どうしましょう。取り敢えず間接法でやってみましょうか。直接法はBinary Hack的なノリになりそうな気がします。
最近、巷でよく聞くGTD (Getting This Done)について、ちょっと気になったので調べてみたところ、実は自分のやってたTODOメモとその管理方法が既にGTDライクなものだと判明して吹きました。
ただGTDに比べると、Organizeでの分類が大雑把(「まだやってない」「要見直し」「終わった」「メモ」)なこと、Reviewの習慣がない(気が向いたらやる)こと、Processの手順が恣意的なことくらいでしょうか。とはいえ、色々と参考にできるところはあったので、徐々に改善していこうかな(ということを「まだやってない」に入れた)。
Vimのソースコードを読んでいて気付いたのですが、ハッシュテーブルの最適化が斬新で吹きました。普通、ハッシュテーブルを実装しようとしたら、ハッシュテーブルそのものを表す構造体(Vimではhashtab_T)と、ハッシュテーブルに格納される要素を表す構造体(Vimではhashitem_T)を定義します。そして後者にはキーと値に関する情報が含まれます。
ところがhashitem_Tのメンバーはhi_key(キー)とhi_hash(キーのハッシュ値のキャッシュ)のみ、つまりキーに対応する値に関するメンバーが存在しないのです。
何故このようなことができるかというと、
ということらしいです。こんな考えは思いついたこともありませんでしたよ。これを最初に考えた人、誰なんでしょうね。
Vimスクリプトの組み込み関数を追加する手順は次の通り:
gettabvar()ならばCレベルでの名前はf_gettabvar()とすることが望ましい。functions[]に追加したい関数のエントリー(Vimレベルでの関数名、引数の最小個数、引数の最大個数、Cレベルでの関数へのポインタ)を追加する。なお、エントリーは関数名についての辞書式順で適切な位置に記述しなければならない。functions[]より前に、追加したCレベルの関数のプロトタイプ宣言を追加する。なお、ファイル内の各内容は基本的に辞書式順に並べられているため、適切な位置に記述することが望ましい。
組み込み関数を追加する上での注意点:
argvarsとrettvの2つ。前者はVimレベルでの関数の引数の配列への参照。後者はVimレベルでの関数の戻り値を格納する場所へのポインタ。nとするとargvars[n].v_type == VAR_UNKNOWNであるため、これで判定する。'secure'やrestricted modeでは動作しないよう、チェックを行う。これはif (check_restricted() || check_secure()) return;のように簡単にできる。Vimにgettabvar()/settabvar()を追加するパッチを書いてみました(書き終えるまでのメモ)。ベースはhttp://vim.svn.sourceforge.net/svnroot/vim/trunk/のr805です。
割と知らない人が多い(と感じる)ので書いておきます。
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を極めると言ったりruby教えて貰おうと言われた手前、取り敢えずFAQ等を読んでRubyについての概要を適当にメモしていたのですが、なんかもう、Ruby派に転向しそうな勢いになってきました。何で今まで敬遠してたんでしょう。
ただ、Rubyが最初から今の姿であったという訳ではないですからね。Pythonも1.xの頃はアレだったと聞きますし。しかし、その辺りの変化を差し引いても、Rubyを初めて知った当時(約7年前)の自分のレベルがいかに低かったかを思い知らされました。これに気付けただけでもそれなりの収穫だと思います。精進しないと駄目だ。
取り敢えず眠くなったので今日はこの辺で終了。言語の概要は把握できたので、後は適当に何か書いて細かいところを補強します。とはいえ、何を書こうかしら。
皆さんどうやってるんですか。
普通、グループで分割しようとしても綺麗に分けられるはずがありません。なので、グループ以外の分類方法(例: Google Readerのtags)が使えるならそれを使い、使えないなら次のような基準で分けています。
id:tomyheroさんのようなグループ分けだと「知り合いの面白いPerl hacker」をどこに入れるかで困りますね。
+gauche)を作る。俺もrubyやろ- (2008-01-03)
ruby教えて貰おう