[微ネタ] unless 修飾子 in D | パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください

パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください

開発の解決方法や新しい手法の情報を、パークのエンジニアが提供します。パークのエンジニアが必要な場合は、ぜひお気軽にお問い合わせ下さい。 株式会社パーク:http://www.pa-rk.co.jp/

ちかです
前回に引き続き D 言語ネタです。小ネタ未満の微ネタです。

Perl や Ruby にはステートメント修飾子なる制御構文があります。
たとえば、 Ruby の次のコードは x が偶数でないときだけ標準出力に x の値を出力します。

puts x unless x.even?

もし D 言語で似たような機能を実現するとしたらどうするか考えてみました。

import std.stdio;
import std.datetime;

unittest
{
    auto i = 0;
    i++.unless(true);  assert(i == 0);
    i++.unless(false); assert(i == 1);
}
void unless(lazy void expression, bool condition)
{
    condition || expression;
}

void main()
{
    auto x = 10;
    writeln(x, " is not even.").unless(x % 2 == 0);
    writeln(x, " is not odd.").unless(x % 2 != 0);

    auto writeObsoleteSlogan = () => writeln("TIMTOWTDI");
    (write("Hello, "), writeObsoleteSlogan())
        .unless(Clock.currTime().year >= 2000);
    (write("Goodbye, "), writeObsoleteSlogan())
        .unless(Clock.currTime().year < 2000);
}

出力 (公式サイト で 2014 年 12 月 7 日に動作確認)

10 is not odd.
Goodbye, TIMTOWTDI

20 行以上のコードをブログに貼ったところで 読んでもらえないことは分かってます。

でもチラ見してみて! 気持ち悪いでしょ!
writeln(x, " is not even.").unless(x % 2 == 0) はまだしも
i++.unless(true) とかエグい。

肝心の unless 関数の本体は condition || expression です。
D 言語では && 演算子と || 演算子の右辺は特別に void 型を返す式も許可されており、それぞれ ifunless の役割を担っています。
慣れないと読みにくいので実際に使われているかどうかは知りませんが...

それから D 言語では、

  • 仮引数を lazy 修飾すると、実引数として渡された式を遅延評価します。
  • 関数呼び出しを第 1 引数のメソッド呼び出しのような形式で記述できます (UFCS (Uniform Function Call Syntax))。

という機能があるため、 《第 1 引数: 遅延評価される式》.《関数名》(《第 2 引数: 条件》) という記述ができてしまいます。

同じ要領で until 修飾子や rescue 修飾子みたいなこともできます。

unittest
{
    auto i = 0, sum = 0;
    (i++, sum += i).until(i == 5);
    assert(i == 5);
    assert(sum == 15);
    (sum += i).until(++i == 10);
    assert(i == 10);
    assert(sum == 45);
}
void until(lazy void expression, lazy bool condition)
{
    while (!condition) expression;
}
unittest
{
    void throws() { throw new Exception("thrown"); }
    void nothrows() nothrow { }    auto i = 0;
    nothrows().rescue(i++);        assert(i == 0);
    throws().rescue(i++);          assert(i == 1);
    nothrows().rescue!(ex => i++); assert(i == 1);
    throws().rescue!(ex => i++);   assert(i == 2);
}
void rescue(lazy void expression, lazy void onexception)
{
    try { expression; }
    catch (Exception) { onexception; }
}
void rescue(alias onexception)(lazy void expression)
{
    try { expression; }
    catch (Exception ex) { onexception(ex); }
}

この記事はあくまでネタです。
言語機能をちょっと試してみただけですので
良い子はマネしないでね!

p.s.
上の unlessuntil は条件式として bool 型しか受け付けていませんが、
他の型も真偽値として扱えるので (型パラメーターを取るなりして) 受け付けた方がいいですね。