27 December, 2008

[Haskell][RWH] Chapter 1

RWHのChapter 1を読む。


  • not equalは!=ではなく/=

  • temporary definitionをletで。例えば、ghci> let e = exp 1とか。

  • ghci> :info []とかすれば、[]の情報が調べられる。

  • ghci> :set +tで型情報を表示する。解除は:unset +t:type itでunsetしても簡単に調べられる。

  • itで前の計算結果を参照出来る。



簡単にHaskellのプログラムを書くにはinteractを使えば良いらしい。
main = interact f 但し、fはString -> Stringな関数。これで標準入力から読んで標準出力に書けば良い。

% cat WC.hs
main = interact wordCount
where wordCount input = show (length (lines input)) + "\n"
%

Prelude> :info interact
interact :: (String -> String) -> IO () -- Defined in System.IO

実行は
% runghc WC.hs < quux.txt

Exerciseはとりあえずこんな感じ?

main = interact wordCount
where wordCount input = numLines input ++ "\t" ++ numWords input ++ "\t" ++ numChars input ++ "\n"
numLines input = show (length (lines input))
numWords input = show (length (words input))
numChars input = show (length input)

04 December, 2008

[Scala] map and flatMap on scala.Function1

scala.Function1 lacking @ λ Tony’s blog λ

Scalaの関数の型の scala.Function1 にもっと便利な機能を、という話。
mapはともかくflatMapをこうやって定義するのか、というのはなんか勉強になったので、メモ代わりに。
同様に S combinator というのも自分ではなんか巧く使えないものなので参考になるなぁ、と。on は普通に便利かも。

[Misc] These days...

Project Eulerはその後も細々と解いてはいるのだが、いちいち解法をblogに書くのが面倒になって更新をさぼってました。あと1問でLevel 2なんだけどなー。

 あと、Scala勉強会@関東2の準備が忙しかったりとか。仕事でのプレゼンは勿論あるけど、私的な勉強会でのプレゼンはこれが始めてで、なんていうか仕事とは別の緊張感がありますね。つまり仕事と違って、「実は自分は良く解ってなくて外しているんじゃないかしらん」みたいな不安とか。

そういえばそろそろReal World Haskellの発売ですね。東京近辺で興味がある人がいれば是非とも読書会とか検討しません?

20 October, 2008

[Project Euler] Problem 22

Problem 22

 この問題では、入力データをコードに直接貼付けず、ファイルから読み込むようにする必要がある。
 ファイルからデータをどう読み込むかだが、scala.io.Source.fromFile("filename").getLines で行単位で読み込めるイテレータを生成するので、それを使うのが楽だと思う。(本当はそういう目的のライブラリでは無い様にも思うのだが...。)
 names.txt がどこに置かれているのかというと、Eclipse上のプロジェクトProjectEuler下の、srcの下の、パッケージP020の下に、names.txtが置かれているので、filenameとしては"src/P020/names.txt"になった。

object P022 {
def main(args:Array[String]) {
val line:String = scala.io.Source.fromFile("src/P020/names.txt").getLines.next
val names1:List[String] = line.split(",").map{s => s.substring(1, s.size-1)}.toList
val names2:List[(String,Int)] = names1.sort{(a,b) => a.compareTo(b)<0}.zipWithIndex.map{t => (t._1, t._2+1)}
println(names2.head) // (AARON,1)
def f(t:Tuple2[String,Int]):Int = t match {
case (s,i) => i * s.toCharArray.map{c => c-'A'+1}.foldLeft(0)(_+_)
}
println(f(names2.head)) // (AARON,1)
println(names2.map(f).foldLeft(0)(_+_))
}
}

[Project Euler] Problem 21

Problem 21

素数リストとかは過去の問題のを使い回し。

object P021 {
val initPrimes:List[Int] = List(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)
def primes(init:List[Int]):List[Int] = {
def min(a:Int, b:Int) = if (a<b) a else b
def sq(a:Int) = a*a
init ::: List.range(init.last+1, min(100000,sq(init.last))).filter{x => init.forall{x%_!=0}}
}
val primeList:List[Int] = primes(primes(initPrimes))
def factors(n:Int):List[Tuple2[Int,Int]] = {
def g(n:Int, p:Int, c:Int):Tuple2[Int,Int] = if (n%p==0) g(n/p, p, c+1) else Tuple2(n,c)
def f(n:Int, ps:List[Int], ts:List[Tuple2[Int,Int]]):List[Tuple2[Int,Int]] = (n,ps) match {
case (1,_) => ts
case (_,Nil) => error("n="+n)
case (_, a::as) if n%a==0 => {
val z = g(n,a,0)
f(z._1, as, Tuple2(a,z._2)::ts)
}
case (_, a::as) => f(n, as, ts)
}
f(n, primeList, Nil)
}
def sumDiv(ts:List[Tuple2[Int,Int]]):Int = {
def pow(a:Int, b:Int):Int = b match {
case 0 => 1
case _ => pow(a,b-1)*a
}
def f(t:Tuple2[Int,Int]):Int = {
List.range(0, t._2 +1).map{pow(t._1, _)}.foldLeft(0)(_+_)
}
ts.map(f).foldLeft(1)(_*_)
}
def sumDivisors(n:Int):Int = sumDiv(factors(n))-n
def main(args:Array[String]) {
println(sumDivisors(220))
println(sumDivisors(284))
val l = List.range(2,10000).filter{a => {
val b=sumDivisors(a)
val c=sumDivisors(b)
val r=(a!=b)&&(a==c)
if (r) println(a+"<->"+b)
r
}}
println(l)
println(l.foldLeft(0)(_+_))
}
}

---
 ここまでProject Eulerを解いてきて思ったのだが、組み込みライブラリとか言語仕様で、

  • アノテーションを付けるだけで関数の値をメモ化してくれる。
  • とりあえず1,000,000以下の素数は予め計算されている。
  • 内部でIntの計算が桁溢れしたら勝手にLong -> BigInt と桁を増やしてくれるような数値型。
  • 素因数分解も組み込み関数で用意されている。
とかだと、かなり楽が出来るのになぁ...。

19 October, 2008

[Project Euler] Problem 20

Problem 20

BigIntegerを使うだけ。

object P020 {
def main(args:Array[String]) {
def fact(n:Int):BigInt = n match {
case 0 => new BigInt(java.math.BigInteger.ONE)
case _ => new BigInt(java.math.BigInteger.valueOf(n))*fact(n-1)
}
val r = fact(100).toString.toCharArray.map{c => c-'0'}.foldLeft(0)(_+_)
println(r)
}
}

[Project Euler] Problem 19

Problem 19

難しくは無いはずなんだが、1900を1990と入力していたtypoの為でなかなか正解に辿り着けなかった。

object P019 {
def isLeap(y:Int):Boolean = y match {
case _ if y%400==0 => true
case _ if y%100==0 => false
case _ if y%4==0 => true
case _ => false
}
def daysOfMonth(y:Int, m:Int):Int = m match {
case 9 => 30
case 4 => 30
case 6 => 30
case 11 => 30
case 2 => if (isLeap(y)) 29 else 28
case _ => 31
}
def daysInYear(y:Int):Int = if (isLeap(y)) 366 else 365
def daysFrom1Jan1900(year:Int, month:Int, day:Int):Int =
List.range(1900,year).map{y => daysInYear(y)}.foldLeft(0)(_+_) +
List.range(1,month).map{m => daysOfMonth(year,m)}.foldLeft(0)(_+_) +
(day-1)
val day0Jan1900:Int = daysFrom1Jan1900(1900,1,0) // Sunday
def dayOfTheWeek(year:Int, month:Int, day:Int):Int = (daysFrom1Jan1900(year,month,day)-day0Jan1900)%7
def main(args:Array[String]) {
println(dayOfTheWeek(1901,1,1))
println(dayOfTheWeek(2000,12,1))
println(
(for(y <- List.range(1901,2001); m <- List.range(1,13))
yield dayOfTheWeek(y,m,1)).filter{_==0}.size)
}
}

