Dwarf Standing on the Shoulders of Giants

If I have seen further it is only by standing on the shoulders of giants. by Issac Newton

2010年1月30日

LL プログラミング

つい先日、ヒストグラムを作る用事があったが、 gnuplot には生データをヒストグラムに変えてくれる機能はついていないので、自分で作ることにした。最初はワンライナーで書いていたのだが、根本のアルゴリズムが sort して uniq するというものであったので、データ数が多くなると遅い。それ以上に、ヒストグラムを規格化したくなったので、そうすると総和を求めて後からそれで割るわけだから、ループを二回回さなくてはならず、そもそもワンライナーでは書けない。そういうわけで、自分でスクリプトを書くことにした。

スクリプトを書くにあたって、まあアルゴリズムとかもそりゃ大事なのだろうが(データ構造はたぶんスクリプトの場合ほとんど問題にならないはず)、何よりも大事なのは言語の選択だと思う。自然言語でも Sapir-Whorf だとか言われるが、プログラミング言語においても全く同様で、言語がプログラミングパラダイムを非常に強く規定するので、それを通してプログラマの思考も支配されることになる。

とりあえずデータ処理をするにあたって、一番最初に考えるべき選択肢は awk だと思う。が、この場合はループを二回回す以上、 awk は却下。次に、 Coreutils とワンライナーの awk や ruby 等を使って簡単に書けるならば、シェルスクリプトを検討すべき。だが、この操作ではループを回したりデータを配列変数に貯めたり四則演算をしたりといった操作が必要になる。ならば、シェルスクリプトは却って面倒であろう。そうなると選択肢は perl か ruby となる( Python は自分が使えない/使う気がないので最初から却下)。ここでは標準入力をいじったり、あるいはオプションを getopts を使って処理したい。そうすると perl の方が向いている気がする(そう考えて書いたが、よくよく考えると ruby の方が簡単だったかもしれない)。

コードはこんな感じ。最初はワンライナーから始めたが、エラー処理等つけるとこんなにタイプ量が多くなってしまった。少し負け組な気がする。

#!/usr/bin/perl

use     POSIX;
use Scalar::Util qw( looks_like_number ); 
use     Getopt::Std;

getopts("nk:b:e:",\%opt);

print   keys %_;

if(exists $opt{k})
{
        $split  = 1;
        $key    = $opt{k}-1;
}
else
{
        $split  = 0;
}

if(exists $opt{r})
{
        $isreg  = 1;
}
else
{
        $isreg  = 0;
}

($unit,$input)  = @ARGV;
unless( looks_like_number($unit) && $unit>0 )
{
        print   STDERR  << "END";
histogram [-kbe] unit [input]
  -n       :    normalization
  -k <key> :    key
  -b <val> :    begin value
  -e <val> :    end value
END

exit    1;
}

if( $input eq "" ){     $input  = "/dev/stdin"; }

open( FIS, $input );
@list   = ();
while(<FIS>)
{
        chomp;
        if( $split )
        {
                @temp   = split /\s+/;
                $_      = $temp[$key];
        }
        push( @list, $_ );
}
close( FIS );

foreach (@list)
{
        $hashkey        = floor($_/$unit)*$unit;
        $hash{$hashkey}++;
}
@hashkeys       = sort { $a <=> $b }    keys    %hash;

if( looks_like_number($opt{b}) ){       $begin  = $opt{b};      }
else{   $begin  = $hashkeys[0]; }
if( looks_like_number($opt{e}) ){       $end    = $opt{e};      }
else{   $end    = $hashkeys[-1];        }
if( $begin > $end ){    print   STDERR  "Strange begin/end value\n";    }


for     ( $i = 0 ;  ; $i++ )
{
        $_      = $i*$unit+$begin;
        if( $_ > $end ){        last;   }
        unless( exists $hash{$_} ){     $hash{$_}       = 0;    }
        if( $isreg ){   $hash{$_}       /= $#list;      }
        print   "$_\t$hash{$_}\n";
}

ちなみに、自分のコーディングスタイルはタブを多用するもの。こうやって expand で開くとかなり見にくい。

シェルスクリプトを使ったものとしては、こういうのが気に入っている。どうせ platex は2回実行して、その後に dvipdfmx を実行して、最後にファイルを開くのは決まりきっているのだから、こういうスクリプト。

#!/bin/zsh

isslide=0
while   getopts s       OPT
do
        case $OPT in
        "s" )   isslide=1;shift
        esac
        if      [ $# -eq 1 ]
                then
                        break
        fi
done

arg=`dirname $1`/`basename $1 .tex`


platex  $arg
platex  $arg

if      [ $isslide -ne 0 ]
        then
                dvips -z -f     $arg | bkmk2uni >$arg.ps
                cjkps2pdf       $arg.ps
        else
                dvipdfmx -f dl-14.map   $arg
fi

gnome-open      $arg.pdf        1>/dev/null     2>/dev/null &

ちなみに、オプション -s はスライド作成に powerdot を使い、 dvipdfmx が使えないときのため。

やはりプログラミングをしていて一番楽しいのは ruby で、気に入っているのはこのようなコード。たくさんある研究室のサーバのうち。一体どれが空いているのかを調べるのはかなり面倒なので、こういうスクリプトを書いた。

#!/usr/bin/ruby

servers = [ 'foo', 'bar', ... , 'hoge' ]  #server's names in the lab
servers.each    do      |server|
        print   "#{server}:\n"
        print   `ssh #{server}  ps axu|gawk '{sum+=\$3}END{print sum}'`
end

もちろんほぼ同じタイプ量で同じ内容のコードを perl でも書けるが、なぜか ruby の方が楽しい。途中に awk を使っているのもミソ。

ラベル:

0 件のコメント:

コメントを投稿

この投稿へのリンク:

リンクを作成

<< ホーム