ページ内ジャンプ:

アレゲなニュースと雑談サイト

taro-nishino の日記から検索

taro-nishino (32033)

taro-nishino
(メールアドレス非表示)
2009 年 11 月 08 日
PM 11:39
Perl

H.Merijn Brand氏と言えば、Text::CSV_XSSpreadsheet::Read等が有名で、皆さんも御世話になった人が多いと思います。氏はCPANモジュールのみならず、p5pでも活躍されていて、特にHP-UXやAIXといった、どちらかと言えば一般のPerlerには馴染みがないエンタープライズなプラットフォームへのPerlポートを手がけています。
ところで、氏のソースコードを読まれた人は気が付くと思いますが、ちょっと変わったコーディングスタイルです。一般的にPerlerのコーディングスタイルはDamian Conway氏の″Perl Best Practices″に沿った方法が多いと思います(中には、まだGNU方式に拘る人もいますが)。何が変わっているかと言いますと、ブロックの閉じブレースのインデントです。私は最初エディタの違いでこう見えるのかと思い、気持ちが悪くて仕方がありませんでした。しかし、馴れるに連れて、何か考えがあるはずだと思い始め、氏のエッセイ″Why my style is best″を見つけ、読んで納得しました。今では氏のスタイルの方が合理的であるとさえ思っていて、私も氏のスタイルを採用しています。ただ、外に出すには完全に少数派なので、いやいやながら大多数派に合わせています。勿論、各人いろいろなスタイルがあっていいと思いますが、見てくれではなく、要はしっかりとした理由とポリシーがあるかが重要だと思います。
私訳を以下に載せておきます。

私のスタイルがベストな理由
H.Merijn Brand

プログラミングのスタイル及びレイアウトについては、人数と同程度に多くの意見がある。私に言わせれば、最も重要なことは、貴方、貴方のチーム、または貴方の会社が指針として従うと決めたことの思考過程を考えることである。
インデントの量、ブレース、ブラケット、括弧の置き方がよく考えられていたことの実感無しにGNUスタイルのコーディングを洗脳されて、非常に多くの(若い)プログラマが学校を卒業する有様を、私は深刻に考える。
私個人にとって、GNUコーディングスタイルは、私が多くのGNUプロジェクトに貢献しない理由の一つである。そのスタイルは私のロジックにフィットせず、しかも私の送るパッチが、その時に根底にあるロジックを理解するのに良い方法だと思うスタイル/レイアウトで私が書いたという理由だけで却下されるのなら、私は諦める。
ここで、(Perl)コードレイアウトの唯一正しいと私が思うもの及び理由を見学するツアーを作ろう。この大部分はPerl::Tidyと正しい.perltidyrcを使って達成されるだろう。

コードブロックの中のインデント
開きブロックブレースは右か左か?
ブレースは左
ブレースはブロックを形成するための単なる構文糖であるので、目に見えるように、条件にではなくブロックに束縛すべきである。閉じブレース(またはPASCALのような言語でのEND)は目に見えるようにブロックの終わりを見せているので、そのブロック自体と同じインデントを持つべきだ。

・インデント幅は4で、タブは可(8にセットされている時)。
・開きブレースは条件と同じ行にすべきである。
・ブロックはインデントされるべきである。
・閉じブレースはブロックと同じインデントを持つべきである。

if ($flag eq "a") {
    $anchor = $header;
    }

継続行のインデント

if ($flag eq "a") {
    $anchor = substr ($header, 0, 6) .
        substr ($char_list, $place_1, 1) .
        substr ($char_list, $place_2, 1);
    }

または以下でも可。

if ($flag eq "a") {
    $anchor =
        substr ($header, 0, 6) .
        substr ($char_list, $place_1, 1) .
        substr ($char_list, $place_2, 1);
    }

ブレースは右

if ($bigwasteofspace1 && $bigwasteofspace2 ||
    $bigwasteofspace3 && $bigwasteofspace4) {
    big_waste_of_time ();
    }

または以下でも可。

if (   $bigwasteofspace1 && $bigwasteofspace2
    || $bigwasteofspace3 && $bigwasteofspace4) {
    big_waste_of_time ();
    }

または以下でも可。

if ( $bigwasteofspace1 && $bigwasteofspace2 ||
     $bigwasteofspace3 && $bigwasteofspace4) {
    big_waste_of_time ();
    }

elseが伴う時は?

if ($flag eq "h") {
    $headers = 0;
    }
elsif ($flag eq "f") {
    $sectiontype = 3;
    }
else {
    print "invalid option: " . substr ($arg, $i, 1) . "\n";
    dohelp ();
    }

