読者です 読者をやめる 読者になる 読者になる

kwsktr's study log

kwsktr のおべんきょログ

コーディングスタイルだけは一人前を目指す試み :: (1)

Perl ベストプラクティスを手にいれたので、勉強がてら書いていきます。



当エントリは『Perlベストプラクティス』の「劣化」でしかないので、興味が湧いたら本書を入手して読まれることをお勧め。高いけれども。
まずは、Google Booksでプレビューしてみても良いのかもです。
Perlベストプラクティス - ダミアンコンウェイ - Google ブックス


こういう本を職場で買って回し読みとか読書会する文化があるといいよね。

1章 ベストプラクティス

1.1 3つの目標

よいコーディングスタイルがあれば、ソフトウェアプロジェクトのコストを削減出来る。

  • 堅牢性
    • コード内のバグが減る
  • 効率性
    • コードが早く動く
  • 保守性
    • コードが読みやすくなる
1.3 新しい習慣

読んだ部分からでも意識的に実践してみよう。

2章 コードのレイアウト

2.1 かっこ

かっこと中かっこにK&Rスタイルを使用する

my @names = (
    'Damian',
    'Matthew',
    'Conway',
);

for my $name (@names) {
    for my $word ( anagrams_of(lc_name) ) {
        print "$word\n";
    }
}
  • ブロックを制御する構造の末尾に、開きかっこを配置
  • ブロックの内容を次の行から始める
  • その際には1レベルのインデントを付ける
  • ブロックの閉じかっこは、新しい行に配置する
  • 閉じかっこのインデントレベルは開きかっこと統一する
2.2 キーワード

制御キーワードを後続の開きかっこから分離する

for my $result (@results) {
    print_sep();
    print $result;
}

while ($min < $max) {
    my $try = ($max - $min) / 2;
    if ($value[$try] < $target) {
        $max = $try;
    }
    else {
        $min = $try;
    }
}
2.3 サブルーチンと変数

サブルーチンまたは変数の名前を後続の開きかっこから分離しない

my @candidates = get_candidates($marker);

CANDIDATE:
for my $i (0..$#candidates) {
    next CANDIDATE if open_region($i);

    $candidates[$i]
        = $incumbent { $candidates[$i]{region} };
}
2.4 組み込み関数

perlfunc - Perl 組み込み関数 - perldoc.jp

組み込み関数や「名誉」組み込み関数のために不要なかっこを使用しない

組み込み関数は、優先度を強制する必要がなければ、かっこなしで呼び出しても構わない。
組み込み関数でかっこを使用する必要があると感じた場合は、サブルーチンのルールに従い、組み込み関数の名前と開きかっことの間にスペースを入れない。

2.5 キーとインデックス

複合キーやインデックスを周囲のかっこから分離する

$candidates[$i] = $incumbent { $candidates[$i]{ get_region() } };

インデックスが単なる定数またはスカラー変数である場合を除く。

2.6 演算子

ホワイトスペースを使用して、2項演算子オペランドから際立たせる

my $displacement
    = $initial_velocity * $time + 0.5 * $acceleration * $time**2;  # (1)

my $velocity
    = $initial_velocity + ($acceleration * ($time + $date_time));  # (2)

my $spring_force = !$hyperextended ? -$spring_constant * $extension : 0;  # (3)

my $tan_theta = sin $theta / cos $theta;  # (4)
  1. 行が余分に必要になるとしても、2項演算子が呼吸できる空間を与える
  2. 優先度を強調する(変更する)ためにかっこも使用する場合は、常にスペース1つで十分
  3. 単項演算子は常にオペランドにくっつけておく
  4. 名前付きの単項演算子は、組み込み関数のように扱い、オペランドから適度なスペースに保つ
2.7 セミコロン

すべての文の後ろにセミコロンを配置する

2.8 コンマ

複数行にまたがるリストの各値のあとにコンマを配置する

my @dwarves = {
    'Happy',
    'Sleepy',
    'Dopey',
    'Sneezy',
    'Grumpy',
    'Bashful',
    'Doc',
}
2.9 行の長さ

78列の行を使用する

2.10 インデント

4列のインデントレベルと使用する

2.11 タブ

インデントにはタブではなくてスペースを使用する

2.12 ブロック

2つの文を1行にまとめない

