2010-03-11 posted at 11:37am

報告

東京大学理科二類、合格しました。

大して勉強しないままいつのまにか事が済んでしまって、なんだか現実感がない。

filed in

2010-02-27 posted at 4:40pm

二次試験終了

センターも含めて 350~360 点くらい。受かってるといいな。

filed in

2010-02-04 posted at 10:57pm

instance_eval した時 Ruby 1.8 と Ruby 1.9 で定数の見え方が違った

環境

% ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
% ruby1.9 -v
ruby 1.9.1p376 (2009-12-07 revision 26041) [i386-darwin9]

例えばこんなコードを実行すると Ruby 1.8 では "Bar" 、Ruby 1.9 では "Foo" が出力される。

class Foo
  CONST = 'Foo'
  def exec(&block)
    instance_eval(&block)
  end
end

class Bar < Foo
  CONST = 'Bar'
  new.exec { p CONST }
end

このコードの場合だと Ruby 1.8 だと "X" が出力されるが Ruby 1.9 では X::Const が見えず NameError になる。

class Foo
  def exec(&block)
    instance_eval(&block)
  end
end

module X
  CONST = 'X'
  class Bar < Foo
    new.exec { p CONST }
  end
end

Sinatra::Base を継承したクラスを書いてて (要は atomos なんだけど) before フィルターからそのクラスと同一モジュール下にあるクラスを参照しようとしてこれではまった。

あとこんなのも。

class Foo
  CONST = 'Foo'
  def exec(&block)
    instance_eval(&block)
  end
end

class Bar < Foo
  CONST = 'Bar'
  def exec(&block)
    super
    instance_eval(&block)
  end
  new.exec { p CONST }
end

Ruby 1.9 でこれを実行すると "Foo", "Foo" と出力されるのだけど、superinstance_eval(&block) の順序を逆にすると今度は実行結果が "Bar", "Bar" と変わってしまう (Ruby 1.8 ではいずれも "Bar", "Bar") 。さすがにキモい気がする。

追記

class Foo
  CONST = 'Foo'
  def exec1(&block)
    instance_eval(&block)
  end
  def exec2(&block)
    yield
  end
end

class Bar < Foo
  CONST = 'Bar'
  new.exec1 { p CONST } #=> "Bar" (1.8), "Foo" (1.9)
  new.exec2 { p CONST } #=> "Bar" (1.8, 1.9)
  new.instance_eval { p CONST } #=> "Bar" (1.8, 1.9)
end

Bar.new.exec1 { p CONST } #=> NameError (1.8), "Foo" (1.9)
Bar.new.exec2 { p CONST } #=> NameError (1.8, 1.9)
Bar.new.instance_eval { p CONST } #=> NameError (1.8), "Bar" (1.9)

Ruby 1.9 の instance_eval ではオブジェクトのコンテキストだけでスコープが定まり、他ではブロックの保持するスコープが使われているっぽい。

filed in

2010-01-18 posted at 10:21am

センター自己採点

775/900 (国語+英語+数学IA+数学IIB+化学I+生物I+日本史B)

さすがに前期の足切りにはかからないけど、これじゃ後期は受けられないなー

filed in

2010-01-15 posted at 3:15pm

マルチタッチディスプレイを作った (去年の話)

もうずいぶん前のことになりますが、昨年五月に行われた高校の文化祭でマルチタッチディスプレイを制作、展示しました。そのときの動画がようやくアップロードされた (動画の管理は生徒会が行っている) ので、のせておきます。

Laser Light Plane (LLP) という手法を用いてマルチタッチディスプレイを実現しています。くわしくはここの LLP の項を参照してください。

ただ制作したとは言うものの、僕自身は電子工作等の技術を持ち合わせていないということもあり、大部分はこの企画に一緒に携わってくれた友人の力によるものです。あと、展示で実際に動かすプログラムは自分が作るはずだったんですが、それも時間がなくて tbeta 付属のデモを動かしてるだけだったり...

