ラベル 計算機科学 の投稿を表示しています。 すべての投稿を表示
ラベル 計算機科学 の投稿を表示しています。 すべての投稿を表示

2013年2月17日日曜日

JavaのAST標準について

みけです。

最近はPluggable Annotation Processor APIにハマっています。

ただ、ここで一つの課題に直面しています。

このエントリーでは、僕が今直面していて、どうしてもやっつけたい課題について書いて行きたいと思っています。


JavaのASTの標準仕様?


これは僕が不勉強なのもあるのですが、

JavaでASTを操作する場合の標準AST(Abstract Syntax Tree)のモデルがないような気がします。

もちろん、package com.sun.toolsにはASTの実装がありますし、

Pluggable Annotation Processor APIをいじっているとInterfaceベースでASTを扱うので、

ないとは言えないのですが、

AST解析用にあるだけっぽくて、ASTからクラスファイル生成の標準がないような感じがします。


ASTモデルにあっては、javaファイルをパースしてASTモデルを生成する方向のAST利用シーンと、

自らASTモデルを生成してjavaファイルを生成するという二つの利用目的があるように思います。


Eclipse JDT


一応Eclipse JDTを簡単に調べてみましたが、

Eclなんとかを使用することが大前提のため、

宗教的な理由により却下。


好みは


僕はGroovyっ子なので、GroovyASTTransformationで使われている

package org.codehause.groovy.ast のモデルを使うのが比較的好きです。

このパッケージは非常に洗練されていて、

僕は一日程度で覚えることができました。


だからこのASTモデルを元にして、

javaファイルジェネレーションを実行できたらいいなと思う反面、

このpackage自体を取り出すのが面倒なので、

さて、困った…という状況にあります。


ASTモデル形成後の話


ASTモデルでクラスを組成できたとしても、

それを実際のjavaファイルに出力するには別途出力するエンジンが必要になるかと思っています。

その辺りについてはGroovyのコードをまだ追っていないので、

よくわかっていません。

まあ、結局のところ、勉強不足という事ですね。


Groovy依存を取り除く


また、Javaの標準的なASTの仕組みが欲しいので、

Groovy依存を無くしたいと思っている次第です。

なので、希望としてはJavaのAST標準がJSRあたりで提起されないかな〜

なんて思っています。


考えてもしかたがないので


ありうる形でASTモデルとjavaファイルジェネレーションを分離しつつ、

標準っぽいのを作って行きたいなーと思っています。

で、思っているだけだと

『[発声練習] 「何がわからないか」がわからないをそのままにしておかない技術モドキ』

こういう感じで突っ込まれそうなので、

githubあたりでコードを公開しつつ、

やろうかなと思っているんですが、

イマイチ抽象的な分野なので、

