以下の内容はhttps://azumakuniyuki.hatenablog.com/entry/unless-or-ifより取得しました。


unlessが読めなくなった

これはPerl Advent Calendar 2025の八日目ぐらいの記事です。 たまたまカレンダーを見たら枠が空いていたので気まぐれの思いつきで書くことにしました。 師走八日の夜十時*1の時点で今日の枠が埋まってないので飛び入り参加です。

今年は(も)YAPCへ行けなくてPerl成分が全くない一年ってのも無粋やし何か書くかと思い起こせば、昨年末から別の言語ばっかり書いててPerlはあんまり書いてなくて何もない?と思いつつも、Perlから少し離れたことで起きたことについて書きます。

掲題はちょっと大げさ・誇張表現でして、実際に読めないことはないのですが、unlessで書いてる条件を理解するのに時間がかかるようになりました。

前提

わりと前からメール関係のライブラリPerlRubyで同じものとして実装したのをOSSとして公開してて、 今年の二月にGoで書いた同じものをリリースしました。 で、PerlRubyには存在するunlessがGoには無いので、全てifで書いたわけですが、まだリリースして時間が経ってなく僕がGoに慣れてない故にコードが幼いこともあり、 効率を上げたりいろいろ細いリファクタリングでGoを触る時間が多くなりました。

原本

同じものを三言語で実装しているのですが、Goで書いたやつ が一番カッチリしてて僅かですが精度も高いので、また、最初にGoで書いて、フワッと書いても大丈夫なPerlに移植することで、例外的な値の入力やら考慮漏れも防げるので、 Go版が原本でありバグ修正や新しい機能の実装は先ずGoで書いて、動作を確認したらPerlRubyにも移植するって開発方針にしています。

unlessが読めない

そこで発覚したのがunlessが読めなくなってるです。

// Go
if neko != "nyaan" { return false }

って書いているのをPerlで書くとき、周辺の条件文がunlessで書いているから合わせようと

# Perl(1)
return 0 unless $neko eq "nyaan";

と書いて、「んん?unlessのとこって真になるべき条件やんね?」「どやったっけ?」でGoで書いた方に戻って確認して、 なんか自信が無いから

# Perl(2)
return 0 if $neko ne "nyaan";

って書いて演算子を反転してunlessにして、という面倒くさいことをやる機会が増えました。 とは言え、しばらくPerlのコードを書いているとunless 真になるべき条件で分かりやすいやん?と思うのですが、 しばらくPerlから離れていると「unlessで条件が書いてるんやけどどっちが正しい処理やったっけ?」で読めなくなってる有様です。

Rubyの方はもっと読めない