my @clean_words
    = map {
        my $word = $_;
        $word =~ s/$EXPLETIVE/[DELETED]/gxms;
        $word;
    } @raw_words:

map ブロックや grep ブロックにも当てはまる。

2.13 チャンク化

コードを段落に分ける

# 認識されている配列を処理する
sub addarray_internal {
    my ($var_name, $needs_quotemata) = @_;
    
    # 元の配列をキャッシュする;
    $raw .= $var_name;
    
    # 必要に応じて、メタ囲繞されたコードを作成する
    my $quotemeta = $needs_quotemata ? q{map {quotemeta $_} } : $EMPTY_STR;
    
    # 変数の要素を展開し、それらの論理和をとる
    my $perl5pat = qq{(??{join q{|}, $quotemeta \@{$var_name}})};
    
    # 必要に応じて、デバッグコードを挿入する
    my $type = $quotemeta ? 'literal' : 'pattern';
    debug_now("Adding $var_name (as $type)");
    add_debug_mesg("Trying $var_name (as $type)");

    return $perl5pat;
}

2.14 Else

}とelseを同じ行に並べない

}
else {

と、書く。

2.15 縦の整列

対の項目は縦に整列される

my @months = qw(
    January February Marth
    April   May      June
    Jully   August   September
    October November December
);

my %expansion_of = (
    q{it's}    => q{it is},
    q{we're}   => q{we are},
    q{didn't}  => q{did not},
    q{must've} => q{must have},
    q{I'll}    => q{I will},
);

$name   = standardize_name($name);
$age    = time - $birth_date;
$status = 'active';

$ident{ name   } = standardize_name($name);
$ident{ age    } = time - $birth_date;
$ident{ status } = 'active';

2.16 長い文の分割

長い式は演算子の前で分割する

push @steps, $steps[-1]
             + $radial_velocity * $elapsed_time
             + $orbital_velocity * ($phase + $phase_shift)
             - $DRAG_COEFF * $altitude
             ;

継続行は、それらが属している式の1列目でインデントすべき。

2.17 非終端式

文の途中にある長い式を抽出する

分割する長い式が文の途中にある場合には、その式を変数代入として独立させたほうがよい。

2.18 優先度による分割

長い式は常に最も優先度の低い演算子で分割する

優先度が最も低いと言えない演算子で分割しなければならない場合には、分割した行に式の先頭よりも1レベル深いインデントを付ける。

2.19 代入

長い代入式は代入演算子の前で分割する

$predicted_val
    = ($minimum + $maximamu) / 2
      + $predicted_change * max($fudge_factor, $local_epsilon);
2.20 3項演算子

従属した3項演算子は列でフォーマットする

my $salute = $name eq $EMPTY_STR                       ? 'Customer'
           ; $name =~ m/\A((?:Sir|Dame) \s + \S+) /xms ? $1
           : $name =~ m/(.*), \s+ Ph[.]?D \z      /xms ? "Dr $1"
           :                                             $name
           ;
2.21 リスト

長いリストはかっこで囲む

my @months = qw(
    January February Marth
    April   May      June
    Jully   August   September
    October November December
);

for my $item (@requested_items) {
    push @items, {
        "A brand new $item",
        "A fully refurbished $item",
        "A ratty old $item",
    };
}

print (
    'Processing ',
    scalar(@items),
    ' items at ',
    time,
    "\n",
);
2.22 自動レイアウト

選択したレイアウトスタイルを機械的に適用する

The Perltidy Home Page

 -l=78   # Max line width is 78 cols
 -i=4    # Indent level is 4 cols
 -ci=4   # Continuation indent is 4 cols
 -st     # Output to STDOUT
 -se     # Errors to STDERR
 -vt=2   # Maximal vertical tightness
 -cti=0  # No extra indentation for closing brackets
 -pt=1   # Medium parenthesis tightness
 -bt=1   # Medium brace tightness
 -sbt=1  # Medium square bracket tightness
 -bbt=1  # Medium block brace tightness
 -nsfs   # No space before semicolons
 -nolq   # Don't outdent long quoted strings
 -wbb="% + - * / x != == >= <= =~ !~ < > | & **= += *= &= <<= &&= -=
       /= |= >>= ||= .= %= ^= x="
         # Break before all operators