報告
東京大学理科二類、合格しました。
大して勉強しないままいつのまにか事が済んでしまって、なんだか現実感がない。
二次試験終了
センターも含めて 350~360 点くらい。受かってるといいな。
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" と出力されるのだけど、super と instance_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 ではオブジェクトのコンテキストだけでスコープが定まり、他ではブロックの保持するスコープが使われているっぽい。
センター自己採点
775/900 (国語+英語+数学IA+数学IIB+化学I+生物I+日本史B)
さすがに前期の足切りにはかからないけど、これじゃ後期は受けられないなー
マルチタッチディスプレイを作った (去年の話)
もうずいぶん前のことになりますが、昨年五月に行われた高校の文化祭でマルチタッチディスプレイを制作、展示しました。そのときの動画がようやくアップロードされた (動画の管理は生徒会が行っている) ので、のせておきます。
Laser Light Plane (LLP) という手法を用いてマルチタッチディスプレイを実現しています。くわしくはここの LLP の項を参照してください。
ただ制作したとは言うものの、僕自身は電子工作等の技術を持ち合わせていないということもあり、大部分はこの企画に一緒に携わってくれた友人の力によるものです。あと、展示で実際に動かすプログラムは自分が作るはずだったんですが、それも時間がなくて tbeta 付属のデモを動かしてるだけだったり...
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
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 で我慢。
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
要素の属性
id や class ならば以下のように書くだけでよい。
#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}
~ は埋め込む文字列に pre や textarea のような内容を勝手にインデントされると意味が変わってしまう要素を含む場合に用いる。
%div.post
%h2= post.title
~ post.content
制御構文
- @posts.each do |post|
%div.post
%h2= post.title
~ post.content
- の後に書く。end は不要。
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-aggregates の count メソッドを使う所をコメントアウトするととりあえず見れるようになった。
パーマリンクにアクセスしようとすると別のエラーが
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 とか見てみたものの原因がすぐにはつきとめられなかったので今日はここまで。
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);
}
}
}
関係ないけど、ビット演算子の優先順位が普通の加減乗除より先だと思っててはまった。