ToDo:
2005-10-01の日記で、java.lang.Object#getClass()の返り値の型がClass<? extends Object>と宣言されているにも関わらず、java.lang.Objectのサブクラスの静的な型に応じて返り値の型が変わるのかわからないというような事を書いたが、今日、ふと思い立ってJLS 3rdを眺めていたら、JLSの4.3.2「The Class Object」に次のような記述があった(強調した部分が返り値の型に関する記述)。java.lang.Object#getClass()の返り値の型はコンパイラによって特別に、Class<? extends T>(TはgetClass()の呼び出し対象の式の静的な型)とみなすように扱われているということだ。
The method getClass returns the Class object that represents the class of the object. A Class object exists for each reference type. It can be used, for example, to discover the fully qualified name of a class, its members, its immediate superclass, and any interfaces that it implements. A class method that is declared synchronized (§8.4.3.6) synchronizes on the lock associated with the Class object of the class. The method Object.getClass() must be treated specially by a Java compiler. The type of a method invocation e.getClass(), where the expression e has the static type T, is Class<? extends |T|>.
しっかし、利便性のためとは言え、一つのメソッド定義だけをコンパイラが特別扱いするのはびみょーな気がする。こういう問題は、SelfTypeがあればもうちょっとまともに解決できたのかも。もしも、Javaの構文を拡張するなら、こんな感じか?
public native java.lang.Object<? extends like this> getClass();
2006/10/28のエントリで、GenericなVisitorと題して、VisitorをGeneric化する手法を紹介したんだが、研究のプログラムに使ってみて、あれは少なくともIDEの補完機能を活用するという点においてはかなり使いにくいことがわかってきた。というわけで、改良版というか、本来ならこうしとくべきだったという方法を紹介しておく。まず、以前紹介した手法では、Visitorはvisitメソッドの引数の型だけをパラメータ化して、戻り値の型はメソッドでパラメータ化していたが、そうではなく、戻り値の型もVisitorでパラメータ化する。具体的には、こんな感じ。
interface Visitor<R, C> {
R visit(AddExp e, C c);
R visit(SubExp e, C c);
R visit(MulExp e, C c);
R visit(DivExp e, C c);
R visit(IntExp e, C c);
R visit(VarExp e, C c);
R visit(SetExp e, C c);
R visit(BlkExp e, C C);
}
他の部分は、以前とほとんど同じだが、Visitorの型パラメータが増えているので、それに関する部分だけ変更する必要がある。
以前の手法と比べた場合の欠点として、visitメソッドごとに異なる型の返り値を持つことができないということがあるが、考えてみるとそういうケースはあんまり無かったし、大した欠点にはならない気がする。
import java.util.*;
public class Main { public static void main(String[] args){ Exp exp = new BlkExp( new SetExp("x", new IntExp(2)), new SetExp("y", new IntExp(3)), new SetExp("z", new IntExp(4)), new MulExp(new AddExp(new VarExp("x"), new VarExp("y")), new VarExp("z")) ); Integer result = exp.accept(new Calculator(), new Env()); System.out.printf("{ x = 2; y = 3; z = 4; (x + y) * z } = %d%n", result); } }
class Calculator implements Visitor<Integer, Env> { public Integer visit(AddExp e, Env env){ return e.left.accept(this, env) + e.right.accept(this, env); } public Integer visit(SubExp e, Env env){ return e.left.accept(this, env) - e.right.accept(this, env); } public Integer visit(MulExp e, Env env){ return e.left.accept(this, env) * e.right.accept(this, env); } public Integer visit(DivExp e, Env env){ return e.left.accept(this, env) / e.right.accept(this, env); } public Integer visit(IntExp e, Env env){ return e.value; } public Integer visit(VarExp e, Env env){ Integer value = env.lookup(e.name); if(value == null) throw new RuntimeException("undefined variable '" + e.name + "'"); return value; } public Integer visit(SetExp e, Env env){ Integer value = e.exp.accept(this, env); env.set(e.name, value); return value; } public Integer visit(BlkExp e, Env env){ Integer value = 0; for(Exp element : e.exps) value = element.accept(this, env); return value; } }
class Env { private Map<String, Integer> table = new HashMap<String, Integer>(); Integer lookup(String name){ return table.get(name); } void set(String name, Integer value) { table.put(name, value); } }
abstract class Exp { abstract <R, C> R accept(Visitor<R, C> v, C c); }
abstract class BinExp extends Exp { final Exp left, right; BinExp(Exp left, Exp right) { this.left = left; this.right = right; } }
class AddExp extends BinExp { AddExp(Exp left, Exp right){ super(left, right); } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class SubExp extends BinExp { SubExp(Exp left, Exp right){ super(left, right); } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class MulExp extends BinExp { MulExp(Exp left, Exp right){ super(left, right); } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class DivExp extends BinExp { DivExp(Exp left, Exp right){ super(left, right); } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class IntExp extends Exp { final int value; IntExp(int value){ this.value = value; } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class VarExp extends Exp { final String name; VarExp(String name){ this.name = name; } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class SetExp extends Exp { final String name; final Exp exp; SetExp(String name, Exp exp){ this.name = name; this.exp = exp; } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
class BlkExp extends Exp { final List<? extends Exp> exps; BlkExp(Exp... exps){ this.exps = Arrays.asList(exps); } <R, C> R accept(Visitor<R, C> v, C c){ return v.visit(this, c); } }
Matzにっき経由。MS版Ruby処理系「IronRuby」が発表されたそうな。すげえ。
行って来た。soutaroさんと16:00につくばセンターで待ち合わせていたのだが、実はGaucheNightは19:30開始。今行くと早過ぎるけど、大学戻っても仕方無いということでとりあえず秋葉原まで。会場で飯が食べられるかどうかわからなかったので吉野屋で軽く飯を食べてから会場へ。
会場に到着したのは18:30頃で、まだ開始1時間前なのにもうそれなりに人が並んでいて、ちょっとびびってしまった。再入場不可ということで、ちょっと躊躇したが、どうせ他に時間をつぶす所も無いしということでさっさと入場してしまうことに。PEGの論文読んだりErlangのSoftTypingの論文がアレだという話をsoutaroさんから聞いたりして時間をつぶした。
で、19:30になっていよいよ開始。第1部?は、Scheme/Common Lispについて、出演者(黒田さん、川合史朗さん、子飼弾さん、小野さん、山下さん、まつもとさん(Skype出演))が座談会のような形で語るというもの。出演者のキャラクター性が出ていて楽しめたけど、話が途中からグダグダになっていた感が。ツッコミ所も色々あったし。こういうとき、プロシン方式だと色々嬉しいんだけど、それを期待するのは酷だろうか。あと、通信環境の問題か、まつもとさんとSkypeがうまくつながらず、結局ほとんどまつもとさん抜きで話が進んでいたのが残念。
で、座談会が終わった後に若干の休憩をはさんで第2部。こっちの方は、LL Nightみたいな感じ、自分の作ったソフトウェアについて5分間で語るというもの。一番印象に残ったのは、最初の方であった、Gauce rfb(うろ覚え)。こういう風な技術の無駄使い(褒め言葉)は実に素晴らしいと思う。つか、ここで魔法言語リリカルLispのスクリーンショットを見ることになるとは思わんかった。あと、植山さんのPEGパーザコンビネータの話は個人的にもうちょっと聞きたかった。
GaucheNight終了後、予想よりも若干早く終わったので、植山さんに挨拶。以前LingrのGaucheルームでちょっと議論をしたことがあって、一度実際に会ってみたいと思っていたのだった。終バス(終電)まであまり時間も無いので、ちょっとだけ話をして、さっさと会場を離脱。なんとか11:00の筑波大学行きのバスに間に合った。
何故かあちこちで流行っているFizzBuzz問題。せっかくなので、Onionでも書いてみた。Javaで書くのとほとんど変わらんけど、iの型を宣言してないとか、for文に括弧がいらないとか、ビミョーに違う。
for i = 1; i <= 100; i++ {
cond {
i % 5 == 0 && i % 3 == 0 { System::out.println("FizzBuzz"); }
i % 3 == 0 { System::out.println("Fizz"); }
i % 5 == 0 { System::out.println("Buzz"); }
else { System::out.println(i); }
}
}
_ offizielle poker regeln [http://poker-9951.joueb.com/news/online-poker-texas-hold [..]
_ jeu slots casino gratuit [http://groups.google.fr/group/tegan2503/web/play-baccarat ..]
_ spiele bank [http://groups.google.de/group/muntaha9819/web/draw-poker [..]
_ cpayscom2 online casino [<a href=http://www.bloglines.com/blog/casino-4497?id=51>ju..]
_ baccarat spielen [<a href=http://blogs.iloha.net/casino7174/entries/19545.sh..]
_ lerpkrty [<h1>si ripete il passo 2 e il passo 3 e si vede il proprio..]
_ bestkasinonet [<H1>Stellt sich im praktischen Spiel heraus, dass von eine..]
_ grossman [<h1>Ci sono forse limiti al modo in cui potreste spenderli..]
_ numoewll [<h1>Sono in verit� i giocatori italiani stessi che ne hanno..]
最近、研究に使うプログラムを作っていて、Javaで関数型プログラミングっぽい事をするライブラリが欲しくなったことが何度もあり、自分で作ってみたくなった。2005/10/03のエントリで、似たような事を書いていたが、まだ色々機能が足りなく、実用に使えるようなものにはなっていなかった。今回は実際に使えるライブラリを目指して作ってみたい。
実は、同じような事を行うライブラリとして、FunctionalJというものが既に公開されているが、これは個人的にイマイチ。
まずは、関数を表す型から作っていこう。これは、基本的には
interface Function<R, A> {
R call(A arg);
}
というインタフェースさえあれば、あとはカリー化するなどしてどうとでもできるが、そういうのはさすがに実際に使いたいとは思えない。というわけで、関数の型は引数の数ごとに別に用意したいところ。ただ、普通使う関数では、せいぜい引数の数は6個が限度だと思うので、多めに見積もって8個程度インタフェースを用意すればいいか。具体的にはこんな感じ(実際にはAPIとして公開するために、各インタフェースは別ファイルに分ける必要があるが)。
package jp.gr.java_conf.mizu.flj;
interface Fn<R, A> {
R $(A arg);
}
interface Fn2<R, A1, A2> {
R $(A1 arg1, A2 arg2);
}
interface Fn3<R, A1, A2, A3> {
R $(A1 arg1, A2 arg2, A3 arg3);
}
interface Fn4<R, A1, A2, A3, A4> {
R $(A1 arg1, A2 arg2, A3 arg3, A4 arg4);
}
interface Fn5<R, A1, A2, A3, A4, A5> {
R $(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5);
}
interface Fn6<R, A1, A2, A3, A4, A5, A6> {
R $(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5, A6 arg6);
}
interface Fn7<R, A1, A2, A3, A4, A5, A6, A7> {
R $(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5, A6 arg6, A7 arg7);
}
interface Fn8<R, A1, A2, A3, A4, A5, A6, A7, A8>{
R $(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5, A6 arg6, A7 arg7, A8 arg8);
}
この辺はFunctionalJと基本的に同じだが、名前をかなり短めにしてあるのがポイント。この辺りの基本的なインタフェースは頻繁に使うだろうから、短い名前にしてもわからないということはたぶん無いだろうと思う。それよりも、これらの無名サブクラスを作ったときに記述が煩雑になる方が個人的には嫌だ。
2007/05/25追記:<>を<と>にエスケープするのを忘れていたせいで、型パラメータが消えていたので、訂正。
夕食を食べてから、研究室に集合して、斎藤君と一緒に歩くことに。歩き始めた時点でかなり日が落ちていたので、半分くらい歩いた時点で周りは真っ暗。筑波大学のループ道路は、街灯がほとんど無い箇所があるので、その辺りを通ったときには、つまずかないように足元をかなり注意して歩く必要があり、ちょっと怖かった。
いい加減に(コメント/TB)スパムがうざくなってきたので、これまでのてきとーな正規表現マッチングによるフィルタリングではなく、斎藤君のお勧め通りに、本文がASCII文字しか含まないコメントをはじくようにしてみた。
どうでもいいけど、半月くらい前から一応twitterやってます。あまり書いてませんが、一応。 http://twitter.com/kmizu
今回は、タプルをどう表現するかについて考えてみる。まず簡単な方法としては、以下のようなクラスを一つだけ用意して、三つ以上の値を扱いたい場合は、Tuple<String, Tuple<Integer, Double>>のようにして扱うというのが考えられるが、これは使いづらいので却下。
class Tuple<T1, T2> {
...
}
というわけで、関数型を表現するときと同様、値の個数に応じて、別のクラスを作ることにする。タプルも関数型と同様によく使うと考えられるので、名前も短めにしておく。
class Tp<T1, T2> {
private T1 at1;
private T2 at2;
Tp(T1 t1, T2 t2) {
at1 = t1;
at2 = t2;
}
public T1 at1() {
return at1;
}
public T2 at2() {
return at2;
}
}
class Tp3<T1, T2, T3> {
private T1 at1;
private T2 at2;
private T3 at3;
Tp(T1 t1, T2 t2, T3 t3) {
at1 = t1;
at2 = t2;
at3 = t3;
}
public T1 at1() {
return at1;
}
public T2 at2() {
return at2;
}
public T3 at3() {
return at3;
}
}
...以下同様
さて、これだけだと、タプルを構築するときに毎回型パラメータを指定して、new Tp<Integer, String>のようにしなければならずうっとおしい。そこで、タプルを構築するために、以下のような多相メソッドを集めたユーティリティクラスも用意しておく。
class TpFn {
public static <T1, T2> tp(T1 t1, T2 t2) {
return new Tp<T1, T2>(t1, t2);
}
public static <T1, T2, T3> tp(T1 t1, T2 t2, T3 t3) {
return new Tp<T1, T2, T3>(t1, t2, t3);
}
}
ユーティリティメソッドを使うときは、static importしておけば単に 以下のように呼び出せばOK。
Tp<String, Integer> t = tp("Hello", 1);
食費つけはじめてから1ヶ月たったので、残額リセットですよ。K山さんがせっかくつくばに来てくれたので、RanRanの洗礼を受けさせよう、というM田先生の提案によって、M田先生、K山さん(初RanRan)、斎藤君とRanRanへGo。K山さんの注文にinterruptしてBIG丼大盛にしたM田先生の早業に感服。それを普通に平らげたK山さんにも感服。俺はもちろんダイエット中なので、ねぎとろ野菜丼というカロリー低そうなメニューにしました。ダイエット中にRanRanに行くという選択が既に何か間違えている気がしなくも無いがキニシナイ。1日くらい大丈夫だ、たぶん。
BIG丼大盛を完食したK山さん
というわけで問題。reducelとreducerを実装せよ。制限時間はあわせて10分。ただし、reducelとreducerは、Haskellにおいて次の挙動を示すものとする。
*Main> reducel (\x y -> "("++x++"#"++y++")") $ map show [1..4]
"(((1#2)#3)#4)"
*Main> reducer (\x y -> "("++x++"#"++y++")") $ map show [1..4]
"(1#(2#(3#4)))"
というわけで、Onionでも実装してみた。以下がOnionによる解。0要素の場合、正しく動作しないが、問題でも0要素の場合の挙動は定められてないので、まあいいだろう。かかった時間は大体4〜5分くらい。やはり、function typeと、parameterrized typeが無いのが痛い。
interface Fn {
_(arg1 :Object, arg2 :Object) :Object;
}
def reducel(fn :Fn, lst :List) :Object {
it = lst.iterator;
accum = it.next;
while it.hasNext { accum = fn._(accum, it.next); }
return accum;
}
def reducer(fn :Fn, lst :List) :Object {
it = lst.listIterator(lst.size);
accum = it.previous;
while it.hasPrevious { accum = fn._(it.previous, accum); }
return accum;
}
System::out.println(reducel(
#Fn._(accum :Object, e :Object) {
return "(" + accum + "#" + e + ")";
}, ["1", "2", "3", "4"]
));
System::out.println(reducer(
#Fn._(e :Object, accum :Object) {
return "(" + e + "#" + accum + ")";
}, ["1", "2", "3", "4"]
));
実行結果。
>onion reduce.on (((1#2)#3)#4) (1#(2#(3#4)))
おまけ:Java版
import java.util.*;
public class Reduce { interface Fn<R, A> { R _(A arg1, A arg2); }
static <A> A reducel(Fn<A, A> fn, List<A> lst) { Iterator<A> it = lst.iterator(); A accum = it.next(); while(it.hasNext()) accum = fn._(accum, it.next()); return accum; }
static <A> A reducer(Fn<A, A> fn, List<A> lst) { ListIterator<A> it = lst.listIterator(lst.size()); A accum = it.previous(); while(it.hasPrevious()) accum = fn._(it.previous(), accum); return accum; }
public static void main(String[] args) { System.out.println(reducel( new Fn<String, String>(){ public String _(String accum, String e) { return "(" + accum + "#" + e + ")"; } }, Arrays.asList("1", "2", "3", "4") )); System.out.println(reducer( new Fn<String, String>(){ public String _(String e, String accum) { return "(" + e + "#" + accum + ")"; } }, Arrays.asList("1", "2", "3", "4") )); } }
おまけ2:Nice版
package reduce;
<A> A reducel((A, A)->A fn, List<A> lst) { let it = lst.iterator; var accum = it.next; while(it.hasNext) accum = fn(accum, it.next); return accum; }
<A> A reducer((A, A)->A fn, List<A> lst) { let it = lst.listIterator(lst.size); var accum = it.previous; while(it.hasPrevious) accum = fn(it.previous, accum); return accum; }
void main(String[] args) { println(reducel((String accum, String e) => { return "(" accum "#" e ")"; }, ["1", "2", "3", "4"])); println(reducer((String e, String accum) => { return "(" e "#" accum ")"; }, ["1", "2", "3", "4"])); }
void main(String[] args) { println(reducel((String accum, String e) => { return "(" + accum + "#" + e + ")"; }, ["1", "2", "3", "4"])); println(reducer((String e, String accum) => { return "(" + e + "#" + accum + ")"; }, ["1", "2", "3", "4"])); }
おまけ2-2:Nice版その2。Niceにはせっかくマルチメソッド+syntax sugarによって、既存のクラスにメソッドを追加したように見せかける機能があるのだから、こっちの方がNiceらしい、かも。
package reduce;
<A> A reducel(List<A> lst, (A, A)->A fn) { let it = lst.iterator; var accum = it.next; while(it.hasNext) accum = fn(accum, it.next); return accum; }
<A> A reducer(List<A> lst, (A, A)->A fn) { let it = lst.listIterator(lst.size); var accum = it.previous; while(it.hasPrevious) accum = fn(it.previous, accum); return accum; }
void main(String[] args) { println(["1", "2", "3", "4"].reducel((String accum, String e) => { return "(" accum "#" e ")"; })); println(["1", "2", "3", "4"].reducer((String e, String accum) => { return "(" e "#" accum ")"; })); }
Nemerleでも書いてみた。せっかくNemerleで書くのだから、マクロを使って書いてみることに。
public class Reduce {
public static reducel[T](fn :(T * T)->T, lst :list[T]) :T{
def foldl[T2, V](fn :(V * T2) -> V, init :V, lst :list[T2]) :V{
match(lst){
| [] => init
| x::y => foldl(fn, fn(init, x), y)
}
}
match(lst){
| x::y => foldl(fn, x, y)
}
}
public static reducer[T](fn :(T * T)->T, lst :list[T]) :T{
match(lst) {
| [x] => x
| x::xs => fn(x, reducer(fn, xs))
}
}
}
macro Reducel(expression, lst){
def l = "left";
def r = "right";
<[ Reduce.reducel(fun($(l :usesite), $(r :usesite)){ $expression }, $lst) ]>
}
macro Reducer(expression, lst){
def l = "left";
def r = "right";
<[ Reduce.reducer(fun($(l :usesite), $(r :usesite)){ $expression }, $lst) ]>
}
マクロを使う方は次のような感じ。
using System;
Console.WriteLine(
Reducel("(" + left + "#" + right + ")", ["1", "2", "3", "4"])
);
Console.WriteLine(
Reducer("(" + left + "#" + right + ")", ["1", "2", "3", "4"])
);
Mizushima Kota/e-mail: i021216{at}coins.tsukuba.ac.jp/SKype ID: mizu_standard
kanbayashi君に頼まれたので、貼ってみました。
_ 斎藤ただし [> 全部自炊 まじ素晴らしいっす。 ]