returnとかnextとか後置unlessで且つ後ろにつける条件は==とかeqとか肯定文*2に限定して使っていますが、 Perlは暫く見ているとunless文が読めるのに対して、どういうわけかRubyの方は$があるか無いか程度の違いにもかかわらず unlessで書いた式の条件が読めないと言えるぐらい理解に時間がかかるようになってて 「もうなんか読めへん理由は分からんけどunlessやめるわ💢」ってことで、少し前に90%以上のunlessをif`に書き換えました。

github.com

rb-sisimai(0) % find lib -type f -exec grep unless {} + | wc -l
      32 ←元々Perlと同じく400行ぐらいあった

RubyPerlほど量を書いていないので、単に目とアタマが慣れていなくて読めないだけかもしれませんが、大量のunlessを減らしました。

Perlの方はまだ結構あります、まぁRubyほど読めないことも無かったので。

p5-sisimai(0) % find lib -type f -exec grep unless {} + | wc -l
     442

読めなくなった理由は単に年を取ってアタマの性能が落ちたことによるものかもしれない気がしないでもない雰囲気もなくはない *3ので、 また、三言語で同じものを作っててコードの見た目も寄せているので、Perlunlessをなるべくifに変えた方が良いかなぁとは思っています。

unlessの簡潔さ

    for my $e ('from', 'by') {
        # Remove square brackets from the IP address such as "[192.0.2.25]"
        next unless defined $token->{ $e };
        next unless length  $token->{ $e };
        next unless index($token->{ $e }, '[') == 0;
        $token->{ $e } = shift Sisimai::RFC791->find($token->{ $e })->@* || '';
    }

これはメールのReceived:ヘッダーに書いているIPアドレスが妥当なものであるかどうか*4 を確認するコードの抜粋でして、ifに書き換えると次のようになります。

    for my $e ('from', 'by') {
        # Remove square brackets from the IP address such as "[192.0.2.25]"
        next if !defined $token->{ $e };
        next if length($token->{ $e }) == 0;
        next if index($token->{ $e }, '[') != 0;
        $token->{ $e } = shift Sisimai::RFC791->find($token->{ $e })->@* || '';
    }

二番目と三番目はともかく、definedに付ける否定演算子!が見にくいというか細いので、そろそろ老眼の年とか考えると!はあんまり使いたくないなぁと 思うのですが、

next if defined($token->{ $e }) != 1;
next if defined($token->{ $e }) eq "";

真偽値としての1であることは分かっていても整数値として評価するのもなんか変な感じがしますし、偽の場合は0が返ってくるわけでもない *5みたいなので、unlessで書く方が簡潔である *6と思います。

となると、一ヶ所だけunlessで残りはifってものちょっと収まりが悪い気がしますので、三つともunless で揃えたいのが人情です。

目に優しい否定文

この節は本筋とは無関係な余談であり八つ当たりみたいな内容ですが、否定演算子の話が出てきたので横道に逸れると、 !って一文字で動作が大きく変わるのに細くて目立たないので#define ⚠️ !とか#define ❗ !みたいなことが出来れば良いと思いつつも 例えば否定演算子として使うときだけ太字になるとか、絵文字は流石にちょっと...という時もあるので字面がイカれてるけど!!!って書くとか、 いろいろ思案した結果、Goではなるべく== falseと書く*7ようになりました。

人が見たら無駄に冗長なコードと見えるかもしれませんが、自分しかコミットしないコードですし、誤読したり認識に時間がかかるよりはマシってことで。

アタマの健康

一方で、unlessはそのまま残しておいて移植するときはアタマの体操的な観点で取り組めば良いのと違うの?という考えもあります。 何年後にそうなるかは知りませんし、そうならない可能性もありそうな気がしますが、機械が書くので人間は直接コードを書かなくなっても 健康診断のチラシとかに「アタマの健康のためにプログラムを書きましょう」みたいなことが書いてある世界になってるかも知れません。 現代におけるパズルとか数独みたいな位置づけです。

生きのこり

あるいは審判の日を超えたターミネーター的な世界になって捕らえた機械のプログラムを書き換える必要が出てきた際に手も足も出ない若者を押しのけて 「なんやジジィ!」と怒鳴られながらもサラサラっと書き換えて「いいか小僧ども、この時代に老いぼれを見たら『生き残り』と思え」 ってゴールデンカムイの土方さんみたいなセリフを発するためにもプログラムを読み書きする能力は維持したいものです。

松の内どころか年すら明けてないのですが寒中見舞い申し上げます。

*1:僕にしてはマァマァ夜更かし

*2: 二重否定は読めない

*3:この一文に「ない」が何個も出てくる多重否定をサラサラっと書けるのにunlessが一瞬で理解できないのは何故か

*4:ver. 3.14.15.2みたいなのをIPアドレスと認識しないように

*5:perl -e 'printf "[%s]\n", defined($p)'は[]で空文字列になった

*6:この記事を書き始めたあたりはunlessが読めないと思ってたのですが書き始めて暫く立つと目とアタマが慣れてきてマァ読める状態になりました

*7:コンパイルしたら同じやと思うけど冬休みにバイナリの比較をしようかな




以上の内容はhttps://azumakuniyuki.hatenablog.com/entry/unless-or-ifより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14