No Programming, No Life

プログラミング関連の話題や雑記

お題:ポーカーをGroovyで解いてみた

問題

ポーカー

5枚のカードを示す文字列を入力とし、ポーカーの役を出力せよ。
ただし:

一枚のカードは、スートを表す文字+ランクを表す文字列 で構成される。
スートを表す文字は、S, H, D, C のいずれか
ランクを表す文字列は、2, 3, ..., 9, 10, J, Q, K, A のいずれか
下表の役に対応すること。下表の役に該当しない場合は '--' を出力すること。
カードはジョーカーを含まない52枚から5枚が選ばれる。
不正な入力への対応は不要。

対応すべき役と、その役だった場合に出力する文字列は以下のとおり:

フォーカード : 4K
フルハウス : FH
スリーカード : 3K
ツーペア : 2P
ワンペア : 1P
上記のいずれにも該当しない : --

例えば、入力が「D3C3C10D10S3」ならフルハウスなので「FH」と出力する。
入力が「S8D10HJS10CJ」ならツーペアなので「2P」と出力する。

回答

考察

  • Groovyでの解答例は既に@さんが書いてらっしゃいましたが一応書いてみました。(しかもアルゴリズムも酷似してしまい、なんだかすみません。)
  • #countBy 使いたかっただけです。
  • enumをなんとかして使おうとしたため、変換処理まわりが却って回りくどい感じになってしまいました。
  • #toPat 内で結果を返す前に出力もしています。

GroovyとClojureでunlessを書いてみた

Groovy!(あいさつ)
Clojure!(あいさつ)

今回はClojureのマクロの練習も兼ねて、unlessを書いてみました。
unlessはifの逆です。(つまり、条件が偽なら実行するやつですね)

まずはClojure

  • test-isでテストなども書いてみました。
  • マクロって素敵。

次はGroovy

  • メソッドチェーン!
  • でもGroovyだとelseは予約語なので、 'else' とかシングルクォートで囲んで濁さないといけないのがイケてない。

Clojureで文字列から動的に関数名を作って実行したいんだが・・・

リストの第一要素がシンボルなら関数として起動できるのかな?と思っていたんだがどうも違うらしい。

とりあえず、printlnを例として。(symbol 文字列) でシンボルは作れるらしい。

$ clj
Clojure 1.4.0
user=> (symbol? 'println)
true
user=> (= 'println (symbol (str "print" "ln")))
true

でも、実行しても期待の動作をしてくれない。なんでだろう。

user=> (println "hello")
hello
nil
user=> ((symbol (str "print" "ln")) "hello")
nil
user=> (if true "yes" "no")
"yes"
user=> ((symbol "if") true "yes" "no")
ArityException Wrong number of args (3) passed to: Symbol  clojure.lang.AFn.throwArity (AFn.java:437)

なにか大きな勘違いをしているような気もするが、ひとまずメモとして。
追って調べる。

2012-06-03追記

@さんからメンションがあった。



なるほど、やっぱりシンボルとFnは別物なんだな。今回のやつは load-string を使うのがスマートっぽい。

user=> ((load-string (str "print" "ln")) "hello")
hello
nil

ただ、これだとifとかには使えないのかな?

user=> ((load-string "if") true "yes" "no")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: if in this context, compiling:(null:12) 
id:wamanさんからもコメントがあった
(eval (list (symbol (str "print" "ln")) "hello"))

とすると動きました! いまいち動作を理解してませんけど。

こちらは eval する例ですね。
evalは (source eval) とかで覗いてみると、clojure.lang.Compiler を生で使っているようで、プリミティブな操作っぽいですね。
さらに色々やってみたんですが、倭マンさんはlistしてから全体をevalしてますが、リストの先頭の部分(つまり、関数として起動させる部分)のみをevalする感じでも動くようです。

user=> ((eval (symbol "println")) "hello")
hello
nil

これ、evalしてからsymbolしてるので合成関数にしてしまっても便利かもしれません。

user=> (def eval-and-symbol (comp eval symbol))
#'user/eval-and-symbol
user=> ((eval-and-symbol "println") "hello")
hello
nil

エラトステネスの篩を使ってClojureで素数を求める

はじめに

Project Eulerの問題7, 素数10001番目を求める問題。
今回はエラトステネスの篩を使って実装してみた。

By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
What is the 10 001st prime number?

Problem 7 - Project Euler

ソース

解説

関数sieveは篩という意味で、引数の数列の先頭の数値で、先頭以降の数列から先頭の数値の倍数になっているものをふるい落した数列を返却しています。この関数をprimesの中でiterateに渡して結果の先頭を取得すれば無限素数列となっているはずです。
ちなみに、primesが関数になっているのは、頭を抱え込まない*1ようにするためです。

試してみる

$ clj
Clojure 1.4.0
user=> (load-file "primes.clj")
#'user/primes
user=> (take 10 (primes))
(2 3 5 7 11 13 17 19 23 29)
user=> (take 100 (primes))
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541)
user=> (nth (primes) 10000)
OutOfMemoryError Java heap space  clojure.core/filter (core.clj:2463)

どうもヒープを食い尽くすようです…アルゴリズムの検討が必要みたい。
また、改良して行ってみたいと思います。

ちなみに、clojure.contribにprimesがあるので、答えを調べてみる

$ clj
Clojure 1.4.0
user=> (use '[clojure.contrib.lazy-seqs :only (primes)])
nil
user=> (take 10 primes)
(2 3 5 7 11 13 17 19 23 29)
user=> (take 100 primes)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541)
user=> (nth primes 10000)
104743
user=> 

ということで、104743らしいです。nthで10000を指定しているのは、インデックスが0番目から開始されるためです。

さらに別解

java.math.BigInteger に #nextProbablePrime() というメソッドがあって*2、これを使っても素数が求められそうです。

$ clj
Clojure 1.4.0
user=> (defn nextPrime [p] (.nextProbablePrime p))
#'user/nextPrime
user=> (def primes (iterate nextPrime (BigInteger. "2")))
#'user/primes
user=> (take 10 primes)
(2 3 5 7 11 13 17 19 23 29)
user=> (take 100 primes)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541)
user=> (nth primes 10000)
104743

"おそらく" ってのがひっかかりはしますが、10001番目くらいなら正確に求められるようです。

参考

Clojureの文法で参考にしたのは以下の書籍です。

プログラミングClojure

プログラミングClojure


7つの言語 7つの世界

7つの言語 7つの世界

アルゴリズムの参考にしたのは以下のHaskellの書籍です。

プログラミングHaskell

プログラミングHaskell

*1:参考の書籍のプログラミングClojure参照