filed in

2010-01-14 posted at 3:26pm

tig.rb のアップデート in Mac OSX

証明書を入れないといけないけど、macports でそれらしいのが見つからなかったので直接入れた。

% wget http://ftp.de.debian.org/debian/pool/main/c/ca-certificates/ca-certificates_20070303.tar.gz
% tar zxvf ca-certificates_20070303.tar.gz
% cd ca-certificates-20070303/mozilla/
% make
% for i in *.crt; do
for> sudo cp $i /System/Library/OpenSSL/certs/`openssl x509 -hash -noout -in $i`.0
for> done

filed in

2010-01-11 posted at 5:02pm

heroku いいよ

Heroku というのは Ruby アプリケーションを運用するプラットホームを提供するサービスで、Rack を土台としたものなら何でも動かせる。GAE が GAE 固有の事情をいろいろ考慮しなくてはならないのと比べると、こっちはだいぶ楽。あと、git push でデプロイできたりとか。

もちろんそれなりに制約があるし、そもそも無料プランだとストレージとして 5M しか与えられなかったりするけど、外部リソースとして利用できるサービスがたくさんあることを考えれば、小さなアプリを運用する場としては十分だと思う。

ただ一つ (自分にとって) 残念なのがクレジットカード番号を使って verify しないと独自ドメイン持ち込みなどの機能が使えないこと。Heroku | Accounts and Billing によれば

Why do I have to provide my billing details to use add-ons?

Some features, most notably outgoing email and custom domains, carry a potential for abuse. As a business, we need to be able reliably identify and contact our users in the event of an issue. We have found that a credit card on file provides the most reliable way of obtaining verified contact information.

悪用されないためということだそうだ。

今 18 歳の自分がクレジットカードを持つようになるのはいつのことになるんだろうか。大学生になれば別に持てなくはないみたいだけど。

仕方ないので今は Xrea Plus で我慢。

filed in

2010-01-08 posted at 3:13pm

Haml 入門

始め Haml の紹介記事を読んだ時、要素の属性の書き方がいけてない (HTML で普通に書くより長いし、あと http-equiv があるときとか) あたりが気に入らなかったのだけど、Haml 2.2 からは HTML と同様に書けると知った (via Haml レシピブック 11の技 - ursmの日記) ので試しに atomos のテンプレートを Haml を使うよう切り替えてみた。

最低限の使い方をメモ。

基本

%h1 What is the Best Template Engine on Ruby?
%p
  The answer is
  %strong Haml

<h1>What is the Best Template Engine on Ruby?</h1>
<p>
  The answer is
  <strong>Haml</strong>
</p>

%{タグ名} で表された要素に対し、内容を同じ行に続けて書くか、次の行からインデントを一つ深くして書くことで包含関係を表現する。

なお、以下のようにこれら二つを同時に用いる書き方は許されていない。

%p The answer is
  %strong Haml

要素の属性

idclass ならば以下のように書くだけでよい。

#header
  %h1#title Evolute
  %p.description Just Another Weblog

要素名が省略された場合は div と見なされる。

それ以外の属性は Ruby のハッシュリテラルを用いて表す。

%html{:xmlns => "http://www.w3.org/1999/xhtml"}

{} の中身は Hash を返す Ruby コードならなんでもよく、

%html{html_attrs('ja')}

とも書ける。( html_attrs{:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => 'ja', :lang => 'ja'} のような値を返す Haml::Helpers のメソッド)

また、上で書いた HTML 風の記法ではこう。

%html(xmlns="http://www.w3.org/1999/xhtml")

属性値の部分にはローカル変数またはインスタンス変数か、引用符で囲んだ文字列が使える。引用符の中では式展開が利用可能。

%a(href=@url)
%a(href="/post/#{@post.id}")

これまで挙げたものを併用することもできる。

%a.permalink(rel="bookmark"){:href => entry.permalink }