[Project Euler] Problem 18

Problem 18

n段目では、(n-1)段目で左右を選ぶ選択で大きな方を選ぶ、というのを再帰的にやれば良いが、計算時間を減らす為に、Problem 15と同様にメモ化する。

import scala.collection.mutable.HashMap
object P018 {

val input:Array[String] = Array(
"75",
"95 64",
...中略...
"63 66 04 68 89 53 67 30 73 16 69 87 40 31",
"04 62 98 27 23 09 70 98 73 93 38 53 60 04 23") // y=0
val data:Array[Array[Int]] = input.map{s => s.split(" ").map{x => x.toInt}}
val height = input.size
def value(x:Int,y:Int):Int = data(height-1-y)(x)
val map:HashMap[Tuple2[Int,Int],Int] = new HashMap()
def search(x:Int,y:Int):Int = map.get(Tuple2(x,y)) match {
case Some(x) => x
case None => (x,y) match {
case (_,0) => {
val v = value(x,y)
map.put(Tuple2(x,y),v)
v
}
case _ => {
val s0 = value(x,y)
val s1 = search(x,y-1)
val s2 = search(x+1,y-1)
val v = if (s1>s2) s0+s1 else s0+s2
map.put(Tuple2(x,y),v)
v
}
}
}
def main(args:Array[String]) {
println(search(0,height-1))
}
}

[Project Euler] Problem 17

Problem 17

綴りが間違っていると正解が出ないので、念のために数詞を調べ直したりとか、英語を使ってない我々にはそれだけで敷居が高い問題。
この手の問題を解く時は、match-case構文を便利だと特に感じる。