*2:[http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigInteger.html#nextProbablePrime():title:bookmark]

Groovyで関数型を意識したFizzBuzzを書いてみた

はじめに

なるべく関数型を意識して書いてみました。

なんとか140文字に収めることができました。

ノート

ごちゃごちゃしてて分かりにくいので、読みやすくなるように軽く解説。

aクロージャ
a={n,s->['']*n+s}

まずはこれですが、n(数値を想定)とs(文字列を想定)の2引数を受け取るクロージャをaという変数に格納しています。

[''] * n + s

の結果がこのクロージャの値になりますが、これは例えばn3で、s'fizz'だった場合、

['', '', '', 'fizz']

というリストになります。

fクロージャ
f={n->[0..n,a(3,'fizz')*n,a(5,'buzz')*n].transpose().collect{i,f,b->[f,b].any()?f+b:i}}

さて、次は長いですが、順番に見て行きましょう。
まずは、transposeですが、これは複数指定されたリストの縦を横を組み替えたリストにするメソッドです。たとえば、こんな感じです。

assert [['a', 'b', 'c'], [1, 2, 3]].transpose() == [['a', 1], ['b', 2], ['c', 3]]

注意点としては、長過ぎた要素は無視されることでしょうか。

assert [['a'],['b','b'],['c','c','c']].transpose() == [['a', 'b', 'c']]

さて、fクロージャに戻って、transpose()している部分を見てみましょう。

[0..n, a(3,'fizz') * n, a(5,'buzz') * n].transpose()

これは、

  • 第1要素が0〜nまでの数列
  • 第2要素が、先ほどのaクロージャで作った、['', '', '', 'fizz'] 配列を n 回繰り返した配列
  • 第3要素が、も同じく 'buzz'配列の n 回繰り返した配列

となりまして、それをtranspose()するわけですから、結果としては、以下のような配列となります。

[[0, '', ''], [1, '', ''], [2, '', ''], [3, 'fizz', ''], [4, '', ''], [5, '', 'buzz'], [6, '', ''], ...]

出来上がる配列の数は一番数が少ない 0..n に合わせられるのがミソですね。

さて、お次はcollectしている部分です。collectに渡しているクロージャはこんな感じになっています。

{ i, f, b -> [f, b].any() ? f+b : i}

これは、引数を3つ取り(それぞれ、数、fizz文字列、buzz文字列を想定)、結果の文字列へと加工している部分です。

たとえば、このクロージャに配列の [15, 'fizz', 'buzz'] を引数として渡すと、'fizzbuzz' という文字列が返却されます。

def toFizzBuzz = { i, f, b -> [f, b].any() ? f+b : i}
assert 'fizzbuzz' == toFizzBuzz([15, 'fizz', 'buzz'])

Groovyでは引数として配列を渡すと展開されて適用されますので、第1要素がi、第2要素がf、第3要素がbといったようにバインドされるイメージです。

[f, b].any() ? となっていますので、 'fizz' もしくは 'buzz' 文字列がある場合のみ、文字列を結合した結果とし、そうでなければ、数値の i を返却します。

fクロージャの戻り値

ここまでで、fクロージャはfizzbuzzの配列を返却できています。これでほぼ完成なのですが、あとは出力処理を行うのみとなります。

fクロージャを利用して結果出力
f(100).drop(1).each{println(it)}

ここでは、fクロージャに引数として100を与え、0〜100までのfizzbuzzリストを取得しています。
ただし、fizzbuzzは1〜nのという条件ですので、drop(1)で、1つ要素を取り覗いています。
あとはおなじみ、eachで回して出力しているだけです。

おわりに

Groovyでも書こうと思えばそれなりに関数型っぽく書けました。
以上、誰かの何かの参考になれば幸いです。

関連記事

Buzzになる数字をn個挙げるをClojureで(その2)

このシリーズの一覧はこちら

はじめに

Clojure! (あいさつ)

Buzzになる数字をn個挙げるをClojureで - No Programming, No Lifeの続き。
clojure-contrib.str-utilsのstr-joinをuseして、出力をカンマ区切りにしてみました。

useを追加

(use '(clojure.contrib (str-utils :only (str-join))))

userequirereferを一気にやってくれるので、名前空間なしで参照できるようになります。
:onlyキーワードで必要な関数だけ指定することで、名前空間の汚染を最小限に留めることができます。
(useとrequireについては require と use - 水底で思うことがとても参考になりました)

出力部分を str-join を使うように修正

前は、doseqを使っていたのですが、今回はuseしてきたstr-join関数を利用してみたいと思います。

Usage: (str-join separator sequence)

str-utils API reference (clojure-contrib)

使い方は、第一引数にセパレータとなる文字を、その後くっ付けたいシーケンスを指定する感じです。

; 出力 (str-join版)
(println
  (str-join ", " (take 10 (to-only-buzz-list fizz-buzz-list))))

ここでは、先頭10個に絞った fizz-buzz のシーケンスを ", " で繋いで出力しています。

出力結果

5, 10, 20, 25, 35, 40, 50, 55, 65, 70

おわりに

clojure-contribは非常に強力な関数ライブラリなので、使い方を一覧にしたりしたいです。
Enjoy Coding!

ソース