最近の更新

関連


その他いろいろ

MODxでつくる! 最強のCMSサイト カバー
MODxでつくる! 最強のCMSサイト

はやくMODx 2.0でないかなあ

zshで

cd -[TAB]

とすると、setopt auto_pushdしていた場合は過去にいたディレクトリが

~% cd -
0 -- /home/**/work/dotfiles/.vim  (一番昔)
1 -- /home/**/work/dotfiles
2 -- /home/**/work                (直近)

のように表示されます。しかし、このままタブを押し続けていくと、最も昔にいたディレクトリから順次選択れていくのはやや直感的ではありません。できれば直近にいたディレクトリを優先してリストアップしてほしい。

zstyleのヘルプを見ても’completion:complete:cd:*’でリストを逆順にする方法が分からず、途方に暮れていたところ…

Zsh Workshop: The Directory Stackを見ていて

cd +[TAB]

が目的の動作だったと閃きました。

~% cd +
1 -- /home/**/work
2 -- /home/**/work/dotfiles
3 -- /home/**/work/dotfiles/.vim

ただUS配列では+はシフトが必要で若干面倒なので、setopt pushd_minusとして+と-の意味を入れ替えると良さそうです。

しかしリスト/メニューを逆順にするzstyleが見つからないのは結構問題です。file-sortに関しては’reverse’というオプションがあるらしいですが、それ以外は候補を自分で生成するしかないのでしょうか。

リビジョン間の差分やコードの色分け表示に対応したsvnリポジトリのブラウザWebSVN
こんな便利な物があるとは知らず、今までブラウジング目的だけでtrac使っていました。
早速乗り換えるべく設置してみましたが、バージョン2.2.1をUTF-8で使っていると、他の文字コードで化けてファイルの中身が一切表示されなかったりします。

そこでWebSVNの文字化け対策 - okonomiの日記を参考に2.1.1版のパッチを書きました。/usr/bin/nkfを利用しています。

diff -ur websvn-2.2.1/include/svnlook.php websvn/include/svnlook.php
--- websvn-2.2.1/include/svnlook.php    2009-05-10 20:19:20.000000000 +0900
+++ websvn/include/svnlook.php  2009-06-28 09:44:13.000000000 +0900
@@ -558,7 +558,7 @@
     // If there's no filename, we'll just deliver the contents as it is to the user
     if ($filename == "") {
       $path = encodepath($this->getSvnpath($path));
-      passthruCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' '.$pipe);
+      passthruCommand($config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' '.'|/usr/bin/nkf -w'.$pipe);
       return $highlighted;
     }

@@ -570,7 +570,7 @@
     if ($l == "php") {
       // Output the file to the filename
       $path = encodepath($this->getSvnpath($path));
-      $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename);
+      $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).'|/usr/bin/nkf -w'.' > '.quote($filename);
       $retcode = 0;
       execCommand($cmd, $retcode);
       if ($retcode != 0) {
@@ -632,6 +632,7 @@

         $path = encodepath($this->getSvnpath($path));
         $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' | '.
+          "/usr/bin/nkf -w |".
           $config->enscript." --language=html ".
           ($l ? "--color --pretty-print=$l" : "")." -o - | ".
           $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $tempname";
@@ -648,7 +649,7 @@
       } else {
         $highlighted = false;
         $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->getSvnpath($path)));
-        $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename);
+        $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' |/usr/bin/nkf -w> '.quote($filename);
         $retcode = 0;
         execCommand($cmd, $retcode);
         if ($retcode != 0) {
@@ -727,7 +728,7 @@

     // Output the file to the filename
     $path = encodepath($this->getSvnpath($path));
-    $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.quote($filename);
+    $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).'|/usr/bin/nkf -w'.' > '.quote($filename);
     $retcode = 0;
     execCommand($cmd, $retcode);
     if ($retcode != 0) {
@@ -777,7 +778,7 @@

       // Output the file to a temporary file
       $path = encodepath($this->getSvnpath($path));
-      $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' > '.$tmp;
+      $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' | /usr/bin/nkf -w > '.$tmp;
       $retcode = 0;
       execCommand($cmd, $retcode);
       if ($retcode != 0) {
@@ -797,12 +798,13 @@
       if ($config->useEnscript) {
         $path = encodepath($this->getSvnpath($path));
         $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).' | '.
+          "/usr/bin/nkf -w|".
           $config->enscript." --language=html ".
           ($l ? "--color --pretty-print=$l" : "")." -o - | ".
           $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote;
       } else {
         $path = encodepath($this->getSvnpath($path));
-        $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev);
+        $cmd = $config->svn." cat ".$this->repConfig->svnParams().quote($path.'@'.$rev).'|/usr/bin/nkf -w';
         $pre = true;
       }

