ラベル other languages の投稿を表示しています。 すべての投稿を表示
ラベル other languages の投稿を表示しています。 すべての投稿を表示

2011年12月13日火曜日

Alloy 本

土曜に Amazon のお急ぎ便で注文したら、日曜の夜に届いた。 Alloy本
日曜は暇がなかったので、今日、帰宅してから早速 Alloy Analyzer をダウンロードして、テキストに取りかかる。(単なる jar なので java -jar ですぐ動く。)

下図の左側ペインのようにコードを書き込んで、[Execute]>[Show Metamodel]を実行すると、、、

、、、このようなダイアグラムが得られる。

このダイアグラムは、四角形の配置を変えたり、色やフォントを変えたりして、ある程度見せ方を好きなように変えられるようだけど、どうもダイアグラムはそれほど本質的なものではないっぽい。

まあ、1時間ほどいじっただけだし何も理解できてないけど、上の上の図で左側のペインに書いてある Alloy コードがやっぱり主役っぽい。
これに述語(pred)やファクト(fact)をちょっと書き足しては、それに対してアサーション(assert)をちょっと付け加えて、そしたら実行して右側のペインで確認して、って流れになる模様。そうだとしたら、どこか TDD に似ていてとっつきやすい。

とは言っても、関心の重点は実装ではなくむしろ仕様であって高い抽象レベルで仕様をモデリングする事が目的の技術らしい。
ちなみに、抽象度をレベル分けするときに、伝統的に分析レベル、設計レベル、実装レベルなんて言ったりするけど、Alloy と同じ雰囲気で、なおかつレベルの違う他言語を持ってくると、分析レベル:Alloy、設計レベル:LePUS3、実装レベル:Coq てな感じに当てはまるんじゃないかと思う。

まあ、いきなりいろんな事をやり始めなくても、とりあえず Alloy だけでも、例えば金融システムのビジネスルールとかに適用して、仕様バグを取り除くような事は、結構すぐにでも始められそうな予感。

2011年12月6日火曜日

Jaskell このやろう!

名前だけはずいぶん前から知ってたけど、あまり興味が無くて放置していた Jaskell。

『プロダクティブ・プログラマ』で紹介されて高評価だったから、さぞかし有望株なのだろうと思いきや…

誰も使ってる気配がない…

まあダウンロードはできるから、ちょっと試してみようとしたけど、そもそも動いてくれない。

Exception in thread "main" java.lang.NoSuchMethodError: jfun.parsec.Parsers.plus(Ljfun/parsec/Parser;Ljfun/parsec/Parser;)Ljfun/parsec/Parser;
 at jfun.jaskell.JaskellParser.(JaskellParser.java:299)
 at jfun.jaskell.JaskellParser.instance(JaskellParser.java:1377)
 at jfun.jaskell.JaskellParser.parseExprOrLib(JaskellParser.java:1436)
 at jfun.jaskell.Jaskell.parseExprOrLib(Jaskell.java:2356)
 at jfun.jaskell.Jaskell.parseExprOrLib(Jaskell.java:2376)
 at jfun.jaskell.Jaskell.eval(Jaskell.java:2469)
 at jfun.jaskell.Jaskell.evalInputStream(Jaskell.java:2657)
 at jfun.jaskell.Jaskell.evalResource(Jaskell.java:2555)
 at jfun.jaskell.Jaskell.evalResource(Jaskell.java:2531)
 at jfun.jaskell.Jaskell.importPrelude(Jaskell.java:1990)
 at jfun.jaskell.shell.Shell.getShellRuntime(Shell.java:42)
 at jfun.jaskell.shell.Shell.main(Shell.java:35)
ちなみにこの例外は、java コマンドから Jaskell Shell を起動しようとしたときにも、Jaskell インスタンスを生成してそいつにスクリプトを読ませようとしたときにも、 どっちでも発生する。

第一、起動の仕方らしきものにたどり着くまでだいぶかかった。プロダクティブ・プログラマで紹介されている URL、「http://jaskell.codehaus.org/」 は、Jaskell でググっても一番上にくるやつだけど、動かし方がどこにも全然書いてない。

jaskell-1.0.jar を実行したら何か起こるだろうと思ったけど、うんともすんとも言わない。MANIFEST.MFにも、やっぱなんも書いてなかった。

しょうがないから、JavaDoc を読んでたら Jaskell クラスというのがあって、こいつが文字列やファイルを評価する eval () メソッドを持ってるから、インスタンス作って eval() したら、上の例外。

半泣きでググってたら、Jaskell Shell というのを見つけるが、トップページからリンクされてる訳でもなく、どこが本物のプロジェクトページだか、訳が分からない。で、動かしてみたら、また上の例外。