成果がなかなか現れない(´・ω・`)

@SupressWarnings("serialVersionUID")
public class Hoge implements java.io.Serializable {}

この程度でいいので生成できるASTとコードジェネレーションから作っていければいいかな

なんて思います。


2012年8月21日火曜日

DQXのモンスターの動きのアルゴリズムがなかなか頑張っている件について

ドラクエばっかりやりすぎて、

眠いです。

みけです。

ひょんな思いつき


DQXでレベルを上げる時にお世話になるトンブレロ。

かわいいですね。

動きが本当にブタさんのようでかわいいですね。

獅子のごとく可愛い子は崖から突き落としてみました。


崖から突き落としてみると、本当にストンと落ちます。


真っ直ぐでなくちゃんと最小経路をとおって来ようとする


落とされたトンブレロはどのような動きをするか?

平地でやっている分にはトンブレロの動きは直線的です。

なので、登れない崖をよじ登ってくるのかと思いましたが、

ちゃんと回りこんでくるようになっていました。

ちゃんと最小経路のアルゴリズムが組んであるようです。


プレイ動画でないのがアレですが…


2012年8月11日土曜日

素数の時にEnrico Pucciと出力するプログラムを書いてみた

ドラクエ、素手プレイのため、

全然先に進めません。

みけです。

おやおや!


FizzBuzz問題が話題になっているさなかですが、

面白いエントリーがあったので、批判しつつ取り上げます。


SST


素数のときだけ"JOJO!"って出力するプログラムを作ってみた - くりにっき

まあ、Rubyを応用したシンプルなプログラムです。

ただ、Integer#prime?という言語に元々ある素数判定を使うのは

反則な気がしなくもないかな。

この問題のポイントは素数であるか判定できるプログラムを書くことなので…


KPT


素数のときにJoJo - scala - - 言葉をポッケに持ち歩こう

Scalaをつかった、これまたシンプルな実装です。

Scalaには素数を判定するというメソッドがないので、

自前で実装しています。

それはいいんだけど、計算量が多すぎるorz

例えば、素数1009を素数であると計算するための計算回数が、1007回になります。

そんなに計算する必要はありません。


計算量


どれくらい計算する必要があるかというと、

2で割り切れるかどうか調べたら、対象となる数値の約1/2まで、

調べれば良いことがわかると思います。

何故ならば、対象となる数値(N)の1/2より大きい数値(M)は、

もはや商が1で余りがM - Nなので、

Mの値を増やしていっても商が1以外の数値を取ることはないからです。

同様に、3で割り切れるかどうか調べたら、対象の数値(N)の1/3より大きい数値は

もう調べる必要がありません。

何故ならば、Nの1/3より大きい数値MでNを割ると、

商1または2、余り1~Mの間の値になるからです。

という感じで、割り算をする値kをインクリメントしつつ、

チェックする最大値MをN/kとして限定していくことで、

計算量を減らします。


屁理屈はここまでで…


素数の時にJOJOと出力するプログラムを書こうとしたのですが、

素数でJOJOと言ったら、忘れることのできないキャラが居ますね。

プッチ神父です。

というわけで、素数になったら、Enrico Pucciと出力されるプログラムを

JavaScriptで書きました。


サンプルではまあ、他のブログのご多分に漏れず、1~100までやっています。





JavaScript可愛いですね。

2012年3月30日金曜日

推論に関する覚書き2[『系統樹思考の世界』の読書メモ]

前回のポストは時間の関係で途中で投稿しました。

観察されたデータを絶対的なものとするか、

それとも

仮説を絶対的なものとしてそれに見合うデータを待つか

という二つの立場に対して柔軟な立場をとるのが3.アブダクションです。



3.アブダクション


データを批判的に検討しつつ、データが仮説に対してモル証拠としての価値を擁護する(三中、同著、p.64)

理論の「真偽」を問うのではなく、観察データのもとでどの理論が「より良い説明」を与えてくれるのかを相互比較する


というのが、アブダクションの立場です。

例えば次のような例が挙げられます。
  • 観察データ : 二つあるパソコン用のディスプレイが両方とも何も映らなくなった。
    • 仮説1 : 電源ケーブルが外れた
    • 仮説2 : グラフィックカードが壊れた
    • 仮説3 : 両方のディスプレイが壊れた

この観察データからは仮説の1が「真に」正しいのか、

それとも仮説の2が「真に」正しいのか問うことがナンセンスです。


なぜなら、観察データは個々の場合によって異なるからです。

こういった個々のケースを推論するような場合には、

従来の演繹法や帰納法などのように

データと仮説との関係性に強さを与えるのではなく、

もっとデータと仮説との間の弱い関係性が必要です。


今、手元にあるデータから最も当てはまりそうな、

いい感じの仮説をとりあえずは採用するスタイルの推論を

アブダクションと呼びます。


そして、アブダクションという推論は終わることなく続けられます。

というのも、アブダクションという推論は「真偽」の絶対的な確定行為ではなく、

どの説が最もよくデータに当てはまるかを

テストし、一時的に採用するという

一時的な仮定行為だからです。


例に戻る

先の例においては、皆さんも行う通り、

最初の観察データの後、

とりあえず電源ケーブルを見たり、

他のディスプレイに変えたり

他のパソコンに繋げてみたりといった

新たなデータを取得してみて、

どの仮説が適切であるかを推論していきます。


これが例えば、開発であった場合などは

  • 観察データ:画面のボタンを減らしたら、業務効率が上がった
    • 仮説1 : タブを押す回数が少なくなったから
    • 仮説2 : ユーザーがシステムのUIに不慣れであった。

このような仮説が立てられます。

もし、この後に、「ユーザーはUI操作に習熟している」というようなデータが得られれば、

仮説1の方が妥当でありそうですから、

  • いらない機能が多かった

とシステムに対して新たな事実を発見することができそうです。


そうなれば、その機能はいらないのか、

必要だけど普通のユーザーは使わないので、

特別なユーザーだけ使えるようにする

といった選択が出てくると思います。


まとめ


こうして見てみると、アブダクションという推論過程は

いわゆるアジャイル的なプラクティスに近いものが

あるのかと思われます。


ウォーターフォール型開発は、

真の仕様とは何であるかを定義して、

(演繹的もしくは経験的に真を決定してから、)

開発するようなスタイルでです。

「真」に到達したときに開発は終わります。


これに対してアジャイル型開発は、

変化を受け入れ、もしかしたら学習をして

開発していくスタイルです。

開発にはおそらく終わりはないでしょう。

なぜなら、すべてのリリースが一時的な仮定行為なのだから。



もしかしたらアブダクション的なスタイルの

開発というのもありかもしれませんし、

それでユーザーの価値が向上するなら、

取り組んで見る価値があるかもしれません。


こぼれ話


実は『系統樹思考の世界』という本を読み進めていくと、

アブダクションという推論様式はAIを開発する過程で研究されたという記述が出てきます。


AIは機械が人間のような振る舞いを見せるために

学習するという部分を研究する必要があったのでしょう。


そうするうちに研究の結果が他の分野へも派生して行って、

昨今のアジャイルプラクティスの勃興につながったのではないか

な~んて仮説が出てきたりしますね。

(逆かもしれませんが…)

2011年7月21日木曜日

ランダムの妥当性とは?~その2

前回はランダムを発生させるところまでやったのだが、発生するランダムな値が本当にランダムかわからないという問題があることがわかったので、今回は少し検証してみようと思う。

まずは、発生している値の平均値が10であるか検証。
これ基本です。


def getAverage = {map ->
    def size = map.size
    def sum = withPool {
        map.parallel
        .map{it.value}
        .reduce{s, i ->
            s + i
        }
    }
    sum / size as double
}

def testMap = [[key:1,value:11], [key:2,value:9], [key:3,value:10]]
assert 10.0 == getAverage(testMap)


下のassertは平均値を算出したメソッドが妥当かテストしているだけです。
では、おもむろにテストしてみる。


10.times{
    assert 10.0 == getAerage(getRandom(20 * 10))
}


とりあえず、大丈夫なようだ。

つぎは分散を算出する。これも基本です。


def getSigmaSquare = { map, avg ->
    def size = (map.size <= 2)? 2 : map.size
    def sum = withPool{
        map.parallel
        .map{
            def dif = it.value - avg as double
            dif * dif
        }
        .reduce{s, i ->
            s + i
        }
    }
    sum / (size - 1) as double
}

def testMap = [[key:1,value:11], [key:2,value:9], [key:3,value:10]]
assert 1.0 == getSigmaSquare(testMap, 10)


下のassertは分散を算出したメソッドが妥当かテストしているだけです。
では、おもむろに実行してみる。

10.times{num ->
    def rand = getRandom(10 * 20)
    def avg = getAverage(rand)
    def sigs = getSigmaSquare(rand, avg)
    def sigm = Math.sqrt(sigs)
    println sprintf("num : %2d / avg : %5.1f / sigs : %5.1f / sigm : %4.1f", num, avg, sigs, sigm)
}


実行結果


num :  0 / avg :  10.0 / sigs :  11.8 / sigm :  3.4
num :  1 / avg :  10.0 / sigs :  10.5 / sigm :  3.2
num :  2 / avg :  10.0 / sigs :  16.1 / sigm :  4.0
num :  3 / avg :  10.0 / sigs :  10.6 / sigm :  3.3
num :  4 / avg :  10.0 / sigs :  10.3 / sigm :  3.2
num :  5 / avg :  10.0 / sigs :  11.6 / sigm :  3.4
num :  6 / avg :  10.0 / sigs :   9.9 / sigm :  3.1
num :  7 / avg :  10.0 / sigs :  10.8 / sigm :  3.3
num :  8 / avg :  10.0 / sigs :   8.5 / sigm :  2.9
num :  9 / avg :  10.0 / sigs :   5.3 / sigm :  2.3


発生した値はだいたい、10 ± 3.5 程度にあるということになるのだろうか。

2011年7月20日水曜日

ランダムの妥当性とは?~その1

小難しいタイトルですが、ランダムの妥当性ってなんなのか考えてみることにしました。

とりあえず、スタートのスクリプトは@fumokmmさんのエントリー『Groovyで範囲内乱数』です。

では、スクリプトをどうぞ。(コピペ)

IntRange.metaClass.define{
    random{
        int from = delegate.isReverse() ? to : from
        int to = delegate.isReverse() ? from : to
        int size = to - from + 1
        (Math.floor(Math.random() * size) + from) as int
    }
}


これで、乱数発生の装置は作成できたので、おもむろに作成した乱数をListにぶっこむ処理を書いてみる。


range = 1..20
def getRandom = {run ->
    def values = []
    run.times{values << range.random()}
    withPool {
        values.parallel
        .map{[it, 1]}
        .groupBy{it[0]}.getParallel()
        .map{it.value = it.value.size();it}
        .sort{it.key}
        .collection
    }
}
そして、実行してみる。
getRandom(20 * 10)
予想される結果としては、1~20までの数値が10個を中心として発生するはず。 実行結果1
[1=4, 2=7, 3=7, 4=14, 5=12, 6=10, 7=12, 8=12, 9=5, 10=10, 11=10, 12=5, 13=8, 14=11, 15=12, 16=19, 17=6, 18=18, 19=10, 20=8]
実行結果2
[1=11, 2=8, 3=11, 4=5, 5=10, 6=10, 7=11, 8=7, 9=14, 10=12, 11=9, 12=11, 13=10, 14=14, 15=10, 16=10, 17=7, 18=12, 19=8, 20=10]
実行結果3
[1=10, 2=10, 3=10, 4=9, 5=14, 6=15, 7=8, 8=11, 9=10, 10=8, 11=7, 12=9, 13=10, 14=10, 15=11, 16=6, 17=10, 18=5, 19=10, 20=17]

む、一応、ランダムに数字が発生しているような気がするが、これはランダムと言えるのだろうか?