垂直方向に合わせる

sub _directives
{
    { ENDIF => \&_endif,
       IF => \&_if,
        };
    } # _directives

subの開きブレースは1カラムにあるべきであり、Viまたはそのクローンを使う人にとっては、}、{、]]、[[は期待通り、そのように動く。
他のコンテナに対するインデントスタイル
開きを垂直方向に合わせる

$dbh = DBI->connect (undef, undef, undef, {
    PrintError => 0,
    RaiseError => 1,
    });

if (!defined (start_slip ($DEVICE, $PHONE,  $ACCOUNT, $PASSWORD,
                $LOCAL,  $REMOTE, $NETMASK, $MTU)) &&
    $continuation_flag) {
    do_something_about_it ();
    }

トークン配置を閉じる

my @month_of_year = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    );

または以下でも可。

my @month_of_year = (qw(
    Jan Feb Mar Apr May Jun
    Jul Aug Sep Oct Nov Dec
    ));

ブロックの閉じブレースと同様に、閉じ括弧は閉じるコンテナのデータに属するのであるから、同じインデントを持つべきである。
水平方向に合わせて定義せよ
勿論

function <space> <paren> <no-space> <first-arg> <comma> <space>

if ((my $duration = travel ($target, $means)) > 1200) {

いらいらの種の一つ。関数名と開き括弧の間にスペースを持つことは、我々がどのように考えているかに非常にマッチする。一例として、私が誰か彼/彼女の一日を訊くなら、彼/彼女は以下のように答えるかも知れない。

I woke up
I freshened myself
I had breakfast
I got to work
I worked
I had lunch
I worked again
I went home
I had diner
I watched TV
I brushed my teeth
I went to bed

コンピュータの言葉で言えば、

wake_up ();
wash ($self);
eat ("breakfast");
goto ("work")
work ();
eat ("lunch");
work ();
goto ("home");
eat ("diner");
watch_tv ();
wash ($teeth);
sleep ();

ベテランのプログラマなら以下を考えるだろう。

for $day in (qw( Mon Tue Wed Thu Fri )) {
    wake_up ();
    wash ();
    eat ("breakfast");
    :
    :

または、行動の並びをもっと強調するなら、

for $day in (qw( Mon Tue Wed Thu Fri )) {
    wake_up ();
    wash       ();
    eat          ("breakfast");
    :
    :

私の意見では、行動を実行するために取るものよりも、行動が遥に重要であることを、上記では明白に示している。私はプロセスを通し読みする時、人が仕事に就くために何の乗り物を使うか、朝食に卵があるかは気にしない。これらは行動へのパラメータである。

for $day in (qw( Mon Tue Wed Thu Fri )) {
    wake_up ();
    wash    ($day eq "Fri" ? "bath" : "shower", water_temp => "47");
    eat     (type => "breakfast", eggs => 2, toast => 4, Tea => "yes");
    travel  (target => $work, means => "train");
    :
    :

必要があれば、私は関数の引数を一目みるだけだろう。eatを読めば、何の行動が取られるのか分かる。プログラムの流れを理解するにはそれで十分である。関数への引数は、すべての引数が関数のためにあることを関数が知るために、括弧を使ってグループ化されなくてはならない。すなわち、括弧は引数をグループ化するためにあるのであって、関数を関数たらしめるためではなく、括弧が関数にではなく引数に属するのである。従って、括弧は関数を閉じるのではなく、引数蟻を閉ざすことになる。
引数は、区切りをもっと見易くするだけのため、1カンマと1スペースで区切られる。

my $width = $col[$j + $k] - $col[$j];
 
my %bf = map { $_ => -M $_ } grep { m/\.deb$/ } dirents ".";

ステートメントはセミコロンとスペースで終わる。

my $i = 1;

ループのセミコロンに対しては、

for (@a = @$ap, $u = shift @a; @a; $u = $v) {

コメントブロックのインデント
・コメントが左マージンで整列されているなら、そのまま残す。
・元のコメントがインデントされていたなら、周辺のコードにマッチするようにインデントせよ。
・コメント自体を再フォーマットするな。ラップするな。
長いクオートを外側にインデントする

if ($source_stream) {
    if (@ARGV > 0) {
      die "You may not specify any filenames when a source array is given\n";
      }
    }

if ($source_stream) {
    if (@ARGV > 0) {
      die "You may not specify any filenames" .
          "when a source array is given\n";
      }
    }

for (@methods) {
    push (@results, {
        name => $_->name,
        help => $_->help,
        });
    }

表示オプション しきい値: