2010年11月30日火曜日

Emacs文字コード判別の優先順位を変える

Emacsで日本語を含むファイルを開くと、まれにエンコードの自動判別に失敗することがある(仕事上の経験では、utf-8で書かれたcssファイルをsjisと勘違いすることが多い)。

そうした場合は、2つの関数 prefer-coding-system, find-alternate-file を使ってファイルを読み込み直す。

エンコードを変えてファイルを開き直すときのキー入力(例)

;utf-8-dos の優先順位を上げる
M-x prefer-coding-system [RET] utf-8-dos
;find-alternate-file関数を実行。現在のバッファを削除して読み直す
C-x C-v foo.txt

面倒だからこの2つをラップする関数を作ったほうがいいかも。

2010年11月29日月曜日

Lisp の every と some 風の関数を JavaScript で

Common Lisp や Emacs Lisp 等では every や some という高階関数が提供されており、複数の値に対するテストを簡潔に記述することができる(述語関数/predicateの、リストへの適用)。

Lispの例

  (every #'evenp '(1 2 3)) => nil        ; 偶数でない要素があるため偽(nil)
  (some #'evenp '(1 2 3)) => t           ; 偶数の要素が少なくとも1個あるため真(t)

ここで、evenp は引数が偶数の場合に t を返す組み込み関数

JavaScriptの例

JavaScriptで同様のことを実現したいという場合は、次のような形になるはず。

// 関数定義
function every(p, arr){
  for (i = 0, n=arr.length; i < n; i++) {
    if(!p(arr[i])) return false;
  }
  return true;
}
function some(p, arr){
  for (i = 0, n=arr.length; i < n; i++) {
    if(p(arr[i])) return true;
  }
  return false;
}
function evenp(x){
  return (x%2==0) ? true : false;
}
// 実行
every(evenp, [1, 2, 3]);   // => false
some(evenp, [1, 2, 3]);    // => true

こんな感じ。

実は every, some が組み込まれているブラウザもある

念のため調べたところ、Firefox(Gecko 1.8b2 以降)にはこれらの関数が配列のメソッドとして実装されているとのこと。

everyの利用例(Mozilla Developer Center より転載)

function isBigEnough(element, index, array) {
  return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed は false

mozilla.orgのページにある「互換性」のコードを利用すれば、Firefox以外のブラウザでもeveryが利用できるようになる。

2010年11月25日木曜日

VBAでeval(Evaluate関数)を使ってみる

2010年現在でも、中小零細企業では表計算から請求書や納品書等の書類にいたるあらゆる業務で Microsoft Excel が活躍する。したがって、Excel VBA でのプログラミングをすることも多い。

VBA の場合、セルにデータを入力しておくことでプログラムを制御するのが一つのマイベストプラクティス。たとえば一番最後のワークシートにプログラム上の定数を記載しておく。後日ユーザーがプログラムの挙動を変えたくなったらそのシートをユーザー自身に編集してもらうことで、自分の仕事を減らせる。

この考えをもう一歩進めると、データだけではなく「手続き」や「関数」までもセルに記述しておきたくなる。たとえばある金額の計算過程で現在は 5%増しの計算をしているのだが、来年になったら (8%増し + 100円) に変えたい、といった具合である。

以下ではこの例題をサンプルとして、変更を見越した関数を設計し、VBAの "Evaluate"関数と組み合わせることによって目的が達成できることを示す。

計算関数を定義する

引数は2つ(利率、上乗せ固定額)

Function f計算(利率、上乗せ固定額)
    f計算 = 金額 * (100 + 利率) / 100 + 上乗せ固定額
End Function

VBAでは日本語が利用可能なので、変数名や関数名になるべく日本語を使う。

グローバル変数を定義する

上記の関数内には、自由な変数、つまり仮引数として定義されていない変数「金額」がある。この値は実行時にワークシートのセルに入力されているデータであるのだが、残念ながら上記の関数内から参照することができない(Evaluateにより実行されるコードからはセルの値を"Cell(1, 1)"というような形で参照できない)。したがって関数実行前にセルから読み込み、グローバル変数に設定しておく。そのための入れ物を定義するのが次のコード。

Public 金額 As Long

このPublic文は標準モジュール等のトップレベルに書く。

計算を行うマクロを定義する

  1. グローバル変数に、名前付きセル「入力セル」を代入
  2. 「関数セル」に入力されているコードを適用
  3. 結果を「出力セル」に入力

Sub Macro1()
  金額 = Range("入力セル").value
  計算結果 = Application.Evaluate(Range("関数セル").value)
  Range("出力セル").value = 計算結果
End Sub

ワークシートのセルに必要なデータおよび関数呼出しコードを書く

  1. 任意のセルに「1000」と入力し、セルの名前を「入力セル」とする
  2. 〃 「f計算(5, 0)」と入力し、 〃 「関数セル」とする
  3. 任意のセルを選択して、セルの名前を「出力セル」とする

マクロを実行

  1. [f8]キーを押すなどしてマクロ「Macro1」を実行
  2. うまくいけば「出力セル」に 1050 と出力される

計算を変えてみる

  1. 「関数セル」の内容を「f計算(8, 100)」に変更
  2. マクロを実行
  3. うまくいけば「出力セル」に 1180 と出力される

まとめ

以上で、セル内のプログラムコード書き換えによりプログラムの挙動を変えることができるようになった。

「f計算」以外にもっと関数を定義してやることで、「関数セル」に書けるコードも自由度が増していくはず。

ただし、入力データを参照するためにグローバル変数が必要になるのであまりきれいとは言えない。

また、( )を多用したようなコードをユーザーに書き換えてもらうのは難しいから、変更はプログラム担当者の仕事になるだろう。

2010年11月22日月曜日

tableのtd要素にエクセルのセル番号のようなclassを設定するJavaScript

ここで「エクセルのセル番号」というのはつまり"A1"とか"AZ1"のような列と行を表す文字列のこと。

※「セル番号」よりももっと正式な名称があると思う。

Webサイト制作においては、tableを構成するすべてのセルに対してそれぞれCSSで柔軟にデザインを変えたいというケースが稀にある。そうした場合に手っ取り早いのは各セルにセル番号のような文字列をクラスとして設定してあげること。そして、css内で td.A1{ ...; } ... td.AZ1{ ...; } のようなスタイルを指定する。

小さいtableなら手動でやってもよいが、大きくなるとJavaScriptで動的に設定することになる。以下がそのためのJavaScriptの例。

<script type="text/javascript">
// jQuery等のライブラリ使わないコードです(仕事上、そういう前提だったので)
// 関数定義
function setClassToTd(){
  var alphas = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  var tbls = document.getElementsByTagName('table');

  // html内のすべてのtable要素を処理する
  for(var i=0, n=tbls.length; i<n; i++){
    var trs = tbls[i].getElementsByTagName('tr');
    for(var j=0, m=trs.length; j<m; j++){
      var tds = trs[j].getElementsByTagName('td');
      for(var k=0, l=tds.length, cls; k<l; k++){
        if(k < alphas.length){
          cls = alphas.charAt(k);
        }else{
          cls = charAt(Math.floor(k / alphas.length) - 1) + charAt(k % alphas.length);
        }
        cls += j + 1;
        // 既存のclass設定を消さないように
        var clsbuf = tds[k].getAttribute('class')||tds[k].getAttribute('className');
        if(clsbuf){
          cls += ' ' + clsbuf;
        }
        tds[k].setAttribute('class', cls);
        tds[k].setAttribute('className', cls); //IE
      }
    }
  }
}
// loadイベントで実行 
if (window.attachEvent) { //IE
  window.attachEvent("onload", setClassToTd);
}else{
  window.addEventListener('load', setClassToTd, false);
}
</script>

2010年11月9日火曜日

FLVのメタデータ(動画の幅や高さ等)を調べるPerlモジュール

仕事では、FLVファイルをサイトに掲載してください、という類の要求がしばしばある。

大抵の場合、FLVファイルだけポンと渡されるので、実際にFLVを掲載するために必要な「幅」「高」さといった情報はもらえないことが多い。そのくらいメールに付記して欲しいと思う反面、渡す側の人間にすればそんな細かい情報が要るとは見当がつかないだろうから、この現象は仕方がないということで自ら調べることになる。

Perlを利用する場合は、「FLV::Info」というモジュールで簡単に調べられる。なお、インストールはCPANシェルから特に問題なく行うことができた(CentOS5.3での話)。

メタ情報出力プログラム例

#!/usr/bin/perl
# flvinfo.pl
# 引数で指定されたFLVファイルのメタデータを出力する
# 前提:FLV::Infoがインストールしてあること(CPANにある)
use FLV::Info;
my $reader = FLV::Info->new();
$reader->parse($ARGV[0]);
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();

実行例

$ perl ./flvinfo.pl foo.flv
748 video frames
File name          foo.flv
File size          1082585 bytes
Duration           about 24.925 seconds
Video              748 frames
  codec            Sorenson H.263
  height           240
  type             interframe/keyframe
  width            320
Audio              954 packets
  format           MP3
  rate             44100 Hz
  size             16 bit
  type             stereo
Meta               1 event
  audiocodecid     2
  audiosamplerate  44100
  audiosamplesize  16
  duration         24.958
  filesize         1082585
  framerate        29.97002997003
  height           240
  stereo           1
  videocodecid     2
  width            320

2010年11月8日月曜日

wgetによるHTTPSダウンロードでありがちなエラー

今日、Cygwinでwget使ったときにエラーになった。Webブラウザと違ってwgetではCAの証明書がインストールされていないから、HTTPSサーバー側のSSL証明書を検証できない。検証できないとエラーになってダウンロードもできない、というのが仕様(割と厳格な仕様)。

このエラーを警告レベルに落として、ダウンロードを成功させるというオプションがあるので、これを有効にすればダウンロードが成功する。

$ wget --no-check-certificate https://example.com/foo.jpg

※あくまで急場しのぎ。理想的なのは、CA証明書をブラウザ等からエクスポートしてあげて `--ca-certificate=file' というオプションでそのファイルを読み込む方法だと思う。

2010年11月5日金曜日

htmlでルビを振る

漢字の読み仮名等を表示したいときに使うruby要素。

ruby, rb, rp, rtという4つもの要素を組み合わせて記述するのがスタンダードらしい(それぞれ、ruby, ruby base, ruby parenthesis, ruby text を表す)。

例:青空文庫の「夢十夜」(夏目漱石)

腕組をして枕元に<ruby><rb>坐</rb><rp>(</rp><rt>すわ</rt><rp>)</rp></ruby>っていると、<ruby><rb>仰向</rb><rp>(</rp><rt>あおむき</rt><rp>)</rp></ruby>に寝た女が、静かな声でもう死にますと云う。

確認したところ、Firefox3.6はルビ表示をサポートしておらず、rbの内容の直後にrp, rtの内容がそのまま表示される。IE6-IE8, Chrome7.0, Safari5.0はOK。

HTML5では、rb(ruby base)を記述しなくてもよいようだ。

2010年11月4日木曜日

Apache 利用可能なモジュールを確認するためのコマンド

Apacheがすでに運用されており、どのモジュールがどのように組み込まれているか知りたい、という状況で使うコマンド。

DSO (Dynamic Shared Object) として動的にロードされているモジュールを表示
# /usr/sbin/httpd -M
Loaded Modules:
 core_module (static)
 mpm_prefork_module (static)
(略)
 proxy_ajp_module (shared)
 ssl_module (shared)
Syntax OK
Apacheコンパイル時に静的に組み込まれたモジュールを表示
# /usr/sbin/httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c 

※上記コマンドを実行javascript:void(0)した環境は、ディストリビューション:CentOS-5、Apache:2.2.3(httpd-2.2.3)

Mercurialプロジェクトを作成してコミットするシェルスクリプト

テストを実行できる程度の成果物ができた時点で、プロジェクトを作成しコミットすることが多い。

そこで以下のようなスクリプトを使うことにした。些細なものだが、hg init、hg commit 等を記憶&打鍵する手間が省けるだろう。

$ cat hginit.sh
#!/bin/sh/ -x
# デバッグフラグ -x を付けている
# 引数無しならカレントディレクトリをプロジェクトのディレクトリとみなす
dir=.;
if [ $# -ge 2 ]; then
  echo 'Too much arguments; Usage: hginit.sh DIR';
  exit 1;
elif [ $# -eq 1 ]; then
  dir=$1;
fi

cd $dir
hg init
hg commit -A -m 'Initial commit'
exit 0;
$ ./hginit.sh myproject

2010年11月1日月曜日

Maildir形式のメールボックスと procmail の組み合わせ

postfix等を使う場合に、昨今はメールボックスの形式をMaildir形式にするのが一般的なのだが、procmail はデフォルト状態でmbox形式のメールボックスを想定してメールの配送を行う仕様になっている。

これを知らないでprocmailによる転送を行っていると、「転送先には届くのだが、元のメールボックス(Maildir形式)にメールが保存されない。調べたらmbox形式でspoolの中に保存されていた」という事態が起こる。

procmailで Maildir形式を使いたい場合は、.forward および .procmailrc の中にその旨を指示しておく必要がある。

~/.forward
"|exec /usr/bin/procmail -f- || exit 75 #~/Maildir/"
~/.procmailrc
MAILDIR=$HOME/Maildir/
DEFAULT=$MAILDIR
…レシピを書く部分…

.forward の中で # が付いている部分、つまりまるでコメントのように見える部分に意味がある情報を書くのがすごいところ。