まあ、何個かある jar の組み合わせを変えてみるとか、再コンパイルしてみるとかで、解決しない事もないんだろうけど、もういい。心折れた。降りるわ。

それにしても Neal Ford さんは、なんでこんなの紹介したんだ…。
普通に、Haskell 勉強して行こうやって結論しか出てこない。

2011年12月3日土曜日

Fantom でサンタクロース問題

前回のせんだみつおゲームで Fantom の Actor の使い方が何となく分かったので、サンタクロース問題でもやってみようと思う。クリスマスも近いしな。まあ、本物のプログラマには、そんなの関係ないけどな。

====

SantaClausProblem.main()は、サンタ、トナカイ、小人を生成して起動する

using concurrent

class SantaClausProblem {
  Void main() {
    ActorPool pool := ActorPool()

    Santa santa  := Santa(pool)
    Deer[] deers := (1..9).map |Int n->Deer| { Deer(pool, n, santa) }
    Elf[] elves  := (1..10).map |Int n->Elf| { Elf(pool, n, santa) }

    deers.each |Deer deer| { deer.send("vacation") }
    elves.each |Elf elf| { elf.send("home") }

    while (!pool.isStopped) { Actor.sleep(1sec) }
  }
}

Player は登場人物共通のコード。ActorUnderSanta はトナカイと小人の共通コード。

Actor
 └ Player
     ├ Santa
     └ ActorUnderSanta
         ├ Deer
         └ Elf
const class Player: Actor {
  new make(ActorPool pool) : super(pool) {}

  Void sleepRandom(Range r) { Actor.sleep(1sec* Int.random(r)) }
}
const class ActorUnderSanta: Player {
  const Int number
  const Santa santa

  new make(ActorPool pool, Int number, Santa santa) : super(pool) {
    this.number = number
    this.santa = santa }

  Str name() { this.typeof().name + number  }
}

トナカイと小人はこんな感じ

const class Deer: ActorUnderSanta {
  new make(ActorPool pool, Int number, Santa santa)
    : super(pool, number, santa) {}

  override Obj? receive(Obj? msg) {
    if ("vacation" == msg) { sleepRandom(1..10); santa.send(this) }
    return msg
  }
}

const class Elf: ActorUnderSanta {
  new make(ActorPool pool, Int number, Santa santa)
    : super(pool, number, santa) {}

  override Obj? receive(Obj? msg) {
    if ("home" == msg) { sleepRandom(2..15); santa.send(this) }
    return msg
  }
}

サンタはこんな感じ

const class Santa: Player {
  new make(ActorPool pool) : super(pool) {}

  override Obj? receive(Obj? msg) {
    echo (((ActorUnderSanta)msg).name + " が来た")
    
    if (msg is Deer) addDeer((Deer)msg)
    else if (msg is Elf) addElf((Elf)msg)
    
    return msg
  }
  Void addDeer(Deer deer) {
    Deer[] deers := get("deers", (Deer[])[,])
    deers.add(deer)
    wakeUpIfConditionIsMet
  }
  Void addElf(Elf elf) {
    Elf[] elves := get("elves", (Elf[])[,])
    elves.add(elf)
    wakeUpIfConditionIsMet
  }
  Void wakeUpIfConditionIsMet() {
    if (9 == getDeers().size) deliverToys
    else if (3 <= getElves().size) meetInStudy
  }
  Void deliverToys() {
    echo("おもちゃを配る")
    sleepRandom(1..2)
    getDeers().each|Deer deer| { deer.send("vacation") }
    Actor.locals["deers"] = (Deer[])[,]
  }
  Void meetInStudy() {
    echo("小人と打ち合わせ")
    sleepRandom(1..2)
    Elf[] elves := getElves()
    elves.eachRange(0..2, |Elf elf| { elf.send("home") })
    Actor.locals["elves"] = elves.removeRange(0..2)
  }
  Deer[] getDeers() { (Deer[])Actor.locals["deers"] }

  Elf[] getElves() { (Elf[])Actor.locals["elves"] }

  Obj get(Str key, Obj defaultValue) {
    Obj? result := Actor.locals[key]
    if (null == result) {
      result = defaultValue
      Actor.locals[key] = result
    }
    return result
  }
}
こんな結果が得られる
$ fan santaClausProblem.fan
Deer6 が来た
Deer1 が来た
Elf2 が来た
Elf9 が来た
Deer9 が来た
Deer2 が来た
Deer8 が来た
Deer3 が来た
Elf3 が来た
小人と打ち合わせ
Elf5 が来た
Elf8 が来た
Deer4 が来た
Deer7 が来た
Elf1 が来た
小人と打ち合わせ
Deer5 が来た
おもちゃを配る
Elf2 が来た
Elf7 が来た
Elf9 が来た
小人と打ち合わせ
Deer9 が来た
Deer5 が来た
Elf4 が来た
Elf6 が来た
Elf3 が来た
小人と打ち合わせ