object P017 {

val digitOne:List[String] = List("", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
val teens:List[String] = List("", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen")
val digitTen:List[String] = List("", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety")
def toEnglish(n:Int):String = n match {
case 1000 => "one thousand"
case _ if (n/100 != 0) => digitOne(n/100)+" hundred" + (if (n%100==0) "" else " and "+toEnglish(n%100))
case _ if (10<n)&&(n<20) => teens(n-10)
case _ => Tuple2(n/10, n%10) match {
case (a,0) => digitTen(a)
case (0,b) => digitOne(b)
case (a,b) => digitTen(a)+"-"+digitOne(b)
}
}
def countLetter(s:String) = s.split("[ -]").map{s => s.length}.foldLeft(0)(_+_)
def main(args:Array[String]) {
/*
println(List(1,9,10,11,19,20,21,99).map{x => Tuple3(x,toEnglish(x),countLetter(toEnglish(x)))})
println(List(100,101,110,111,120,121,199).map{x => Tuple2(x,toEnglish(x))})
println(List(200,201,210,211,299).map{x => Tuple2(x,toEnglish(x))})
println(List(900,901,910,911,999,1000).map{x => Tuple2(x,toEnglish(x))})
*/
println(List(342,115).map{x => Tuple3(x,toEnglish(x),countLetter(toEnglish(x)))})
println(List.range(1,1001).map{x => countLetter(toEnglish(x))}.foldLeft(0)(_+_))
}
}

[Project Euler] Problem 16

Problem 16

java.math.BigIntegerを使うだけ。

object P016 {
def main(args:Array[String]) {
val v2 = new BigInt(java.math.BigInteger.valueOf(2L))
val r = v2.pow(1000).toString.toCharArray.map{c => c-'0'}.foldLeft(0)(_+_)
println(r)
}
}

[Project Euler] Problem 15

Problem 15

これも計算機を使わないで解いた方が速い問題。
n x n grid で考えると、2n ステップの中から n 個右を選ぶということだから、
_{2n}C_n を計算すればいい。つまり、(2n)! / (n!)^2 。

もうちょっと計算機っぽく考えると、nx x ny のグリッドの場合、最初に右に行くか下に行くかで、
f(nx,ny) = f(xn-1,ny) + f(xn,ny-1)
但し、
f(0,_) = f(_,0) = 1
を解けば良いが、最初の式は実は _nC_r = _{n-1}C_r + _{n-1}C_{r-1} の事である。
実際にはこのままではf(nx,ny)回の関数呼び出しが発生するので、メモ化する。

import scala.collection.mutable.HashMap
import java.math.BigInteger

object P015 {
val map:HashMap[Tuple2[Int,Int],Long] = new HashMap()
def f(nx:Int, ny:Int):Long = map.get(Tuple2(nx,ny)) match {
case Some(x) => x
case None => {
val z:Long = (nx,ny) match {
case (0,_) => 1
case (_,0) => 1
case (x,y) => f(x-1,y) + f(x,y-1)
}
val zz = map.put(Tuple2(nx,ny),z)
z
}
}
def main(args:Array[String]) {
println(f(20,20))
println(40L/20*39*38/19*37*36/18*35*34/17*33*32/16*31*30/15*29*28/14*27*26/13*25*24/12*23*22/11*21/10/9/8/7/6/5/4/3/2/1)
}
}

[Project Euler] Problem 14

Problem 14

 有名なCollatz問題。最初はListを使っていたのだがOutOfMemoryになるので普通に命令型っぽいプログラムになっている。

object P014 {
def collatz(n:Long):Int = {
def f(n:Long, c:Int):Int = {
if (n==1L) c
else if (n%2L==0) f(n/2, c+1)
else f(3L*n+1, c+1)
}
f(n,1)
}
def mx(a:Tuple2[Long,Int], b:Tuple2[Long,Int]):Tuple2[Long,Int] = if (a._2 > b._2) a else b
def maxLength(m:Int):Tuple2[Long,Int] = {
var ts:Tuple2[Long,Int] = Tuple2(0L,0)
for(x <- List.range(1, m)) {
val c:Int = collatz(x)
if (ts._2 < c) {ts = Tuple2(x,c)}
}
ts
}
def main(args:Array[String]) {
println(collatz(13L))
println(maxLength(1000000))
}
}

[Project Euler] Problem 13

Problem 13

文字列 -> BigInt の変換をして和を取るだけ。

import java.math.BigInteger

object P013 {
val input:Array[String] = Array(
"37107287533902102798797998220837590246510135740250",
...中略...
"53503534226472524250874054075591789781264330331690")
def main(args:Array[String]) {
val sum:BigInt = input.map{s => new BigInt(new BigInteger(s))}.foldLeft(new BigInt(BigInteger.ZERO))(_+_)
println(sum)
println(sum.toString.substring(0,10))
}
}

[Project Euler] Problem 12

Problem 12

 最初に素数列を作成するのに結構時間がかかる気がする。今回は最初10000以下の素数のリストを作り、エラーが出たので30000まで作った。
 約数の数は素因数分解して各素数の指数から求められるのを使う。

object P012 {
val maxRange = 30000
def primes(init:List[Int]):List[Int] = {
def min(a:Int, b:Int) = if (a<b) a else b
def sq(a:Int) = a*a
init ::: List.range(init.last+1, min(maxRange,sq(init.last))).filter{x => init.forall{x%_!=0}}
}
val primeList = primes(primes(List(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)))
val emptyFactors = primeList.map{x => 0}
def factor(n:Int):List[Int] = {
def f(x:Int, fs:List[Int], ps:List[Int]):List[Int] = (x,fs,ps) match {
case (1,_,_) => fs
case (_,Nil,_) => error("fs empty:x="+x)
case (_,_,Nil) => error("ps empty:x="+x)
case (y,a::as,b::bs) if y%b==0 => f(y/b, (a+1)::as, ps)
case (y,a::as,b::bs) => a::f(y,as,bs)
}
f(n, emptyFactors, primeList)
}
def numFactors(n:Int):Int = factor(n).foldLeft(1){(a,b) => a*(b+1)}
def triangle(n:Int):Int = n*(n+1)/2
def main(args:Array[String]) {
println(primeList.length)
println(numFactors(28))
val t:Tuple3[Int,Int,Int] =
Stream.range(1,maxRange).map{x => Tuple3(x,triangle(x),numFactors(triangle(x)))}.filter{x => x._3>500}.head
println(t)
println(factor(triangle(t._1)).zip(primeList).filter{t => t._1 != 0}.map{x => x._2+"^"+x._1})
}
}

18 October, 2008

[Project Euler] Problem 11

Problem 11

表をArray[Array[Int]]に変換、4つの並びを(x,y)のTuple2のListとして表現し、全組み合わせ ( List[List[Tuple2[Int,Int]]]) から積が最大になるものを選択する。

object P011 {
val input:Array[String] = Array(
"08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08",
"49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00",
"81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65",
"52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91",
"22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80",
"24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50",
"32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70",
"67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21",
"24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72",
"21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95",
"78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92",
"16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57",
"86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58",
"19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40",
"04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66",
"88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69",
"04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36",
"20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16",
"20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54",
"01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48")
val table:Array[Array[Int]] = input.map{x => x.split(" ").map{_.toInt}}
def v(x:Int, y:Int):Int = table(y)(x)
val xscan:List[List[Tuple2[Int,Int]]] =
for(y <- List.range(0,20); x0 <- List.range(0,20-4)
) yield for(x <- List.range(x0, x0+4)) yield (x,y)
val yscan:List[List[Tuple2[Int,Int]]] =
for(x <- List.range(0,20); y0 <- List.range(0,20-4)
) yield for(y <- List.range(y0, y0+4)) yield (x,y)
val diagscan1 =
for(x0 <- List.range(0,20-4); y0 <- List.range(0,20-4)
) yield for(i <- List.range(0,4)) yield (x0+i, y0+i)
val diagscan2 =
for(x0 <- List.range(0,20-4); y0 <- List.range(0,20-4)
) yield for(i <- List.range(0,4)) yield (x0+3-i, y0+i)
def f(ps:List[Tuple2[Int,Int]]):Long = ps.foldLeft(1L){(a,t) => a * v(t._1,t._2)}
val result = (xscan ::: yscan ::: diagscan1 ::: diagscan2).sort{(a,b) => f(a)>f(b)}.head
def main(args:Array[String]) {
println(result)
println(result.map{t => v(t._1,t._2)})
println(f(result))
}
}

[Project Euler] Problem 10

Problem 10

 2000000以下の素数を得るには1415以下の素数が判れば良く、1415以下の素数を得るには37以下の素数が判れば良く、37以下の素数を知るには7以下の素数が2,3,5,7であることを知っていればOKである。
 という訳で、エラトステネスの篩を三段重ねにしてみた。

object P010 {
def main(args:Array[String]) {
val maxRange = 2000000
def primes(init:List[Int]):List[Int] = {
def min(a:Int, b:Int) = if (a<b) a else b
def sq(a:Int) = a*a
init ::: List.range(init.last+1, min(maxRange,sq(init.last))).filter{x => init.forall{x%_!=0}}
}
println(new java.util.Date())
val ps1 = List(2,3,5,7)
val ps2 = primes(ps1)
println(ps2.last)
val ps3 = primes(ps2)
println(ps3.last)
val ps4 = primes(ps3)
println(ps4.last)
println(ps4.foldLeft(0L)(_+_))
println(new java.util.Date())
}
}

[Project Euler] Problem 9

Project EulerのProblem 9。実は簡単に手で解ける問題。

a^2 + b^2 = c^2 を満たす a,b,c は、一般性を失わずに、
a = m^2 - n^2, b= 2mn, c = m^2 + n^2 (m > n)
と書ける事が判っている。
a + b + c = 1000 より、m(m+n) = 500 となる。
500 の約数は、(1,2,4,5,10,20,25,50,100,125,250,500) で、
m+n > m, m > n を使うと、m = 20, n = 5。


object P009 {
def main(args:Array[String]) {
println(new java.util.Date)
val result = for(a <- List.range(1,1000);
b <- List.range(a,1000-a);
c = 1000 - a - b
if a*a + b*b == c*c
) yield (a,b,c,a*b*c)
println(result)
println(new java.util.Date)
}
}

[Project Euler] Problem 8

Project EulerのProblem 8

特に工夫せずに、文字列からList[Int]に変換、頭から要素を5つ取り出して、1文字ずらして次へ、という感じ。

object P008 {
def c2i(c:Char):Int = c - '0'
val data:List[Int] = {(
"73167176531330624919225119674426574742355349194934"+
"96983520312774506326239578318016984801869478851843"+
"85861560789112949495459501737958331952853208805511"+
"12540698747158523863050715693290963295227443043557"+
"66896648950445244523161731856403098711121722383113"+
"62229893423380308135336276614282806444486645238749"+
"30358907296290491560440772390713810515859307960866"+
"70172427121883998797908792274921901699720888093776"+
"65727333001053367881220235421809751254540594752243"+
"52584907711670556013604839586446706324415722155397"+
"53697817977846174064955149290862569321978468622482"+
"83972241375657056057490261407972968652414535100474"+
"82166370484403199890008895243450658541227588666881"+
"16427171479924442928230863465674813919123162824586"+
"17866458359124566529476545682848912883142607690042"+
"24219022671055626321111109370544217506941658960408"+
"07198403850962455444362981230987879927244284909188"+
"84580156166097919133875499200524063689912560717606"+
"05886116467109405077541002256983155200055935729725"+
"71636269561882670428252483600823257530420752963450")}.toCharArray().toList.map(c2i)
def mul(cs:List[Int]):Int = cs match {
case List(a,b,c,d,e) => a*b*c*d*e
case _ => -1
}
def f(max:Int, list:List[Int]):Int = {
if (list.length<5) max
else {
val a = mul(list.take(5))
if (a>max) f(a, list.tail)
else f(max, list.tail)
}
}
def main(args:Array[String]) {
println(new java.util.Date)
println(f(-1,data))
println(new java.util.Date)
}
}

[Project Euler] Problem 7

Project EulerのProblem 7

 Problem 3で作った素数列のStreamは10001個の素数を計算させようとするとOutOfMemoryで落ちてしまった。本当はちゃんと理由を考えるべきだよなぁ。計算を遅延しているのは確かめたし、あとは実装上で非常にメモリ効率が悪いとかそういうのなのかなぁ。うぅぅむ、Streamへの理解が足りない様だ。
 とりあえず奇素数10000個のListを作れば良いので、下記の様に済ませた。計算時間は自機で15秒程度。

object P007 {
def main(args:Array[String]) {
println(new java.util.Date())
val ps = primes(10000)
println(ps(0))
println(ps(9999))
println(new java.util.Date())
}
def primes(num:Int):List[Int] = {
def odds(n:Int):Stream[Int] = Stream.cons(n, odds(n+2))
def primes2(ps:List[Int], ss:Stream[Int]):List[Int] = {
if (ps.length >= num) ps
else {
val x = ss.head
if (ps.forall(x%_!=0)) primes2(x::ps, ss.tail) else primes2(ps, ss.tail)
}
}
primes2(Nil,odds(3))
}
}

[Project Euler] Problem 6

Project EulerのProblem 6

 本当は高校の数学を使って紙と鉛筆で解ける様な問題のはずなんだが、いざ解いてみようとすると案の定 k^2 の和の公式を忘れていて、k(k+1) の和の公式から作り直したりとか。手でこういう計算をしなくなってから長いからなぁ。

object P006 {
def p006(n:Int):Int = sq(sum(List.range(1,n+1))) - sum(List.range(1,n+1).map(sq))
def sum(xs:List[Int]):Int = xs.foldLeft(0)(_+_)
def sq(x:Int):Int = x*x
def main(args:Array[String]) {
println(new java.util.Date)
println(p006(10))
println(p006(100))
println(new java.util.Date)
println(f(100))
}
def f(n:Int):Int = sq(n*(n+1)/2) - n*(n+1)*(2*n+1)/6
}

[Project Euler] Problem 5

Project EulerのProblem 5

これはまぁ、1,2,...,20 の最小公倍数を求めれば良いだけの話。

object P005 {
def gcd(a:Long, b:Long):Long = {
if (a>b) gcd(b,a)
else if (a==0) b
else gcd(b%a, a)
}
def lcd(a:Long, b:Long):Long = a/gcd(a,b)*b
def p005(n:Int):Long = List.range(1, n+1).foldLeft(1L)(lcd(_,_))
def main(args:Array[String]) {
println(new java.util.Date)
println(p005(10))
println(p005(20))
println(new java.util.Date)
}
}

[Project Euler] Problem 4

Project EulerのProblem 4

 最初に作ったコードはは3桁 x 3桁の2重ループで積を求めて、それが回文数になっているかをチェックし、積の大きさでソート、という解法で解きました。
 まぁ計算時間が1分以内ならどんな解き方でも良い様なものですが、最大の回文数を1つだけ求めれば良いならば、逆に大きな回文数から順に3桁 x 3桁になるか試した方が無駄が無い様にも思ったので次の様なコードに。ここでもStreamを作ってheadで最初の要素だけ求めます。
 この手のパズル的な問題を解く時には遅延リストは便利ですね。Haskellで解けばもっと楽なのだろうなぁ...。

object P004 {
def main(args:Array[String]) {
time({println(p004)})
}
val palins:Stream[Int] =
for(x <- Stream.range(9,0,-1);
y <- Stream.range(9,-1,-1);
z <- Stream.range(9,-1,-1))
yield (100001*x + 10010*y + 1100*z)
def f(n:Int):Stream[Tuple3[Int,Int,Int]] = {
Stream.range(999,99,-1).filter{x => (n%x==0)&&(100<=n/x)&&(n/x<1000)}.map{x => (x,n/x,n)}
}
def p004:Tuple3[Int,Int,Int] = palins.flatMap(f).head
def time(block:Unit) {
val t0 = System.currentTimeMillis
block
println((System.currentTimeMillis-t0)+"msec")
}
}

17 October, 2008

[Project Euler] Problem 3

 Project EulerのProblem 3

 実のところ、素数列を求める必要は全く無く、単に奇数で順に割って行けば良いだけの問題だと思う。が、せっかくなのでStreamの使い方の勉強という事で、素数列のStreamを作る。

 Streamの使い方は、例えば下記の記事とかが参考になると思う。
 基本的にStreamを使いたいケースというのは、(1)パズル系の問題を解く為に無限数列を作りたい、(2)入力やファイルとかで必要があるまで読み込みたく無い、のどっちかの場合かと思う。勿論、Streamはリストの様に扱え便利なので、ループとかイテレータじゃなくて遅延リストに表現したいという事。
 (1)の場合は、a_n → a_{n+1} = f(a_n) の様に書けるならば、val s = Stream.cons(a0, s.map(f))と書けば良い。Problem 2のような二項漸化式の場合は、s.zip(s.tail)の様な(a_n, a_{n+1})なタプルを作って計算というのが定番。
 (2)の例としては、例えばSimplifying JDBC @ Scala Wikiの例が実用例。JDBCのResultSetをStream[X]に変換するコードとして、

private def strm[X](f: RichResultSet => X, rs: ResultSet): Stream[X] =
if (rs.next) Stream.cons(f(new RichResultSet(rs)), strm(f, rs))
else { rs.close(); Stream.empty };

の様に書かれている。rs.nextがtrueならばStreamの新しい要素xを作って、xとStreamの続きであるstrm(f,rs)自身のconsであるStream.cons(x, strm(f,rs))を返し、rs.nextがfalseになったらEmptyを返して終了。

 Problem 3のコードはこんな感じ。

object P003 {
def main(args:Array[String]) {
// println(primes.take(10).force) // -> List(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
// println(g(13195)) // -> List(29, 13, 7, 5)
time({println(g(600851475143L))})
}
val odds:Stream[Long] = Stream.cons(3, odds.map(_+2))
def sieve(xs:Stream[Long]):Stream[Long] = Stream.cons(xs.head, sieve(xs.tail.filter{_%(xs.head)!=0}))
val primes:Stream[Long] = Stream.cons(2, sieve(odds))
def f(n:Long, ps:Stream[Long], l:List[Long]):(Long,Stream[Long],List[Long]) = {
if (n==1) (n,ps,l)
else {
val m = ps.head
if (n%m==0) {
f(n/m, ps, m::l)
} else {
f(n, ps.tail, l)
}
}
}
def g(n:Long):List[Long] = f(n, primes, Nil)._3
def time(block:Unit) {
val t0 = System.currentTimeMillis
block
println((System.currentTimeMillis-t0)+"msec")
}
}

 Streamは実際に値が使われるまで計算されないので、デバッグプリントしたいときはtake(10)だけじゃなく、forceとかをつけないと駄目。
 oddsが奇数が無限に続くStreamで、sieveがエラトステネスの篩で素数だけにするフィルタ、primesが素数列です。あとこの問題は素因数分解の対象がIntじゃなくてLongでないと収まらない。最初はIntが駄目なのでBigIntで計算したけど実はLongなんてのもあったな、と計算時間を計るtimeを作っていて思い出したり。

[Project Euler] Problem 2

Project EulerのProblem 2

Fibonacci数列というと、やはり遅延ストリームで表現するのがお約束ということで、Streamの使い方の復習。
Fibonacci数列と素数列はなんかイディオムとして覚えてもいいかもしれない。それが理解出来ればStreamが自由に扱える様な気がする。

fibはlazy valで宣言しなくてもOKだったが、なぜOKなのかについては自信が無い。

object P002 {
val fib:Stream[Int] = Stream.cons(1, Stream.cons(2, (fib.zip(fib.tail)).map{x => x._1 + x._2}))
def main(args:Array[String]) {
println(fib.take(10)) // --> Stream(1, ?)
println(fib.take(10).force) // --> List(1, 2, 3, 5, 8, 13, 21, 34, 55, 89)
println(fib.filter{x => x%2==0}.takeWhile{x => x<100}.foldLeft(0)(_+_))
println(fib.filter{x => x%2==0}.takeWhile{x => x<4000000}.foldLeft(0)(_+_))
}
}

なんかHaskellで書いたコードに比べると冗長な感じがする。Listと違ってStreamはシンタックスシュガーが弱いから仕方が無いが。

16 October, 2008

[Project Euler] Problem 1

Project EulerをScalaで挑戦してみることにしました。

 Project Eulerってのは数学っぽいプログラミングの問題が出題されているサイトです。「どう書く.org」の数学問題とかに割と近い。特に数学を専攻した人でなくても解けるし、コンピュータでの計算時間も賢くプログラミング出来れば1分以内ぐらいだそうです。
 問題は英語で出題されているけど、和訳サイトもあります。

 アカウントを作ってログインし、profileの画面で解いた問題を選択し、正しい答えを入力すると、その問題は回答済みとなります。とりあえずの目標は25問解いてLevel 1に昇格すること。200問解くとLevel 5なんだが、78人しかいない様子。

 Project Eulerの良いところは、Webサイトに答えを入力すると、正しいか間違っているかが判ることです。現実の問題は予め正解が判っている訳ではないのが、予め正解の判っている問題を解く学校の勉強と違うところだ、というのは良く聞く話ですが、逆に言うと正解の判らない問題を解くのは勉強方法としては効率が悪い訳です。その点でProject Eulerは勉強用に向いています。
 関数型言語(に限らずにCとかJavaとかプログラミング言語一般でも良いと思いますが)の入門用の例題としては良いのではないでしょうか。

 出来るだけ毎日1題づつ解いて行こうと思っています。

 Problem 1のコードはこんな感じで簡単に解けた。

object P001 {
def main(args:Array[String]) {
println(List.range(1,10).filter{x => (x%3==0)||(x%5==0)}.foldLeft(0)(_+_))
println(List.range(1,1000).filter{x => (x%3==0)||(x%5==0)}.foldLeft(0)(_+_))
}
}

 List.rangeで1, 2,..., 9という数値のリストを作り、filterで3または5の倍数を選択し、foldLeftで合計を求めます。まぁ割とScalaとか関数型言語での定番のイディオムなんじゃなかろうか。
 解法としては勿論、受験数学的に数列の和の公式とかを使って解いてもOKなんだけど、計算時間が1分以内ならばどんな解き方でもOKと考えることにして、素直に実装した。

27 September, 2008

[Scala] Web Flavor

Web Flavorの0.2.0が公開されたので動かしてみました。
Web Flavorは、

  • Scalaコードをスクリプトとして記述してすぐ実行できる
  • 更新されれば自動的にコンパイルされる
という機能を備えたWebフレームワークだそうです。

●動かし方。

 とりあえず何も考えずに、WebFlavor-samples-0.2.0.zip (Apache Tomcat入り実行パッケージ、ソースなし)をダウンロードしてみる。

 私の場合、Mac OS Xだが、~/work にファイルを置いて zip ファイルを解凍。~/work/WebFlavor-samples/ というディレクトリが作られた。

[~/work/WebFlavor-samples] % ls
Apache-Tomcat-LICENSE.txt* logs/
Apache-Tomcat-RELEASE-NOTES* startup.bat*
LICENSE.txt* startup.sh*
Scala-LICENSE.txt* temp/
bin/ webapps/
conf/ work/
lib/
[~/work/WebFlavor-samples] %

startup.sh を起動すると実はこれだけでサーバが立ち上がる。

[~/work/WebFlavor-samples] % ./startup.sh
Using CATALINA_BASE: /Users/miyamoto/work/WebFlavor-samples
Using CATALINA_HOME: /Users/miyamoto/work/WebFlavor-samples
Using CATALINA_TMPDIR: /Users/miyamoto/work/WebFlavor-samples/temp
Using JRE_HOME: /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
2008/09/27 1:38:24 org.apache.catalina.startup.ClusterRuleSetFactory getClusterRuleSet
情報: Unable to find a cluster rule set in the classpath. Will load the default rule set.
2008/09/27 1:38:24 org.apache.catalina.startup.ClusterRuleSetFactory getClusterRuleSet
情報: Unable to find a cluster rule set in the classpath. Will load the default rule set.
2008/09/27 1:38:24 org.apache.catalina.core.AprLifecycleListener init
情報: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
2008/09/27 1:38:24 org.apache.coyote.http11.Http11Protocol init
情報: Initializing Coyote HTTP/1.1 on http-8080
2008/09/27 1:38:24 org.apache.catalina.startup.Catalina load
情報: Initialization processed in 431 ms
2008/09/27 1:38:24 org.apache.catalina.core.StandardService start
情報: Starting service Catalina
2008/09/27 1:38:24 org.apache.catalina.core.StandardEngine start
情報: Starting Servlet Engine: Apache Tomcat/6.0.18
CLASS_PATH: /Users/miyamoto/work/WebFlavor-samples/webapps/webflavor/WEB-INF/lib/scala-compiler.jar:/Users/miyamoto/work/WebFlavor-samples/webapps/webflavor/WEB-INF/lib/scala-library.jar:/Users/miyamoto/work/WebFlavor-samples/webapps/webflavor/WEB-INF/lib/webflavor.jar:/Users/miyamoto/work/WebFlavor-samples/webapps/webflavor/WEB-INF/classes::/Users/miyamoto/work/WebFlavor-samples/bin/bootstrap.jar
2008/09/27 1:38:25 org.apache.coyote.http11.Http11Protocol start
情報: Starting Coyote HTTP/1.1 on http-8080
2008/09/27 1:38:25 org.apache.jk.common.ChannelSocket init
情報: JK: ajp13 listening on /0.0.0.0:8009
2008/09/27 1:38:25 org.apache.jk.server.JkMain start
情報: Jk running ID=0 time=0/54 config=null
2008/09/27 1:38:25 org.apache.catalina.startup.Catalina start
情報: Server startup in 605 ms


●サンプルを表示させてみる

http://localhost:8080/webflavor/ にアクセスすると、トッブページが表示される。

リンクを辿って、Echo (samples/Echo.scala) を表示させてみる。コンテキストルートの /webflavor/下と、/WEB-INF/src/下とがそのまま対応していることが判る。
Source = ~/work/WebFlavor-samples/webapps/webflavor/WEB-INF/src/samples/Echo.scala
URL = http://localhost:8080/webflavor/samples/Echo.scala

Eco.scala は、表示したいHTMLをXMLリテラルとして返す様なscalaのコード片(Flavorと呼ぶ)で、JSPみたいなもの。
内部的には前後にコードを付加する事で(context: Context, request: Request, response: Response, session: collection.mutable.Map[String,Object]) => AnyRef なクラスに書き換えられる。

●管理画面からFlavorを作ってみる

トップページ http://localhost:8080/webflavor/ からリンクを辿って管理画面 http://localhost:8080/webflavor/admin/ に入る。
何も設定を変更してなければ、~/work/WebFlavor-samples/conf/tomcat-users.xml をみれば管理者ユーザ webflavor のパスワードが判るのでそれを使ってログインすればOK。

簡単な例として全く芸のない話だがDate.scalaというのを作ってみる。
フォームにDate.scala を入力して create ボタンを押すと、Date.scalaのソースコード入力ページになるので下記を入力して、

// Title
val title = "Date"

// Return output XML elements.
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{"Time="+(new java.util.Date()).toString()}</h1>
</body>
</html>

updateボタンでサーブして、実行するにはExecuteのリンクを押せば、http://localhost:8080/webflavor/Date.scala として、現在時刻が表示されるのが見える。

●感想

 手軽にscalaで動的ページを作成出来ることが判った。例えばscalaを教育用言語に使う場合とかには、初心者が簡単にWebアプリを書けたりなど教材として良いのではないかなぁ。
 

30 August, 2008

[Scala] How to Use Combinator Parser (3)

Scala の Parser Combinator の使い方の勉強 (続き)

 今回は小ネタ。

 例えばXMLとかでタグが対応していたりとか、(La)TeXのenvironmentなどの¥bigin{xxx}...¥end{xxx}のように、ある範囲の最初と最後に同じ文字列が出てくることを要請したい場合。こんな感じで実現出来る。

 例えばXML風のタグの対応を実装する場合、

def tagged:Parser[Tagged] =
"<"~>ident~">"~tkn~"</"~ident<~">" ^? {
case i~_~t~_~j if i==j => Tagged(i,t)
}

 a~>b, c<~d というのは、a~b, c~d と同じ様なものだが、a, d の結果を使用しない場合に使えるscala.util.parsing.combinator.Parsers.Parserのメソッド。今回の場合は最初の"<"と最後の">"はマッチさせても後で使わないので。但し、~と違って、a~b~c~... と繋いで結果のcaseを作れないので、"</"とかは~で前後を繋いで、caseでは_を使って結果を使わない事を示してみた。
 ^? というのは ^^ と同じ様なものだが、^^ は case にマッチする事が必要。^? の場合、caseのマッチで失敗した場合にはパース失敗と出来る。今回は i!=j になる場合は失敗させるため、^? を使う。

 全体のコードはこんな感じ。(...しかし不等号をblogに書くのはいちいちエスケープするのが面倒だった...)

package test;

import scala.util.parsing.combinator._

abstract class Tkn
case class Lit(s:String) extends Tkn
case class Tagged(tag:String, t:Tkn) extends Tkn
object Test3 extends JavaTokenParsers {
def tagged:Parser[Tagged] =
"<"~>ident~">"~tkn~"</"~ident<~">" ^? {
case i~_~t~_~j if i==j => Tagged(i,t)
}
def tkn:Parser[Tkn] =
( tagged
| ident ^^ {case s => Lit(s)}
)
def main(args:Array[String]) {
val s = "<aaa><bbb>ccc</bbb></aaa>"
println(s)
println(parse(tagged, s))
}
}

結果は下記の様な感じ

結果1
<aaa><bbb>ccc</bbb></aaa>
[1.26] parsed: Tagged(aaa,Tagged(bbb,Lit(ccc)))

---
結果2
<aaa><bbb>ccc</bbbb></aaa>
[1.21] failure: Constructor function not defined at ((((bbb~>)~Lit(ccc))~</)~bbbb)

<aaa><bbb>ccc</bbbb></aaa>
---
結果3
<aaa><bbb>ccc</bbb></aaaa>
[1.27] failure: Constructor function not defined at ((((aaa~>)~Tagged(bbb,Lit(ccc)))~</)~aaaa)

<aaa><bbb>ccc</bbb></aaaa>

26 August, 2008

[Scala] How to Use Combinator Parser (2)

Scala の Parser Combinator の使い方の勉強 (続き)

以下は Scala 2.7.1 の元での話です。

●数式のパーサを書いてみる (続き)

 前回の記事[Scala] How to Use Combinator Parser (1)で、使い方練習として定番の数式パーサを書きました。
 その際に、パーサコンビネータでは左再帰が苦手なので
expr ::= expr "+" term | expr "-" term | term

を、繰り返し
expr ::= term { ("+" | "-") term}

と変形して、それをそのままにコードに直しました。

 実は、用意されている関数 chainl1 を使ってもうちょっと奇麗に処理する事が出来ます。右結合性の演算子に対しては chainr1 を同じ様に使えばいいはず。

 まず、構文木のノード型を定義します。

abstract class Expr {
def eval:Int
}
case class Add(e:Expr,t:Term) extends Expr {
def eval = e.eval + t.eval
}
case class Sub(e:Expr,t:Term) extends Expr {
def eval = e.eval - t.eval
}
abstract class Term extends Expr {
def eval:Int
}
...

と書きます。
 Term は単独で Expr になる事が出来るから、Term が Expr のサブクラスになります。
 Expr のサブクラスの case class として Add, Sub を定義します。
 ここまではいいですよね。

 さて、chainl1 は、
def chainl1 [T, U](first : => Parser[T], p : => Parser[U], q : => Parser[(T, U) => T]) : Parser[T]

というメソッドです。これは、first { q p } にマッチするパーサみたいなもの。
 我々の場合だと、Expr をパースするパーサ def expr:Parser[Expr] が作りたいのだから、T = Expr です。
 expr (+|-) term => expr を表現したいのだから、U = term で、p = term:Parser[Term] です。
 q は "+", "-" とかの演算子の部分ですが、(Expr,Term) => Expr という関数を返すパーサです。つまり、(Expr,Term) => Add の様な関数を返せば OK。
 first をどうするかですが、ここに T = expr だから expr と書くと無限ループになって NG。正解は term を指定します。これで term { q term } になりますね。
 これをコードに直すと、

def add = "+" ^^ {case a => ((e:Expr,t:Term)=>Add(e,t))}
def sub = "-" ^^ {case a => ((e:Expr,t:Term)=>Sub(e,t))}
def expr:Parser[Expr] = chainl1(term, term, (add | sub))

となります。

 term についても同様に考えれば OK で、完成したコードは下記の様。

package test;

import scala.util.parsing.combinator._

abstract class Expr {
def eval:Int
}
case class Add(e:Expr,t:Term) extends Expr {
def eval = e.eval + t.eval
}
case class Sub(e:Expr,t:Term) extends Expr {
def eval = e.eval - t.eval
}
abstract class Term extends Expr {
def eval:Int
}
case class Mul(t:Term,f:Factor) extends Term {
def eval = t.eval * f.eval
}
case class Div(t:Term,f:Factor) extends Term {
def eval = t.eval / f.eval
}
abstract class Factor extends Term {
def eval:Int
}
case class Parens(e:Expr) extends Factor {
def eval = e.eval
}
case class Num(n:Int) extends Factor {
def eval = n
}
class ExprParser2 extends JavaTokenParsers {
def add = "+" ^^ {case a => ((e:Expr,t:Term)=>Add(e,t))}
def sub = "-" ^^ {case a => ((e:Expr,t:Term)=>Sub(e,t))}
def expr:Parser[Expr] = chainl1(term, term, (add | sub))
def mul = "*" ^^ {case a => ((t:Term,f:Factor)=>Mul(t,f))}
def div = "/" ^^ {case a => ((t:Term,f:Factor)=>Div(t,f))}
def term:Parser[Term] = chainl1(factor, factor, (mul | div))
def factor:Parser[Factor] =
( wholeNumber ^^ {case s => Num(s.toInt)}
| "("~expr~")" ^^ {case a~b~c => Parens(b)}
)
}
object Test2 extends ExprParser2 {
def main(args:Array[String]) {
val s = "1 * (2 * 3) - 4*5+(6*7 + 8*9)"
println(s)
println(parse(expr, s))
println(parse(expr, s).get.eval) // -> 100
}
}

実行すると、

1 * (2 * 3) - 4*5+(6*7 + 8*9)
[1.30] parsed: Add(Sub(Mul(Num(1),Parens(Mul(Num(2),Num(3)))),Mul(Num(4),Num(5))),Parens(Add(Mul(Num(6),Num(7)),Mul(Num(8),Num(9)))))
100

23 August, 2008

[Scala] How to Use Combinator Parser (1)

Scala の Parser Combinator の使い方の勉強 (1)

以下は Scala 2.7.1 の元での話です。

●数式のパーサを書いてみる

使い方練習という事で、整数の括弧有り四則演算の出来るパーサを書くことにします。
scala.util.parsing.combinator.JavaTokenParsers を元に作成するのが簡単です。なぜならば、

  • 識別子(大文字+小文字+数字+'_'からなる文字列)、数(整数、実数、指数形式の実数)、文字列リテラル("..."な文字列でUnicode対応)のパーサが予め用意されている。

  • 空白文字を自動的に読み飛ばす機能付き。
だから。

パーサクラスを、

import scala.util.parsing.combinator._

class ExprParser1 extends JavaTokenParsers {
...
}
と宣言して、...の部分を考えることにします。

まず数式の(E)BNF文法ですが、

<expr> ::= <expr> "+" <term> | <expr> "-" <term> | <term>
<term> ::= <term> "*" <factor> | <term> "/" <factor> | <factor>
<factor> ::= "(" <expr> ")" | <number>
と書くと、左再帰でパーサコンビネータでは都合が悪い(expr の解析で expr を呼んで...を繰り返しすぐにstack overflowする)ので、(以下、HTMLでは面倒なので < > は省略)

expr ::= term { ("+" | "-") term}
term ::= factor { ("*" | "/") factor}
と書き直す事にします。

expr ::= term { ("+" | "-") term}
の部分をどうするかですが、

case class OpTerm(op:String, t:Term) { // op = "+" or "-"
def eval(n:Int):Int = op match {
case "+" => n + t.eval()
case "-" => n - t.eval()
case _ => error(this.toString)
}
}
case class Expr(t:Term, ots:List[OpTerm]) {
def eval():Int = ots.foldLeft(t.eval()){(n:Int, ot:OpTerm) => ot.eval(n)}
}
def opTerm:Parser[OpTerm] = ("+" | "-")~term ^^ {case a~b => OpTerm(a,b)}
def expr:Parser[Expr] = term~rep(opTerm) ^^ {case a~b => Expr(a,b)}
と、構文木のノード型を定義し、パーサ関数を定義します。
 関数 opTerm は ("+" | "-" ) term の部分を表現しています。JavaTokenParsersでは、文字列 "+" は「文字列 "+" にマッチするパーサ」に暗黙の型変換がされます。"~" は、あるパーサにマッチした後に別のパーサにマッチすることを表します。"^^" は、 パーサがマッチした後の値を与える関数を書きます。"a~b" は実は case classで、a には("+" | "-")のマッチした結果 (文字列 "+" か "-") が入り、b には term のマッチした結果 (型 Term の値) が入ってます。
 関数 expr は term { opTerm } を表現します。rep(opTerm) で、opTerm の0回以上の繰り返しに対応するパーサになり、opTerm の繰り返しなので List[OpTerm] を与えるパーサとなります。
 case class の OpTerm, Expr の eval メソッドについては説明は省略。

 term ::= factor { ("*" | "/") factor } についても同様に書けるので説明は省略。

 factor ::= "(" expr ")" | number については下記の様に書けます。

def factor:Parser[Factor] =
( wholeNumber ^^ {case s => Num(s.toInt)}
| "("~expr~")" ^^ {case a~b~c => Parens(b)}
)
abstract class Factor {
def eval():Int
}
case class Num(n:Int) extends Factor {
def eval():Int = n
}
case class Parens(e:Expr) extends Factor {
def eval():Int = e.eval()
}

 wholeNumber は、JavaTokenParsersで定義されている、整数を表す文字列にマッチするパーサ。
"("~expr~")" ^^ ... の部分は見当がつくと思います。その2つを "|" で繋いでいます。

パーサを使う部分はこんな感じで書けます。

object Test1 extends ExprParser1 {
def main(args:Array[String]) {
val s = "1 * (2 * 3) - 4*5+6*7 + 8*9"
println(s)
println(parse(expr, s))
println(parse(expr, s).get.eval) // -> 100
}
}
作成したクラス ExprParser1 のメソッド parse に、パースしたい関数 (我々の場合は expr )と、入力 (String以外にjava.io.ReaderなどもOK) を渡します。得られたParseResult[Expr] のメソッド get で Expr 型の結果を得て、eval すれば OK です。

実はもっと要領よく書けるはずですが、判りやすさを優先して書いてみました。

全体のソースは下記の通り。

package test;

import scala.util.parsing.combinator._
// BNF with Left Recursion
// expr ::= expr + term | expr - term | term
// term ::= term * factor | term / factor | factor
// factor ::= ( expr ) | number
//
// BNF without Left Recursion
// expr ::= term { (+|-) term }
// term ::= factor { (*|/) factor }
// factor ::= ( expr ) | number

class ExprParser1 extends JavaTokenParsers {
def opTerm:Parser[OpTerm] = ("+" | "-")~term ^^ {case a~b => OpTerm(a,b)}
def expr:Parser[Expr] = term~rep(opTerm) ^^ {case a~b => Expr(a,b)}
def opFactor:Parser[OpFactor] = ("*" | "/")~factor ^^ {case a~b => OpFactor(a,b)}
def term:Parser[Term] = factor~rep(opFactor) ^^ {case a~b => Term(a,b)}
def factor:Parser[Factor] =
( wholeNumber ^^ {case s => Num(s.toInt)}
| "("~expr~")" ^^ {case a~b~c => Parens(b)}
)
case class OpTerm(op:String, t:Term) { // op = "+" or "-"
def eval(n:Int):Int = op match {
case "+" => n + t.eval()
case "-" => n - t.eval()
case _ => error(this.toString)
}
}
case class Expr(t:Term, ots:List[OpTerm]) {
def eval():Int = ots.foldLeft(t.eval()){(n:Int, ot:OpTerm) => ot.eval(n)}
}
case class OpFactor(op:String, f:Factor) { // op = "*" or "/"
def eval(n:Int):Int = op match {
case "*" => n * f.eval()
case "/" => n / f.eval()
case _ => error(this.toString)
}
}
case class Term(f:Factor, ofs:List[OpFactor]) {
def eval():Int = ofs.foldLeft(f.eval()){(n:Int, of:OpFactor) => of.eval(n)}
}
abstract class Factor {
def eval():Int
}
case class Num(n:Int) extends Factor {
def eval():Int = n
}
case class Parens(e:Expr) extends Factor {
def eval():Int = e.eval()
}
}
object Test1 extends ExprParser1 {
def main(args:Array[String]) {
val s = "1 * (2 * 3) - 4*5+6*7 + 8*9"
println(s)
println(parse(expr, s))
println(parse(expr, s).get.eval) // -> 100
}
}


結果:

1 * (2 * 3) - 4*5+6*7 + 8*9
[1.28] parsed: Expr(Term(Num(1),List(OpFactor(*,Parens(Expr(Term(Num(2),List(OpFactor(*,Num(3)))),List()))))),List(OpTerm(-,Term(Num(4),List(OpFactor(*,Num(5))))), OpTerm(+,Term(Num(6),List(OpFactor(*,Num(7))))), OpTerm(+,Term(Num(8),List(OpFactor(*,Num(9)))))))
100

29 July, 2008

[Scala] Scala exercises for beginners

初心者の為の練習問題

 原文はScala exercises for beginnersを参照の事。
 関数型言語の初心者向けの良い課題だと思うのだが。...が、関数型言語に不慣れだと、そもそもどんな関数を作る事が期待されているのか判んないかもしれないなぁ。(メソッド名とか関数の型から大体の見当がつくかな?)
 ソースコード中の error("課題") の部分を自分の書いたコードで置き換える事が期待されています。
 初心者の回答を自動で採点する為に、scalacheck で回答の正当性を検証する為の方法を誰か解説しない?


// 下記の List のメソッドは使用してはならない:
// * length
// * map
// * filter
// * ::: (および ++ のようなその変形)
// * flatten
// * flatMap
// * reverse (および reverseMap, reverse_::: のような変形)
// これはまた、List に対する for-構文の使用も禁止している。
// 自分で書いた関数は使用して良い。例えば問題 2 では問題 1 あるいは問題 3 を使用して良い。
// 許可された既に存在するメソッドを適切に使用した場合は、エレガントさが評価される。
// 満点: 66点
object Exercises {
def succ(n: Int) = n + 1
def pred(n: Int) = n - 1
// Exercise 1 (問題 1)
// Relative Difficulty 1 (難易度: 1)
// Correctness: 2.0 (正しい回答に: 2.0 点)
// Performance: 0.5 (性能: 0.5 点)
// Elegance: 0.5 (エレガントさ: 0.5 点)
// Total: 3 (合計: 3)
def add(x: Int, y: Int): Int = error("課題: x, yは 0 または正の数と仮定せよ。Int に対する +, - の使用を禁止する。上述の succ/pred の使用のみ許す。")
// Exercise 2
// Relative Difficulty: 2
// Correctness: 2.5 marks
// Performance: 1 mark
// Elegance: 0.5 marks
// Total: 4
def sum(x: List[Int]): Int = error("課題")
// Exercise 3
// Relative Difficulty: 2
// Correctness: 2.5 marks
// Performance: 1 mark
// Elegance: 0.5 marks
// Total: 4
def length[A](x: List[A]): Int = error("課題")
// Exercise 4
// Relative Difficulty: 5
// Correctness: 4.5 marks
// Performance: 1.0 mark
// Elegance: 1.5 marks
// Total: 7
def map[A, B](x: List[A], f: A => B): List[B] = error("課題")
// Exercise 5
// Relative Difficulty: 5
// Correctness: 4.5 marks
// Performance: 1.5 marks
// Elegance: 1 mark
// Total: 7
def filter[A](x: List[A], f: A => Boolean): List[A] = error("課題")
// Exercise 6
// Relative Difficulty: 5
// Correctness: 4.5 marks
// Performance: 1.5 marks
// Elegance: 1 mark
// Total: 7
def append[A](x: List[A], y: List[A]): List[A] = error("課題")
// Exercise 7
// Relative Difficulty: 5
// Correctness: 4.5 marks
// Performance: 1.5 marks
// Elegance: 1 mark
// Total: 7
def concat[A](x: List[List[A]]): List[A] = error("課題")
// Exercise 8
// Relative Difficulty: 7
// Correctness: 5.0 marks
// Performance: 1.5 marks
// Elegance: 1.5 mark
// Total: 8
def concatMap[A, B](x: List[A], f: A => List[B]): List[B] = error("課題")
// Exercise 9
// Relative Difficulty: 8
// Correctness: 3.5 marks
// Performance: 3.0 marks
// Elegance: 2.5 marks
// Total: 9
def maximum(x: List[Int]): Int = error("課題")
// Exercise 10
// Relative Difficulty: 10
// Correctness: 5.0 marks
// Performance: 2.5 marks
// Elegance: 2.5 marks
// Total: 10
def reverse[A](x: List[A]): List[A] = error("課題")
}

[Scala] 2 not problem

3 not problem@ヒビルテ経由で知ったパズル。
3 not problem@パラメトロン計算機、参照の事。

 で、Scalaで解いてみた。
 最初はお約束としてNodeのcase classとしてAnd, Orとか作ってevalをパターンマッチとかで書いていたんだけど、それだと答えが出るのに30分ぐらい必要だったので、出力の組み合わせ8通りをList[Boolean]で持たせてみた結果、5分ぐらいで答えが出る様になった。多分、8bitなのでIntで表現すればもっと速くなるはずだが。
 もっと賢く解く方法がありそうだが、まぁ解けたのでこれで良しとする。


// Brute-force Solver for "The 2-NOTs problem"
// "Synthesize a black box which computes NOT-X, NOT-Y, and NOT-Z from X, Y, and Z,
// using an arbitrary number of ANDs and ORs, but only 2 NOTs."
// See http://www.inwap.com/pdp10/hbaker/hakmem/boolean.html#item19
//
object TwoNots {
// Possible combination of (X,Y,Z)
val input:List[(Boolean,Boolean,Boolean)] =
for(x <- List(false, true);
y <- List(false, true);
z <- List(false, true)) yield (x,y,z)
// Node : input=(X:Boolean, Y:Boolean, Z:Boolean) => output:Boolean
// output : Node value for "input"
// desc : Description
case class Node(val output:List[Boolean], val desc:String) {
override def toString():String = desc
def ===(that:Any) = that match {
case Node(o, _) => output==o
case _ => false
}
def =/=(that:Any) = !(this === that)
def unary_~ :Node = Node(output.map{b:Boolean => !b}, "~["+this.desc+"]")
def zipWithF(that:Node, f:(Boolean,Boolean)=>Boolean):List[Boolean] = (output zip that.output).map{t => f(t._1, t._2)}
def *(that:Node):Node = Node(zipWithF(that, (_ && _)), this.desc + that.desc)
def +(that:Node):Node = Node(zipWithF(that, (_ || _)), "("+this.desc +"+"+ that.desc+")")
def memberOf_?(ns:List[Node]):Boolean = ns.exists(_ === this)
def const_?():Boolean = (this === TrueNode) || (this === FalseNode)
}
val TrueNode:Node = Node(input.map{in => true}, "T")
val FalseNode:Node = Node(input.map{in => false}, "F")
val X:Node = Node(input.map{in => in._1}, "X")
val Y:Node = Node(input.map{in => in._2}, "Y")
val Z:Node = Node(input.map{in => in._3}, "Z")
val notX:Node = Node((~X).output, "~X")
val notY:Node = Node((~Y).output, "~Y")
val notZ:Node = Node((~Z).output, "~Z")
val XYZ:Node = Node((X*Y*Z).output, "XYZ")
val X_Y_Z:Node = Node((X+Y+Z).output, "(X+Y+Z)")
val XY_YZ_ZX:Node = Node((X*Y+Y*Z+Z*X).output, "(XY+YZ+ZX)")

def append(ns:List[Node], n:Node):List[Node] = if (n.memberOf_?(ns) || n.const_?()) ns else n::ns
def append(ns:List[Node], xs:List[Node]):List[Node] = xs.foldLeft(ns){(as:List[Node], b:Node) => append(as,b)}
// Fixed point of f1,f2,... in fs
def fixedPoint(ns:List[Node], fs:List[List[Node] => List[Node]]):List[Node] = {
val ns1 = fs.foldLeft(ns){(as:List[Node], f:List[Node] => List[Node]) => append(as, f(as))}
if (ns1.length==ns.length) ns else fixedPoint(ns1, fs)
}
def spanAnd(nodes:List[Node]):List[Node] = for(na <- nodes; nb <- nodes if (na =/= nb)) yield (na * nb)
def spanOr(nodes:List[Node]):List[Node] = for(na <- nodes; nb <- nodes if (na =/= nb)) yield (na + nb)
def spanWithAndOr(nodes:List[Node]):List[Node] = fixedPoint(nodes, List(spanAnd, spanOr))
def chooseNodeForNot(nodes:List[Node]):List[List[Node]] = for(n <- nodes) yield append(nodes, ~n)
def satisfy_?(nodes:List[Node]):Boolean = notX.memberOf_?(nodes) && notY.memberOf_?(nodes) && notZ.memberOf_?(nodes)
// Solving tactics:
// (1) Start with initial nodes ( = initialNodes)
// (2) Take any two nodes from initial nodes, and make AND and OR of them to add to lists. Repeat this procedure so that
// all possible combinations have been added. ( = spanWithAndOr(...) = n0)
// n0 contains no NOTs.
// (3) Choose one node from n0, and add NOT of the chosen node to n0. Span the nodes list with ANDs and ORs. ( = n1)
// n1 contains one NOT.
// (4) Do same as above to obtain node list "n2", which contains two NOTs. If the n2 contains all of ~X, ~Y, and ~Z,
// the n2 is a solution.
def solve(initialNodes:List[Node]):List[(List[Node],List[Node],List[Node])] = {
val n0 = spanWithAndOr(initialNodes)
for(n1 <- chooseNodeForNot(n0).map{ns:List[Node] => spanWithAndOr(ns)};
n2 <- chooseNodeForNot(n1).map{ns:List[Node] => spanWithAndOr(ns)} if satisfy_?(n2)) yield
(n2.filter(_ === notX), n2.filter(_ === notY), n2.filter(_ === notZ))
}
def main(args : Array[String]) : Unit = {
println("----"+(new java.util.Date()))
// In principle, we can solve with initial=List(X,Y,Z), however, the answer becomes quite
// ugly because X+Y+Z might be expressed as (X+Y)+Z. In order to see answers expressed in
// a symmetric way, some nodes such as XYZ, XY+YZ+ZX, and X+Y+Z are added to initial nodes.
val initial:List[Node] = List(XYZ, XY_YZ_ZX, X_Y_Z, X, Y, Z)
// val initial:List[Node] = List(X, Y, Z)
for(answer <- solve(initial)) {
println("~X = "+answer._1)
println("~Y = "+answer._2)
println("~Z = "+answer._3)
}
println("----"+(new java.util.Date()))
}
}

13 April, 2008

[Scala]"Scala By Example" 翻訳中

ScalaByExample和訳 @ プログラミング言語 Scala Wiki

 Scala By Example (PDF) の和訳を Wiki 上で開始しました。
 現時点で Chapter 4 がほぼ完了。最終的に翻訳が終わった段階で LaTeX -> PDF 化したいと思っていますが、とりあえずは Wiki 上で英文和文の対訳形式で皆様に翻訳のレヴューを行って頂ければと思います。
 Wiki 上で編集するかコメント欄にご指摘を頂ければと思います。

15 March, 2008

[Scala] Re: "A Scala Tutorial for Java Programmer" 翻訳PDF版

A Scala Tutorial for Java Programmers 和訳PDF

翻訳用に使っていたWikiでご指摘頂いた、翻訳文の間違いを修正したPDFを作り直して差し替えました。URLは以前と変わっていません。

ご指摘を下さった、mko様、どうもありがとうございました。