アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

PerlとC++にしかできないような気がした十五の夜

こんにちは!、chibaです。 早くも書くネタが無いのですが、どうしたら良いんでしょうか。
今年の私のテーマは「挑戦」。 先日、そんな私にぴったりな、盗んだバイクで走り出したくなる挑戦をみつけました。

  • PerlとC++にしかできないような気がするアノ機能(挑戦者募集中)
  • #!/usr/bin/perl
    # PerlC++は世界一。
    
    srand(time);
    
    my $a = 0;
    my $b = 0;
    for(my $i = 0; $i < 1000; $i++) {
        (rand(2)<1 ? $a : $b) += 1;
    }
    print "$a, $b\n"
    

    これをPerlや、C++以外の言語で書けるのか、と。
    私は、LISP野郎なのでLISPで回答するとして考えましたが「実行時に変数の名前によって任意の変数にアクセスし、その変数に値を代入できるか」という風にお題を解釈しました。 PerlC++の場合は、?:にそういう機能があるのでしょう。 Common Lispだとどうなるかというと、「実行時に変数の名前によって任意の変数にアクセス」というのは、シンボル名による変数へのアクセスで可能なので、上をCommon Lispに訳すと

    (let ((a 0) (b 0))
      (declare (special a b))
      (dotimes (i 1000)
        (incf (symbol-value
               (if (< (random 2) 1) 'a 'b))))
      (format t "~A, ~A~%" a b))
    ;⇒ NIL
    ----------
    499, 501
    

    こんな感じに書くことになるのかなあと思いました。
    …というようなことをtaharaさんと昼食時に話していたのですが、taharaさん曰くifにsetfが付いてれば可能なんじゃないかとのこと。 なるほど、確かにそうです。
    setfというのは代入する構文のマクロなのですが、ユーザーが色々カスタマイズできます。 実行時に変数名でアクセス→代入というのではなく、そういう構文をマクロで定義するわけですね。
    ユーザーがifにsetfを書けば良いのですが、これが標準で付いている処理系をどっかでみたなーと思ったので調べてみるとCLISPがそうでした。 CLISPだと標準で

    (let ((a 0) (b 0))
      (dotimes (i 1000)
        (incf (if (< (random 2) 1) a b)))
      (format t "~A, ~A~%" a b))
    ;⇒ NIL
    ----------
    513, 487
    

    と書けます。自分はPerlは書けないのですが、+=をカスタマイズすることによって

    for(my $i = 0; $i < 1000; $i++) {
        if (rand(2) < 1) {
          $a
        } else {
          $b
        } += 1;
    }
    

    のようにも書けるようにユーザーが勝手に定義できる、という感じでしょうか。
    こんなことをしているとすぐカオスになりそうですが、LISPに構文は無いようなものなので特に混乱もなかったりします。LISPは単純で良いですね■