Vim: smartword 0.0.2

2009-01-23T23:16:55 / vim, release, output / comment

smartword 0.0.2をリリースしました。重度のVim hackerな方からのプレッシャーを感じたため久々にコードを見直したところ、非常に残念なコードを書いていたことに気付いたため、バグ修正やリファクタリングなどを行いました。機能的には変化ありません。

Vim: 日本語を含むテキストにおけるword単位の移動 / Re: smartword が便利 - ns9log

2009-01-23T22:54:25 / vim, output / comment

日本語の移動の規則がよくわからない

Vimのword/WORD単位の移動は分かち書き言語を前提としているので、日本語などの言語における単語単位での移動はできません(実行時に形態素解析でもすれば別ですが、現状ではそういうことは考慮されていません。最近、vim_devでその手の話題がありましたが、あまり盛り上がってはいませんね: word segmentation in Vim - vim_dev | Google Groups)。

その上で、日本語を含むテキストにおけるword単位の移動は以下のような挙動になっています:

例えばwでの移動は下図のようになります(*はカーソルの移動位置)。

日本語を含むテキストにおけるword単位の移動の規則がよくわからないアルよ
*     * * * *       *       *   *   * *   * *   *               *   *

ただし、以上のことは私のVim経験をまとめただけなので、ソースコード的にどうなっているかは読んだことがないので不明です。

Vim: textobj-quotesを思いついた

2009-01-22T22:16:47 / vim, output / comment

Vim組み込みのtext objectsのうち、記号系のものの使い分けが面倒になってきたので、カーソル周辺のテキストから適当な範囲を対象にするものが欲しくなってきた。

"foo *is* bar (a.k.a 'baz')"
 ^a   ^a        ^i
 |   _|__      _|_________
_|__________________________

例えば上記のようなテキストがあるとして、カーソル位置が"^"の場合にaqまたはiqで"_"で示される範囲が対象となるというもの。

名前はtextobj-quotesでどうだろう。色々とフィジーで面倒なところはあるけど、ちょっと頑張ればできそうな気がする。今度時間ができたらやろう。

Vim: syntaxcompleteのバグ修正

2009-01-19T23:44:30 / vim, output / comment

syntaxcomplete.vimの'iskeyword'の扱いが間違っている点を修正。十中八九Cで使えるようなキーワードしか補完候補に現れなかった。何故あのようなコードになっていたのだろうか。メンテナーには報告済み。

Vim使いから見てkey-chord.elがいかに役に立たないか

2009-01-17T22:40:03 / vim, emacs, output / comment

key-chord.elという、Emacsで複数キーの同時押しにコマンドを割り当てる機能を提供するものがある。初見で「ああ役に立たないな」とは思ったものの、試してみないことには何とも言えないので、Vim版のarpeggio.vimを書き、いくらか設定を追加したのが2ヶ月ほど前の話。今日になって再考してみたが、やはりVim使いには役に立たないという結論になった。

結局、Vim使いでこれを役に立つと思う人は極々稀だろうし、役に立つケースも極少数だろう。

(ついでに言えば上記の事情も踏まえて名前を「arpeggio」にした)

CS beatmaniaIIDX15 DJ TROOPERS、一通り終了

2009-01-17T11:04:40 / bm2dx / comment

久々にCS beatmaniaIIDX15 DJ TROOPERSを起動。公式サイト以外の関連情報には一切触れていないため正確なところは分からないけれど、曲目からして今日プレイした分で隠し曲は出し尽したと思う。これで一通り終了かな。

今作もスタッフの力の入れようには脱帽せざるを得ない。挙げていくとキリがないので端折りますが、最後の隠し曲については特筆に値します。曲自体はおまけもおまけで、今作の収録曲数的にROMの残り容量も厳しいだろうからムービーは汎用だろうと思っていたし、実際にムービー冒頭もGOLD初出のそれだったので油断していました。演出としてはMilitary Splashの方が派手だしインパクトもあったのですが、あのムービー以上のものはないと思う。

後は説明書のアレが前例の再来とならないことを願うばかり。

シェルのプロンプトにgit「ブランチ」を表示するやつ