2011年11月29日火曜日

Fantom でせんだみつおゲーム

Youtube 観てたら、古いテレビの録画で、せんだみつおゲームをやっていた。やっぱりプログラマだから書いてみたくなる。言語はこないだ見つけた、Fantom にしてみる。

こんな感じ

  • Actor を5つ生成して、5名の参加者に見立てる
  • メインのスレッドでは、無限ループしながら Actor にタイミングを出しつづける
  • Actor は、"SENDA", "MITSUO", "NAHANAHA"を出力しながら、ランダムに次の番のアクターを指名する

using concurrent

class SendaMitsuoGame {
  Void main() { play }

  Void play() {
    pool := ActorPool()
    Actor[] actors := (1..5).map { Player(pool, it) }

    actors[0].send("next1")
    actors.each { it.send(actors.toImmutable) }

    messages := ["senda", "mitsuo", "nahanaha"]
    while (true) {
      message := getHeadAndRotate(messages)
      actors.each { it.send(message) }

      Actor.sleep(1sec)
    }
  }
  Str getHeadAndRotate(Str[] m) { m.add(m.removeAt(0)).last }
}

const class Player : Actor {
  const Int number

  new make(ActorPool p, Int number) : super(p) {
    this.number = number }

  override Obj? receive(Obj? msg) {
    if (msg is List) { Actor.locals["all"] = msg }
    else if (msg == "senda") doSenda
    else if (msg == "mitsuo") doMitsuo
    else if (msg == "nahanaha") doNahanaha
    else if (0==((Str)msg).index("next")) {
        setMyTurn(((Str)msg)["next".size..-1].toInt)
    }
    return null
  }

  Bool isNext(Int n) {
    diff := (n - number).abs()
    return 1 == diff || 4 == diff
  }

  Bool isMyTurn() { Actor.locals["myTurn"] }

  Void setMyTurn(Int f) {
    Actor.locals["myTurn"] = (number == f)
    Actor.locals["nextOnesTurn"] = isNext(f) }

  Void doSenda()  { callAndAssign("SENDA") }

  Void doMitsuo() { callAndAssign("MITSUO") }

  Void doNahanaha() {
    if (Actor.locals["nextOnesTurn"])
        echo("" + number + ": NAHANAHA");
  }

  Void callAndAssign(Str call) {
    if (!isMyTurn()) return

    next := nextOne()
    echo("" + number + ": " + call + " -> " + next);

    ((Actor[])Actor.locals["all"]).each { it.send("next" + next) }
  }

  Int nextOne() {
    next := Int.random(1..4)
    if (next < number) return next
    return (++next < 6) ? next: 1
  }
}

出力はこんな感じになる。ミスる事も無く、延々とせんだみつおゲームを繰り返す。適当な確率でミスさせる事も、もちろんできるけど、割愛。

$ fan senda.fan 
1: SENDA -> 2
2: MITSUO -> 5
1: NAHANAHA
4: NAHANAHA
5: SENDA -> 1
1: MITSUO -> 3
4: NAHANAHA
2: NAHANAHA
3: SENDA -> 5
5: MITSUO -> 4
5: NAHANAHA
3: NAHANAHA
4: SENDA -> 3
3: MITSUO -> 1
5: NAHANAHA
2: NAHANAHA
1: SENDA -> 4
4: MITSUO -> 5
1: NAHANAHA
4: NAHANAHA
しかし夜遅くまで、俺、何書いてんだ・・・

2011年11月25日金曜日

Groovy + POI

Groovy では、クロージャを使って若干の下準備(下例では TableBuilder)をしておくと、以下のようなコードが書ける。

static void main(args) {
   new TableBuilder().with {
     sheet(   "test"
         )(   style(header)
         )(   "No"   )(   "名前"      )( nextRow
         )(   style(normal)
         )(   1      )(   "琵琶湖"    )( nextRow
         )(   2      )(   "多来加湖"  )( nextRow
         )(   3      )(   "富内湖"    )( endTable   )
   }.write(new FileOutputStream("sample.xls"));
}

TableBuilder の中では、Apache POI を使っていて、実行すると 下のような Excel ファイルが sample.xls に書き出される。

カッコの開き方が気に入らないが、しょうがないらしい。下の A、B は等価だけど、C は別ものに解釈される。上例を C のようなカッコのに直すと、動作しない。