以前書いたC++がPythonより重い…でソースコードを載せると言いつつ載せて無かったので、引っ張り出してきました。

ptest_regex.zip をダウンロード

昔のことで記憶が曖昧ですが、たしかC++, Python, 生grep (追記: C#も)で

データセット 実験1 実験2 実験3
ソースコード int.*\( ^.*$ ^.*int.*$
のち^.*\(.*$
のち^.*\).*$
(英文?)和文 は.*。 ^.*$ ^.*は.*$
のち^.*た.*$
のち^.*。.*$

をそれぞれ試しています。多分。

用いたデータセットや結果は過去のエントリを参照してください。

勿論「C++よりPythonが全面的に速い」「C++とPythonで同じアルゴリズムを実装すると後者が速い」ということが言いたいのではありません。(そしてそれは多分言えないと思う)

適当に使えそうな/気軽に使ってしまうライブラリ(C++ではboost, ICU、Pythonでは標準のエンコーディング変換関数とre)を自然に使ってみるだけだとPythonの方が早いこともあるらしい、という驚きと悲しみをちょっと記録しただけです。

「自然に」が自分の中では結構大事で、無理して良いなら当然自由度の高い低レベルな言語が一番チューニング出来る可能性がある、でもパラメタが多すぎて難しい。
まあPythonは標準ライブラリを使っているため、ちょっとチューニングしたいと思ったときに改変がやや面倒なのに対し、C++は標準ライブラリ以外を使っている(勿論stlも使ってますが)のでその辺は楽、という点はやや不公平ではあるかもしれませんが。

C++を自然に使ってより早い結果が得られる方法があれば、教えてもらえると嬉しいです。xpressiveとか使ったこと無いけどどうなんんでしょう。

追記
忘れていましたがPython vs C++ vs C# @ 7bitでC#版も書いていたらしい。記憶にない… でもソースもあったので追加しておきました。

昨日、zshでsudoや|を無視して履歴から補完する設定を紹介しましたが、
これをC-P/C-Nにしてしまうと、例えば本当にsudo付きだったldconfigを補完する目的で

sudo l(補完)

とすると肝心の物が引っかからず、

sudo ls ..

などと補完されてしまうのが時々鬱陶しいので、Alt-P / Alt-Nに設定してみました。

zle -N smart-search-history-backward
bindkey '^[p' smart-search-history-backward
zle -N smart-search-history-forward
bindkey '^[n' smart-search-history-forward

この辺は好みに応じてC-P/Nの場合はパイプだけ無視して補完、M-P/Nはsudoも含めて無視して補完、などとすると良いかもしれません。

ちょっと嵌ったのが、bindkeyのキーでAlt(Meta)を指定するには\M-pとか^[pとかありますが、これは大文字小文字センシティブらしいこと。Ctrlでは気にする必要無かったので全部大文字にしてたら引っかかりました。

ちなみに^[は本当のC-[ではなく、単にハットと開始カギ括弧です。

zshでhistory-beginning-search-backwardをCtrl-Pなどに割り当て、入力したところまでマッチするように履歴から補完するように設定している方は多いと思います。

しかし、プロンプト先頭からカーソル位置までが検索対象となってしまうため、先にsudoを入れたり、パイプの途中から補完するといったことはできません。

そこで、現在のプロンプト入力からいくつか指定のパターンを除外した上で補完できるようなzleのウィジェットを書いてみました。

履歴に

sudo apt-get update
make
make install
ldconfig
less

があったとして、普通は

sudo m(補完)

としても何も補完されませんが、この設定を用いると

sudo make install => sudo make

のように補完してくれます。

sudo (補完)

(補完に必要な文字列を何も与えない)としたとき、今回の機能を有効にするかどうかは好みが分かれるところだと思いますが、一応安全な方を取って無効にし、普通にsudoで始まる履歴を検索して

sudo apt-get update

と補完するようにしました。

キーバインドは過去方向がC-P、逆がC-Nです。

# complete from history ignoring leading (sudo, '|', man, which, ..) in current prompt
# only complete in this way if there are some other input than those ignoring patterns
# examples with history:
#  ldconfig
#  make
#  make install
#  less
# case:
#  $ sudo <C-P>  => $ sudo ldconfig
#  $ sudo m<C-P>  => $ sudo make install => $ sudo make
#  $ wget -O - http://.../ | l<C-P> => $ wget -O - http://.../ | less
SMART_SEARCH_HISTORY_PATTERN='(sudo|\||man|which)'
function smart-search-history {
  local trim="$(echo "$LBUFFER" | sed -r "s/^.*${SMART_SEARCH_HISTORY_PATTERN} *//")"
  local old_leader="$(echo "$LBUFFER" | sed -r "/${SMART_SEARCH_HISTORY_PATTERN}/s/(^.*${SMART_SEARCH_HISTORY_PATTERN} *).+?$/\\1/p;d")"
  if [ -n "$trim" ]; then
    LBUFFER="$trim"
    zle $1
    LBUFFER="$old_leader""$LBUFFER"
  else
    zle $1
  fi
}
function smart-search-history-backward {
  smart-search-history history-beginning-search-backward
}
function smart-search-history-forward {
  smart-search-history history-beginning-search-forward
}

zle -N smart-search-history-backward
bindkey '^P' smart-search-history-backward
zle -N smart-search-history-forward
bindkey '^N' smart-search-history-forward

前回、空のプロンプトでEnterを入力するとlsするウィジェットを書いたときに$BUFFERの便利さに気づきましたが、今回もその流れで$LBUFFERというzleの変数を用いています。

現実逃避として格好のシェルスクリプト書き。今回はXのクリップボード操作コマンドであるxselを利用して、stdout (+stderr)をクリップボードにコピーする(更にまたstdoutに書き戻す)zshのグローバルエイリアスを書いてみました。

if exists xsel; then
  alias -g   B=" | xsel -bi" # stdout => clip
  alias -g  B2=" 2>&1 | xsel -bi" # stdout + stderr => clip
  alias -g  BB=" | (cat 1>&2 | xsel -bi) 2>&1" # stdout => clip and stdout
  alias -g BB2=" 2>&1 | (cat 1>&2 | xsel -bi) 2>&1" # stdout, stderr => clip and stdout
fi

zshにおけるグローバルエイリアスとは、alias -gで定義され、「コマンドライン中のどこでも」展開されるエイリアスです。普通のエイリアスは先頭でないと展開されませんが、グローバルエイリアスを用いることで、コマンド末尾につけるパイプ的な処理を効率化することが出来ます。

lessやgrepなどをLとかGとか設定すると便利です。

今回設定したB, B2, BB, BB2ですが、

  • B はstdoutをクリップボードにコピー
  • B2 はstdoutとstderrをクリップボードにコピー
  • BB はstdoutをクリップボードにコピーし、またstdoutに出力
  • BB2 はstdoutとstderrをクリップボードにコピーし、またstdoutに出力

となっています。
名前をC, C2, .. としなかったのは、

make CC=g++

などとしてコマンドライン中で普通に使われそうだったからです。この辺は完全に好みなので何でも良いと思いますが。

早速zshrcに追加したので、まだzshを使っていなくても、丸々コピペですぐに便利設定にできます。

2009 06 20

vim_python

vimでpythonを有効にしてコンパイルすると、*.vimの中でpythonが使えるようになる。

python <<END
import vim
c = vim.current.window.cursor
command("normal F(")
bc = vim.current.window.cursor
vim.current.window.cursor = c
print bc
END

プログラム書いてて関数の引数リストを編集したいときに使えるテキストオブジェクト(citとかdawとかのあれ)を作りたかったが、最も単純にやるにしてもF(ct,とかF,ct)とか組み合わせが多くなる。
その上引数リストの区切りではないカンマや括弧に妨害されないように判定するのは難しそうなので、慣れないvimスクリプトよりはPythonで丸々書いてしまったほうが早そうと思って練習中です。

しかしテキストオブジェクトの作り方がよく分からん。ヘルプにはないようだが、surround.vim見て、それでもダメならソース見るかな。実は実装としては別にオブジェクトでも何でもない、表面上統一されたキーマップなだけかもしれない…

Haskellでラミィキューブシリーズその2。

前回ColorやKubを定義しましたが、今回はそのタイルやリスト関連の補助関数を定義します。

type Kub   = (Color, Integer)
type Hand  = [Kub]
type Run   = [Kub] -- special set of Sets.
type Grp   = [Kub]
type Set   = [Kub]
-- 色や数値を取り出す
getCol (c,x) = c
getNum (c,x) = x
toCols = map getCol
toNums = map getNum
filterColors col = filter ((==col).getCol)
filterNums num = filter ((==num).getNum)
-- タイルのリストをソートする
sortHand :: [Kub] -> [Kub]
sortHand xs = concat $ map (c -> sortCol c xs) colors
              where sortCol col ys = sortBy (x y -> if (getNum x) < (getNum y) then LT else GT) (filterColors col ys)
-- 重複する手(中身が同じで順番だけが異なる)のリストを受け取り、ユニークにする
uniqHand :: [Hand] -> [Hand]
uniqHand [] = []
uniqHand (x:xs)
  | x `handin` xs = uniqHand xs
  | otherwise     = x:(uniqHand xs)
  where handin s []     = False
        handin s (h:hs)
          | (sortHand s) == (sortHand h) = True
          | otherwise                    = handin s hs

また、タイルのリストを判定する関数(predicate)を定義します。

-- 全て(指定したor任意の)同一の色or数値である
isAllNumber n xs = and $ map ((==n).getNum) xs
isSameNumber xs = (length $ nub $ toNums xs) == 1
isAllColor col xs = xs == (filterColors col xs)
isSameColor []         = True
isSameColor ((c,x):xs) = isAllColor c xs
-- 階段になっている(昇順)
isStairs []        = True
isStairs (x:[])    = True
isStairs (x:xx:xs)
  | getNum x + 1 == getNum xx  = isStairs (xx:xs)
  | otherwise    = False
-- ランである(同じ色でソート後に3以上の長さの階段になるもの)
isRun xs = and [isSameColor xs, isStairs (sortHand xs), length xs >= 3]
isColorRun col xs = and [isAllColor col xs, isRun xs]
-- グループである(色違いで同じ数値で3枚以上あるもの)
isGrp xs = and [isSameNumber xs, length xs >= 3]
isNumberGrp n xs = and [isAllNumber n xs, isGrp xs]
-- ランまたはグループである
isSet xs = or [isRun xs, isGrp xs]
-- これは[Kub] -> [Kub]型。場が成立しているか判定
isCleanField xs = and $ map isSet xs

これで基本的な判定は出来ることになります。次回は単純に「指定タイル群(例えば手+場)からランやグループを抽出する」関数を書いてみます。実際これは最後まで使われるか怪しいですが、一応テスト用ということで。

Haskellをまともに使えるようにしないといけなくなったので、練習がてらRummikub [Wikipedia]関係のプログラムを書いてみようと思います。

ゲームの様々な要素がたいてい1次元で表現出来そうなので、練習台には適してそうです。プレイ人口からして既にありそうですが、勉強と言うことで車輪を再発明します。

まずデータ構造から。タイルを表現するKubを色(Color)と数値のタプルにします。

-- data types
data Color = Red | Blu | Yel | Blk
             deriving (Eq, Show)
type Kub   = (Color, Integer)

Kub関連の関数を定義。sortHandでKubのリストを赤・青・黄・黒ごとにソートします。

-- filters
colors = [Red, Blu, Yel, Blk]
getCol (c,x) = c
getNum (c,x) = x
toCols = map getCol
toNums = map getNum
filterColors col = filter ((==col).getCol)
filterNums num = filter ((==num).getNum)
sortHand :: [Kub] -> [Kub]
sortHand xs = concat $ map (\c -> sortCol c xs) colors
              where sortCol col ys = sortBy (\x y -> if (getNum x) < (getNum y) then LT else GT) (filterColors col ys)

今後の見通しとしては、

  • ラン・グループなどの判定関数
  • 手(+場)から可能なラン・グループなどを列挙
  • 手(+場)をできるだけ使い切ってラン・グループを列挙

あたりは普通にやれば出来そうです。

その後ジョーカーを扱うとなると、タイルをデータ型

data Tile = Kub Color Int | Joker deriving (Eq, Show)

とするなどと結構面倒が予想されます。しかもJokerは条件を満たさないと外せないので、場を単にKubの集合としてではなく、結合を維持した入れ子リストで表現する必要が出てくる、など。

できれば場同士の距離判定から、完全な解だけでなく強さ(見通しの深さ)を調整できるプレーヤに仕立てたいと思っています。

ウィンドウが非アクティブになると、自動的にシェード状態(タイトルバーだけ見える状態)にする
AutoShade 1.02をリリースしました。

実は今までのバージョンは、設定ファイル冒頭でPythonのモジュール”re”をロードしていて、失敗した場合にアプリケーションごと巻き込んで落ちるという事態が発生していました。

今回から、Python 2.6がシステムになく、”re”がロードできない状況でも起動・動作するように設定スクリプトAutoShade.pyを修正しました。

re(正規表現)やその他のPythonのモジュールを使用する場合は、個別にPython 2.6をインストールしてください。

« 前のページ次のページ »