2009-01-12T16:42:00 / git, output / comment
function prompt-git-head-name() {
  local git_dir="$(git rev-parse --git-dir 2>/dev/null)"
  if [ -z "$git_dir" ]; then
    return 1
  fi

  local head_name=''
  local additional_info=''
  if [ -d "$git_dir/rebase-apply" ]; then
    if [ -f "$git_dir/rebase-apply/rebasing" ]; then
      additional_info="REBASE"
    elif [ -f "$git_dir/rebase-apply/applying" ]; then
      additional_info="AM"
    else
      additional_info="AM/REBASE"
    fi
    head_name="$(git symbolic-ref HEAD 2>/dev/null)"
  elif [ -f "$git_dir/rebase-merge/interactive" ]; then
    additional_info="REBASE-i"
    head_name="$(< "$git_dir/rebase-merge/head-name")"
  elif [ -d "$git_dir/rebase-merge" ]; then
    additional_info="REBASE-m"
    head_name="$(< "$git_dir/rebase-merge/head-name")"
  elif [ -f "$git_dir/MERGE_HEAD" ]; then
    additional_info="MERGING"
    head_name="$(git symbolic-ref HEAD 2>/dev/null)"
  fi
  if [ -z "$head_name" ]; then
    head_name="$(git branch | sed -e 's/^\* //;t;d')"
    if [ "$head_name" = '(no branch)' ]; then
      # "git branch" doesn't show the correct name of a branch after
      # "git checkout {commitish-and-not-the-head-of-a-branch}",
      # so we have to use another method to get the name of {commitish}.
      head_name="($(
        {
          fgrep 'checkout: moving from ' .git/logs/HEAD |
          sed '$s/^.* to \([^ ]*\)$/\1/;t;d'
        } 2>/dev/null
      ))"
    elif [ "$head_name" = '' ]; then
      head_name='(just initialized; nothing commited)'
    fi
  else
    head_name="${head_name##refs/heads/}"
  fi
  if [ -n "$additional_info" ]; then
    additional_info="|$additional_info"
  fi

  echo " [$head_name$additional_info]"
  return 0
}

Vim: ctxabbr - context-sensitive abbreviations

2009-01-10T18:57:55 / vim, release, output / comment

Vimのabbreviations (略語展開)を文脈に応じて定義できるようにするプラグインctxabbrをリリースした。平たく言うと:abbreviateのラッパー関数。文脈の判定方法がまだこなれてないので何とも。

展開に際し<expr>相当のことをできるようにもしようかと思ったけれど、それをするなら'omnifunc'にした方がいいだろう。そして補完候補の絞り込みを緩いパターンマッチングでできるようにすればいい。例えば補完候補が"foo"、"foobar"、"fooujihisa"の3つならばfbだけで"foobar"を選べるようにするということ。

ただそれを実現するならば、既存の補完に対するラッパーとして実装した方が便利だろう。ユーザー定義の補完については対応する関数が提供されるので簡単に実現できるのだけど、組み込みの補完(例えば:help i_CTRL-X_CTRL-L)については対応する関数がないのでそうはいかない。組み込みの補完についてもユーザー定義の補完と同様の関数を提供するようパッチを書こうかと思ったけれど、結構入り組んでいるので全体の構成を把握していなければ書けない。どうしたものかな。

Kensington Orbit Opticalで横スクロールしたい

2009-01-10T11:22:32 / prog, mac / comment

長らくKensington Orbit Opticalを使っているけれど、横スクロールができないので時々困る。ボールを前後に転がしたら上下にスクロール、ボールを左右に転がしたら左右にスクロール、という風にしたい。MouseWorksのScroll With Mouseで上下スクロールはできるのだけど、左右の成分は無視される。スクロール方向を上下と左右で切り替えることはできるのだけど、操作が面倒で使いものにならないし、切り替えのためにボタンが1つ潰れる。

どうにかできないかな。上下左右どちらも有効にした場合、まっすぐ上下スクロールしているつもりでも左右成分が微妙に反映されるという問題はありそうだけど、微小な量は無視することにすれば大丈夫だと思う。できたらできたで便利なので課題として考慮には入れておこう。

なお、先程、偶然にもXcodeのサンプルに「Trackball」というそれっぽい名前のものを発見したのだけれど、これはトラックボールのようなGUIコントロールのサンプルだった。非常にがっかりした。

Vim: ku 0.2.x高速化案 - 実装中(2)

2009-01-09T02:06:55 / vim, output / comment