ABC
   ("A1")("B1")(
    "A2")("B2")
    ("A1")("B1"
   )("A2")("B2")
   ("A1")("B1")
   ("A2")("B2")

TableBuilder は下のコードのようになる。

class TableBuilder {
   def book
   TableBuilder() { book = new HSSFWorkbook() }
   def nextRow = { cell, style ->
      cursor.curry(cell.offset(1, -cell.columnIndex), style)
   }
   def endTable = { cell, style -> book }
   def cursor = { cell, style, arg ->
      if (arg instanceof Closure) { arg(cell, style) }
      else { 
         cell.setCellValue(arg)
         if (null != style) cell.cellStyle = style 
         cursor.curry(cell.next(), style) 
      }
   }
   def start(sheet){ cursor.curry(sheet.cellAt(0, 0), null) }
   def sheet(name) { start(book.createSheet(name)) }
   def style(styleClosure) {
      { cell, style-> cursor.curry(cell, styleClosure(book))}}
}

また、POI の API が Groovy コードの中で馴染むように、Mixin でメソッドを追加した。

class CellMix {
   def next() { offset(0, 1) }
   def offset(r, c) {
      sheet.cellAt(rowIndex + r, columnIndex + c) }
}
class RowMix {
   def cellAt(c) { getCell(c) ?: createCell(c) }
}
class SheetMix {
   def rowAt(r) { getRow(r) ?: createRow(r) }
   def cellAt(r, c) { rowAt(r).cellAt(c); }
}
class CellStyleMix {
   def borderMethodName = { s-> "setBorder" + s.capitalize() }
   def setBorder = { arg, edge -> 
      owner."${borderMethodName(edge)}"(arg."$edge" ?:BORDER_NONE); 
      setBorder.curry(arg) }
   def setBorder(Map arg) {
   setBorder.curry(arg)("top")("left")("bottom")("right") }
}

カッコを連鎖させるやり方にしたのは、単にクロージャとカリー化を試したかっただけで、実は、普通のクラスと leftShift() のオーバライドだけで、 下のような C++風の書き方もできて、こっちの方がむしろスッキリしたコードになる。

sheet("test") << 
   "A1" << "B1" << endRow <<
   "A2" << "B2" << endTable

2011年11月23日水曜日

Fantom

Info Q を読んでたら、Scala は先行き暗いねって記事があった。その内容自体は、まあ一理あるねくらいだけど、その問題提起をしている人が、気に入ってる言語として Scala と対比してる Fantom というのがあって、ちょっと調べてみた。

ここでサンプルコードを読んでみると、特にキャッチーな感じはしないけど、その分、実用性を念頭に置いてる気がする。知的な遊びのためよりむしろ割と泥臭い現場でも使えるように考えられてる印象。C# が分かる Javaプログラマで、面倒くさがりな人向けって感じか。言語仕様とか文法のパラダイムよりも、バイトコードやモジュールの扱いみたいなアーキテクチャ寄りの面に長所があるのかもしれない。

Linux にインストールするには、

  • オフィシャルサイトのトップページからダウンロードして、
  • 適当なところに展開して、fantom-1.0.XX フォルダの中に入る
  • adm/unixsetup を実行して bin/下のファイル群に実行権限を付ける
  • bin/fan -versionを実行して、動作確認
  • 必要に応じて PATH にbin/を追加する

以下のようにして、FWT という GUI 上の HelloWorld ができる。

  • ファイル FwtHello.fan に以下のコードを書く
    using fwt
    class FwtHello : Test
    {
      Void main()
      {
        Window { Label { text = "Hello world" }, }.open
      }
    }
  • $ fan FwtHello.fan で以下のようなちっこいウィンドウが表示される。

====

全然、Fantom とは関係ない話だけど、Fantom についていろいろググってて、Fantom で迷路の最短経路をとくパズルをやってる人がいたので、その先をたどると面白い記事を見つけた。

問題は「人材獲得作戦・4 試験問題ほか」に載ってて、この問題をどのように使ったかという顛末(「人材獲得作戦・3」)まで書かれていてとても面白い。

ただ、「問題の性質からいって、キューやリストが必要なのは明らかなのに、言語にCを使う人が相当多い。」ってところだけ残念。以下のようにすればオブジェクト指向も関数型も論理型も関係なく簡単に解ける。

  • S の上下左右に隣接する空のマスに1 を書き込む
  • 1 に隣接する上下左右の空のマスに2 を書き込む
  • ・・・
  • N に隣接する上下左右の空のマスにN+1を書き込む
  • Gに着いたら、隣接するマスを N からカウントダウンする形で戻れば完成

コードは簡単だから省略。C 言語はもちろん、多分 COBOL でも書ける。