Ruby コードの実行結果の埋め込み

%div.comment
  = @comment.content
  %p.meta
    by: #{@comment.username}

= の後に Ruby コードを書くか、式展開を使う。埋め込んだ文字列は :escape_html オプションが設定されていれば HTML エスケープされる。

HTML エスケープするかどうかは & または != の前に置くか、式展開する行の行頭に置いて明示的に指定できる。& なら :escape_html に関わらずエスケープされ、! ならされない。

%div.comment
  &= @comment.content
  %p.meta
    & by: #{@comment.username}

~ は埋め込む文字列に pretextarea のような内容を勝手にインデントされると意味が変わってしまう要素を含む場合に用いる。

%div.post
  %h2= post.title
  ~ post.content

制御構文

- @posts.each do |post|
  %div.post
    %h2= post.title
    ~ post.content

- の後に書く。end は不要。

filed in

2010-01-05 posted at 11:34am

atomos を GAE で動かそうと試みる

まず

$ sudo gem install google-appengine

config.ru を書きかえたり、Gemfile を書いてから

$ dev_appserver.rb .

するとこんなエラーが出た。

cannot link Java class com.google.appengine.jruby.AppVersionUploadDelegate, probable missing dependency: Bad version number in .class file

ここのコメントに従い Java Preferences.app で Java SE 6 の優先順位を上げてやるとサーバーは立ち上がるようになった。

http://localhost:8080/ にアクセスすると次は

../dm-aggregates-0.10.2/lib/dm-aggregates/repository.rb:6:in `aggregate': undefined method `aggregate' for #<DataMapper::Adapters::AppEngineAdapter:0x2207d8bb> (NoMethodError)

dm-aggregatescount メソッドを使う所をコメントアウトするととりあえず見れるようになった。

パーマリンクにアクセスしようとすると別のエラーが

Application Error com/google/appengine/api/datastore/Query.java:478:in `<init>': java.lang.IllegalArgumentException: Filters of type org.jruby.RubyObject are not supported (NativeException)

調べてみると同じようなエラーに遭遇している人もいて condition に Date オブジェクトを渡しているのが問題らしい。

appengine_adapter.rb とか見てみたものの原因がすぐにはつきとめられなかったので今日はここまで。

filed in

2010-01-04 posted at 6:29pm

Accumulation of Background Statistics

このへんを参考にしながらやってみた。輝度を実数として保存するため BitmapData の API が使えず Vector に入れて強引に処理したけど AS3 はこのくらいならものともしないようで助かります。

展示なんかで通りかかった人とかを認識するにはよさそうだけど、ウェブページ上で再生するものとして使うなら単純なフレーム間差分の方がいい気がする。

package {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.media.*;
    import flash.net.*;
    import flash.text.*;
    import com.flashdynamix.utils.SWFProfiler;

    [SWF(width="320", height="240", backgroundColor="#ffffff", frameRate="30")] 

    public class Test extends Sprite {

        private const WIDTH:uint  = 320;
        private const HEIGHT:uint = 240;
        private const PIXELS:uint = WIDTH * HEIGHT;

        private var camera:Camera;
        private var video:Video;
        private var bitmap:BitmapData = new BitmapData(WIDTH, HEIGHT, false, 0);
        private var object:BitmapData = new BitmapData(WIDTH, HEIGHT, true , 0);
        private var statistics1:Vector.<Number> = new Vector.<Number>(PIXELS, true);
        private var statistics2:Vector.<Number> = new Vector.<Number>(PIXELS, true);
        private var r1:Number = 0.02;
        private var r2:Number = 0.005;
        private var count:int = 0;
        private var shader:Sprite = new Sprite();
        private var luminance:ColorMatrixFilter;
        private var erode:ConvolutionFilter;
        private var dilate:ConvolutionFilter;
        private var blur:BlurFilter;

        public function Test() {
            stage.scaleMode = "noScale";
            stage.align = "TL";
            SWFProfiler.init(stage, this);

            camera = Camera.getCamera();
            if (camera == null) return;
            camera.setMode(WIDTH, HEIGHT, 30);
            video = new Video(WIDTH, HEIGHT);
            video.attachCamera(camera);
            addChild(video);
            addChild(new Bitmap(object));

            luminance = new ColorMatrixFilter([
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0.3, 0.59, 0.11, 0, 0,
                0, 0, 0, 0, 0
            ]); 
            erode = new ConvolutionFilter(3, 3);
            erode.bias = -(0xff0 + 0xff * 7);
            erode.matrix = [
                1,  1, 1,
                1, 16, 1,
                1,  1, 1
            ];
            dilate = new ConvolutionFilter(3, 3);
            dilate.matrix = [
                1, 1, 1,
                1, 1, 1,
                1, 1, 1
            ];
            blur = new BlurFilter(2, 2);

            if (camera.muted) {
                camera.addEventListener(StatusEvent.STATUS, function(e:StatusEvent):void {
                    if (!camera.muted) start();
                });
            } else {
                start();
            }
        }

        public function start():void {
            shader.graphics.beginFill(0x000000, 0.5);
            shader.graphics.drawRect(0, 0, WIDTH, HEIGHT);
            addChild(shader);
            var tf:TextField = new TextField();
            tf.defaultTextFormat = new TextFormat("_sans", 18);
            tf.autoSize = "left";
            tf.textColor = 0xffffff;
            tf.text = "initializing...";
            tf.x = (WIDTH  - tf.width)  / 2;
            tf.y = (HEIGHT - tf.height) / 2;
            shader.addChild(tf);
            addEventListener(Event.ENTER_FRAME, initialize);
        }

        public function initialize(e:Event):void {
            var frame:Vector.<uint> = getFrame();
            var i:int = PIXELS;
            var x:Number;
            if (count < 50) {
                while (i--) statistics1[i] += frame[i] & 0xff;
            } else if (count == 50) {
                while (i--) statistics1[i] /= 50;
            } else if (count < 100) {
                while (i--) statistics2[i] += (x = (frame[i] & 0xff) - statistics1[i]) > 0 ? x : -x;
            } else {
                while (i--) statistics2[i] /= 50;
                removeChild(shader);
                removeEventListener(Event.ENTER_FRAME, initialize);
                addEventListener(Event.ENTER_FRAME, loop);
            }
            count++;
        }

        public function loop(e:Event):void {
            var frame:Vector.<uint> = getFrame();
            var amplitude:Number;
            object.lock();
            for (var i:int = 0; i < PIXELS; i++) {
                amplitude = (frame[i] & 0xff) - statistics1[i];
                amplitude = amplitude > 0 ? amplitude : -amplitude;
                if (amplitude > 1.414 * statistics2[i] + 10) {
                    statistics2[i] = (1 - r1) * statistics2[i] + r1 * amplitude; 
                    object.setPixel32(i % WIDTH, int(i / WIDTH), 0xff00ffff);
                } else {
                    statistics1[i] = (1 - r2) * statistics1[i] + r2 * (frame[i] & 0xff); 
                    statistics2[i] = (1 - r2) * statistics2[i] + r2 * amplitude; 
                    object.setPixel32(i % WIDTH, int(i / WIDTH), 0x00000000);
                }
            }
            object.applyFilter(object, object.rect, new Point(), erode);
            object.applyFilter(object, object.rect, new Point(), dilate);
            object.applyFilter(object, object.rect, new Point(), blur);
            object.unlock();
        }

        public function getFrame():Vector.<uint> {
            bitmap.draw(video);
            bitmap.applyFilter(bitmap, bitmap.rect, new Point(), luminance);
            return bitmap.getVector(bitmap.rect);
        }

    }
}

関係ないけど、ビット演算子の優先順位が普通の加減乗除より先だと思っててはまった。

filed in