ku 0.2.xでの高速化案の実装中。作業の前段階として$VIMRUNTIME/syntax/下のファイルを対象に100回ほど補完させてプロファイルを行った。予想通りではあるが、結果としてs:_omnifunc_core ('omnifunc'での補完候補のソート・フィルタリング)とs:_omnifunc_compare_lists (個々の補完候補をソートする際の比較関数)が最も重いと判明した。後者は一見して手のつけどころがないため、前者に対して取りかかった。

s:_omnifunc_core中で最も重要なのは補完候補のソート優先順序の算出。開発初期に分かり易さを重視して書いて以降、ずっとそのままだったため、改めて見直すと無駄が多い。具体的には以下の3点:

無駄なパターンマッチの除去
ユーザーの入力から3種類のパターン(に対応する正規表現)を生成して、補完候補のソートはそれらのパターンに対するマッチ位置をベースに行っている。
ソートではマッチした文字列の先頭位置だけでなく末尾位置も考慮に入れている。両方の位置を一度に算出することができないため、同じパターンに対してmatch()/matchend()をそれぞれ呼んでいる。どちらか片方の呼び出しでマッチに失敗したことが分かった場合、もう片方も失敗することは自明なので、このケースを除去。
ソートでは大文字小文字の区別ありでマッチしたものは区別なしでマッチしたものより優先するようにしている。区別なしにしてマッチしなければ区別ありにしてもマッチしないのは自明のため、このケースを除去。
大文字小文字の区別の有無と同様に、3種類のパターンにおいても「これがマッチしなければあれもマッチすることはない」という関係があるため、それに関するケースを除去。
(ここまで書いてて気付いたけれど、上記の3種類のパターンうち1つはマッチする文字列の長さが固定のため、マッチの末尾位置を算出する必要はなかった。これは後で修正しておこう。)
マッチすることが自明な箇所をスキップ
ユーザーの入力はある条件で自動的に補完するようになっているため、補完候補の文字列中にはマッチすることが自明な箇所がある。与えられたパターンが長ければ長いほど、この箇所についてのマッチで無駄な時間が増えるため、これを除外。
(ただ効果は大きいけれど、前提条件に危ういところがあるため、巻き戻さなくてはならないかも知れない。)
空パターンによるマッチの除去・優先順序の工夫
上記の変更により、空パターンによるマッチが行なわれる機会が増加した。空パターンは常にマッチするためマッチを試みる必要がない。このケースを除去。
また空パターンの場合は算出されるソートの優先順序の値の傾向からs:_omnifunc_compare_listsの実行時間が極端に増える。値を工夫してこれを回避。

これだけで10倍程度は速度が向上した。今の私の環境では$VIMRUNTIME/syntax/下に520個のファイルがあるのだけれど、0.196秒程度で済むようになった。体感的には我慢できるレベル。欲を言えば後半分にしたいのだけど、s:_omnifunc_coreについて今のところ効果の高い高速化案は思い付かない。全体として見ればs:_omnifunc_compare_listsの実行時間の比率が結構あるのだけど、こちらも同様。

N-Quine problem

2009-01-07T00:54:58 / prog / comment

I've just thought of a puzzle: N-Quine. A program which generates a source code of a program which generates a source code of another program ... then the last one generates a source code of the first one, where N means the number of programs in this loop. For example, usual Quine can be treated as 1-Quine, because it generates a source code of itself and there is just 1 program in this loop.

But is it possible to write such program? Or, are such programs already written?

Vim: ku 0.2.x高速化案 - 実装中(1)

2009-01-03T23:48:56 / vim, output / comment

ku 0.2.xでの高速化案の実装中。本体側でのキャッシュを実装。詰めが残っているけれど、まあ大方はできた。

ただこれよりは'omnifunc'での候補のソートが重要な要因なので、そちらの方をやるべきだった。実際の使用においては、キャッシュはセッション間で効くようにしないと体感的に差はないし。

Haskell: outermost reduction and stackover flow

2009-01-03T20:48:56 / haskell, output / comment

Twitterにて@ikegami__さんに教えてもらった。実際に試してみると確かにfoldlはスタックオーバーフローになる:

$ ghci
GHCi, version 6.8.3: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> foldl (+) 0 [1..1000000]
*** Exception: stack overflow
Prelude> import Data.List
Prelude Data.List> foldl' (+) 0 [1..1000000]
500000500000

