ActionScript3 で素数列挙を short coding (3)

ActionScript3 で素数列挙を short coding (2) のあと、さらに進化は続いていた。前回は184文字まで縮まっていたが、現在のところ175文字にまでなった。

「もう無理だろう」というようなところから、さらに10文字近くも縮まるなんて驚きである。例によって変遷の過程を紹介する。

たたき台 (184文字)

前回の最後に紹介したのがこのコード。実際にはインデントと改行はないが、読みやすくするためにインデントしたもので紹介していく。

package{
import flash.display.*;
public class C extends Sprite{
  function C(){
    with(graphics)
      for(beginFill(c=1),a=[];c++<1E3;)
        if(!a[d=c])
          for(drawRect(c%10*4,c/10<<2,4,4);d<1E3;)
            a[d+=c]=1
  }
}
}

if 文を消す (182文字)

psyark さんによる && を活用したコード。このあたりから、解読が一層難しくなってくる。

インデントするとこうなる。

package{
  import flash.display.*;
  public class C extends Sprite{
    function C(c=1){
      with(graphics)
        for(a=[];c++<1E3;)
          for(d=c;!a[c]&&d<1E3;drawRect(c%10*4,c/10<<2,4,4))
            beginFill(a[d+=c]=1)
    }
  }
}

使ってるテクニック

  • if の条件を2つ目の for 文の条件判定に && で突っ込むことで、if を不要にした
  • beginFillfor 文の中にもっていくことで、, を省けた。c=1 は前回登場したデフォルト引数にするテクニックでコンストラクタに移動した。
  • drawRect が何度も呼ばれることをいとわない

1重の for 文になった (181文字)

o8que さんによる for が2重から1重になってしまったコード。もはや解読は不能に近い。

インデントするとこうなる。

package{
import flash.display.*;
public class C extends Sprite{
  function C(){
    with(graphics)
      for(d=c=2, a=[];
        a[c] || d>2E3 ?
          (d=++c)<1E3 :
          !drawRect(c%10*4,c/10<<2,4,4);
        )
        beginFill(a[d+=c]=1)
  }
}
}

使ってるテクニックアルゴリズム解読

  • 2つの for を2通りに使うために、三項演算子を使って分岐している
    • c が素数で d が 2E3*1より小さいとき
      • beginFill を呼ぶ
      • d を c だけ増やす
      • a[d] にフラグを立る
      • drawRect する。
    • c が素数で d 2E3 を超えたとき、または、c が素数ではないとき
      • d=++c で c をインクリメントして d に代入する

ケチくさく1文字減らす (180文字)

わたし がo8queさんのコードに驚嘆しつつ、分からないなりに1文字減らすことに成功したヤツ。

インデントするとこうなる。

package{
import flash.display.*;
public class C extends Sprite{
  function C(){
    with(graphics)
      for(a=[d=c=2];
          a[c] || d>2E3 ?
              (d=++c)<1E3 :
              !drawRect(c%10*4,c/10<<2,4,4);
          )
        beginFill(a[d+=c]=1)
  }
}
}

使ってるテクニック

  • d=c=2,a=[]a=[d=c=2] にした。実は配列 a はインデックス 2 以降しか利用しないので、インデックス 0 の場所に何が入っていようが関係ない。

括弧を不要に (178文字)

9re さんによる、三項演算子を for の外に出したコード。少し読みやすくなった気がする。

インデントするとこうなる。

package{
import flash.display.*;
public class C extends Sprite{
  function C(){
    with(graphics)
      for(a=[d=c=2];c<1E3;beginFill(a[d+=c]=1))
        a[c]||d>2E3?
            d=++c:
            drawRect(c%10*4,c/10<<2,4,4)
  }
}
}

使ってるテクニック

  • (d=++c)<1E3 の括弧が無駄だったので、d=++cfor から出して、三項演算子の中に突っ込んだ。

FlexSprite!! (176文字)

このあたりから、コードの改造ではなく別の観点からのショートコード化の研究が行われる。今後、ActionScript でのショートコーディングするときには定型として使えそうである。

口火を切ったのは uwi さん。

インデントするとこうなる。

package{
import mx.core.*;
public class C extends FlexSprite{
  function C(){
    with(graphics)
      for(a=[d=c=2];c<1E3;beginFill(a[d+=c]=1))
        a[c]||d>2E3?d=++c:drawRect(c%10*4,c/10<<2,4,4)
  }
}
}

使ってるテクニック

  • Sprite ではなく FlexSprite を用いている。クラス名は長くなったが、名前空間が flash.display から mx.core になったので、トータルでは2文字減っている。

import の位置を変える (175文字)

言われてみればなんで気づかなかったんだろう、という修正で1文字短縮。Nicolas さんによる import の位置を変更する修正。

インデントするとこうなる。

package{
public class C extends FlexSprite{
  function C(){
    with(graphics)
      for(a=[d=c=2];c<1E3;beginFill(a[d+=c]=1))
        a[c]||d>2E3?d=++c:drawRect(c%10*4,c/10<<2,4,4)
  }
}
import mx.core.*
}

使ってるテクニック

  • import 文を public class C の手前ではなく、最後に持ってくることで、import 文の末尾 ; を省略できる…!

まとめ

さすがにそろそろ限界か。それとも常識を覆す何かが出てくるのか…。

一昔前に、ブログなどでショートコーディングの話を見たときには「難しそう」と思ったのだけど、当事者になってみると無駄な努力に全力で取り組むのは楽しいものである。また、wonderfl という共通のプラットフォーム上で記録が塗り替えられていく様子を見れるのがよかった。

今回の企画が始まって、改めて Short Coding ~職人達の技法~ を読み直してみてるが、前にざっと見たときよりも楽しく読めている。

Short Coding ~職人達の技法~

Short Coding ~職人達の技法~

  • 作者: Ozy
  • 出版社/メーカー: 毎日コミュニケーションズ
  • 発売日: 2007-08-09
  • メディア: 単行本(ソフトカバー)
  • Amazon のレビューを見る

*1: 1E3でいいと思うが…