最初は何でだろうかと思ったけど、Haskellは遅延評価が基本なので、問題の式をfoldlの定義に沿ってoutermost reductionしていくと、長大なリストに対してそうなるのは自明でした。foldlの場合、戻り値は与えられたリスト中の要素全てを用いなければ求まらないため、outermost reductionではこのような問題が起きるのですね。

foldl            :: (a -> b -> a) -> a -> [b] -> a
foldl f z []     =  z
foldl f z (x:xs) =  foldl f (f z x) xs
foldl (+) 0 [1..1000000]
-- foldl (+) 0 1:[2..1000000]

foldl (+) ((+) 0 1) [2..1000000]
-- foldl (+) ((+) 0 1) 2:[3..1000000]

foldl (+) ((+) ((+) 0 1) 2) [3..1000000]
...

一方、foldl'seqを用いてzの算出を先に終えるため、foldlのような問題は起きないことが分かります。

foldl' f z []     = z
foldl' f z (x:xs) = let z' = f z x in z' `seq` foldl' f z' xs
foldl' (+) 0 [1..1000000]
-- foldl' (+) 0 1:[2..1000000]
-- let z' = (+) 0 1 in z' `seq` foldl' (+) z' [2..1000000]
-- let z' = 1 in z' `seq` foldl' (+) z' [2..1000000]
-- 1 `seq` foldl' (+) 1 [2..1000000]

foldl' (+) 1 [2..1000000]
-- foldl' (+) 1 2:[3..1000000]
-- let z' = (+) 1 2 in z' `seq` foldl' (+) z' [3..1000000]
-- let z' = 3 in z' `seq` foldl' (+) z' [3..1000000]
-- 3 `seq` foldl' (+) 3 [3..1000000]

foldl' (+) 3 [3..1000000]
...

しかし、いつseqを使うべきかは素人目にも難しいそうで困りましあ。foldlのようにリスト中の値を全て使う場合はoutermost reductionされると困ります。かといって下手にseqを使うと遅延評価によるメリットが損なわれるはずです。実際、foldl'a strict version of foldlと説明されてはいるものの、xsについては遅延評価のままですし。

とはいえ、この問題なら簡約手順さえ知っていれば容易に理解できることなので、モナドより難しいとは思えません。経験を積めば見解が変わるのでしょうか。

MacBook: リッドクローズドモードで使用中は蓋の開閉でスリープしないで欲しい

2009-01-03T10:34:57 / mac / comment

現在のメインマシンはMacBookで、基本的にリッドクローズドモードで据え置きマシンとして使用しているのだけど、稀に蓋を開けてデュアルモニター的に使う場合もある。

この時、MacBook側のモニターが不要になったからといってMacBookの蓋と閉じるとスリープしてしまう。確かにこの挙動自体は普通なのだけど、元々リッドクローズドモードで使っていた場合は蓋を閉じることでスリープされる方が不便だと思う。どうにかできないかなぁ。

仕方がないので、今のところ使わなくなったときはMacBookのモニターの輝度を最低に落としている。

ハードコアポエム読んでる

2009-01-03T00:35:06 / diary / comment

合間合間、久々にハードコアポエムを読んでいるけれど、色々ときつい。刊行順に読んでいるのだけど、先鋒から既に最大出力なので、これを乗り越えると後はもう楽勝なような、そうでもないような。

Vim: ku 0.2.x高速化案

2009-01-02T23:26:29 / vim, output / comment

ku 0.2.xでの高速化の案、正確には高速化するための予防線のメモ。これに合わせた実装はこれから。というか今日は寝過ぎた。

今日の買物: 本、雑誌、ぬいぐるみ、及びハードコアポエム

2009-01-02T02:07:12 / diary, shopping, book / comment
Today's Shopping: Books

なんか、魔が差した。左から順に:

Vim: ku 0.2.xに向けての作業予定

2009-01-01T23:23:00 / vim, output / comment

kuは九分九厘必要なところができたので、以降は設計上の不味い点を直そうと思う。取り敢えず大きいところとしては以下の2点が挙げられる:

他にも細々とした問題点はあるけれど、特に取り上げるほどのことではないので省略。

2009年の抱負

2009-01-01T01:23:00 / diary / comment

ぼちぼちやろうかなというレベルのものも入れると: