2013年2月9日土曜日

Groovyでメソッドの引数に型を指定する意味

強い動的型付け言語であるGroovy。
JVM上で動く言語でありながら、まるでPHPの様にサクサクコーディングが出来る素敵な言語です。
強い動的型付けである為、型の宣言をする必要が無く、ソースがスッキリします。
ただし、PHP等のスクリプト言語における動的型付けと少し異なるのが、
ただし型の宣言は省略出来るのであって、宣言出来ない訳ではありません。つまり、
「型の指定はしなくても良いけど、する事も出来る」
という訳です。
じゃあ型を指定する場合は一体何が嬉しいの?という点を今回纏めてみました。
以下早速サンプルソースです。動的型付け言語の特徴として、コンパイル時点ではなく、実行時点でなければエラーの検出が出来ないという特徴を活かして説明しています。
*2012/02/12 追記:PHPでは仮引数でクラスやインタフェースを指定する事が出来ます。

型を指定しない場合
def test(a){
println a;
a = a.toLong() //Stringが渡されるとこの時点で例外が発生
println a.getClass()
}

test(1)
test("a")

以下、コンパイルと実行
[koji:groovy]$ groovyc type_check.groovy
[koji:groovy]$ groovy type_check
1
class java.lang.Long
a
Caught: java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "a"
at type_check.test(type_check.groovy:4)
at type_check$test.callCurrent(Unknown Source)
at type_check.run(type_check.groovy:10)

test(1)は問題なく実行され、test("a")はメソッドの途中まで実行されて、toLong()を実行するタイミングで例外が発生しています。
コレはString型にはtoLong()メソッドが存在しない為です。


型を指定する場合
def test(Integer a){
        println a;
        a = a.toLong()
        println a.getClass()
}

test(1)
test("a") //そもそもStringをとるtestメソッドが存在しないのでこの時点で例外が発生

以下、コンパイルと実行
[koji:groovy]$ groovyc type_check.groovy
[koji:groovy]$ groovy type_check
1
class java.lang.Integer
Caught: groovy.lang.MissingMethodException: No signature of method: type_check.test() is applicable for argument types: (java.lang.String) values: [a]
Possible solutions: test(java.lang.Integer), getAt(java.lang.String), wait(), use([Ljava.lang.Object;), wait(long), is(java.lang.Object)
groovy.lang.MissingMethodException: No signature of method: type_check.test() is applicable for argument types: (java.lang.String) values: [a]
Possible solutions: test(java.lang.Integer), getAt(java.lang.String), wait(), use([Ljava.lang.Object;), wait(long), is(java.lang.Object)
at type_check.run(type_check.groovy:10)
[koji:groovy]$

test(1)は問題なく実行されていますが、こちらの場合はtest("a")を実行したタイミングで例外が発生しています。
コレはtest()というメソッド名でStringを受け取るメソッドが存在しないために発生しているエラーです。

ざっくりまとめると、
前者の場合:PHP等の動的型付けなスクリプト言語では一般的なパターン
後者の場合:Java等の静的型付けなお固い言語では一般的なパターン
となります。

ただし注意しなければ成らないのが、後者の場合でもGroovyのコンパイルでは型チェックが出来ないという事です。プログラムが実行され、該当箇所が実際に実行されるまでエラーになるかどうか分かりません。
コレはGroovyが動的型付け言語であるためです。(型チェックが出来ないのは前者も同様)
例えば、以下のように後者のパターンの実行方法を変えた場合にはエラーは発生しません。

test(1)
if( 0 ) {
        test("a")  //ここは実行されないからプログラム的にはエラー無し
}

じゃあ態々メソッドの引数に型の指定をしてもメリット無いじゃないかと思われるかもしれませんが、当然メリットもあります。

まず、型を指定しておくと実行時例外にはなるものの、メソッドの中身は実行されないため、渡された引数が本当に意図した型なのかという事をメソッドの中で一々チェックしなてくても良い点。

2点目が、Groovyで作成したライブラリなどをJavaで利用する時。Groovyのソース上で型を指定しておくとJava側で意図しない値(今回の例であればString)を渡すようなプログラムを書いた場合、Javaのコンパイル時点でその誤りを検出出来ます。

Groovyでメソッドの引数に型を指定しない場合の前者、型を指定した場合の後者のjavapは以下のようになります。(該当メソッド部分のみ抽出)

前者の場合
[koji:groovy]$ javap type_check | grep test
  public java.lang.Object test(java.lang.Object);

後者の場合
[koji:groovy]$ javap type_check | grep test
  public java.lang.Object test(java.lang.Integer);


どういった場合に型を指定するかはプログラマや状況によるとは思いますが、基本的には仮引数に関しては型を指定してあげた方が良いと思います。

2013年2月3日日曜日

Groovyで日時の取り扱いサンプル(Dateオブジェクト)

Groovyが楽しい!
という事で、早速Dateオブジェクトのサンプルを書いてみました。
Scalaはちょっとお休みして今年はGroovyを勉強しようかなと思っています。

2012年11月6日火曜日

PHPのタイムゾーン(時差の計算)についての備忘録

今まで全くタイゾーンについて意識していませんでしたが、実際に自分が外国に来てからいざ
Webアプリケーションを作ってみようと思った段階で、「あれ?日付の取り扱いってどうすれば良いんだ?」と疑問に思ったので、この際なので纏めてみました。ちなみにPHPです。

GMTとUTC

基本的に同じ物です。GMTはよくグリニッジ天文台が云々言われるヤツですね。
詳しい違いは調べて頂ければ分かると思います。
基本的にUTCの方が正確な物というイメージですので、PHPプログラム内ではUTCを使用するようにした方が良いと思われます。

時差について

世界中の国々では当然時差があります。
例えば日本が24:00の場合、冬期ならばドイツは前日の16:00となります。
ちなみにドイツにはサマータイムがあるので、夏場だと時差が7時間となります。 この時点でプログラムで態々計算するのが面倒臭そうだというのが想像出来ると思います。

PHPでどのように取り扱うか

PHP5.2からDateTimeという組み込みクラスが使えるようになっています。
いきなり結論ですが、こいつを使えば全ての問題が解決します。
時差の計算などは全てこのクラスに任せればOKなのです。

実際の使い方は以下。

date_default_timezone_set('UTC');
$t = new DateTime("2012-11-05 21:34:40.0000000");

$t->setTimeZone( new DateTimeZone('UTC'));
echo $t->format(DateTime::ISO8601), "(UTC)<br />";

$t->setTimeZone( new DateTimeZone('Asia/Tokyo'));
echo $t->format(DateTime::ISO8601), "(Tokyo)<br />";

$t->setTimeZone( new DateTimeZone('Europe/Berlin'));
echo $t->format(DateTime::ISO8601), "(Berlin)<br />";


実行すると以下のようになります。

2012-11-05T21:34:40+0000(UTC)
2012-11-06T06:34:40+0900(Tokyo)
2012-11-05T22:34:40+0100(Berlin)

簡単なソースですので見れば分かると思いますが、大まかな流れは、
1.タイムゾーンをセット(これはphp.iniでもOK)
2.DateTimeオブジェクトを生成
3.DateTimeオブジェクトのタイムゾーンを変更する
4.formatメソッドを使って時刻を表示する。

今回の場合、タイムゾーンに世界標準時間(UTC)を指定しているので、DateTimeオブジェクトのコンストラクタに渡してあげた時刻文字列は、世界標準時間として扱われます。
以降、生成したDateTImeオブジェクトのタイムゾーンを変更すると、世界標準時間(UTC)からの時差を求める事が出来ます。

じゃあタイムゾーンに東京とか指定してDateTimeオブジェクト生成するとどうなるんだ?
元々日本国内で運営していたサービスを世界展開する場合、データベースなどに保存されている日時は日本時間だぞ?それをUTCとして扱うとおかしくなるんじゃないの?
という疑問が当然出てくると思います。
その点に付いては、下記の「注意点」を参照してください。

*なお、php.iniかスクリプト内でタイムゾーンを設定していないと以下のようなエラーが出ます。
Fatal error: Uncaught exception 'Exception' with message 'DateTime::__construct() [<a href='datetime.--construct'>datetime.--construct</a>]: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Europe/Berlin' for 'CET/1.0/no DST' instead' in /Users/koji/Sites/flash_cards/hoge.php:3 Stack trace: #0 /Users/koji/Sites/flash_cards/hoge.php(3): DateTime->__construct('2012-11-05 21:3...') #1 {main} thrown in /Users/koji/Sites/flash_cards/hoge.php on line 3


注意点

上記の方法では、既に生成したDateTimeオブジェクトのタイムゾーンを変更する事で、時差を求めています。
言い方を変えると、DateTimeオブジェクトを生成した時のタイムゾーンを元に時差を求めている事になります。

例えば、タイムゾーンを東京としてDateTimeオブジェクトを生成したとします。
で、そのDateTimeオブジェクトからUTC(世界標準時間)を求めた場合、当然のごとくその時間から-9時間となります。

// DateTimeオブジェクトをタイムゾーンをUTCとして生成
date_default_timezone_set('UTC');
$t = new DateTime("2012-11-05 21:34:40.0000000");

$t->setTimeZone( new DateTimeZone('UTC'));
echo $t->format(DateTime::ISO8601), "(UTC)<br />";

$t->setTimeZone( new DateTimeZone('Asia/Tokyo'));
echo $t->format(DateTime::ISO8601), "(Tokyo)<br />";

$t->setTimeZone( new DateTimeZone('Europe/Berlin'));
echo $t->format(DateTime::ISO8601), "(Berlin)<br />";

echo "-----------------------------------------------";

// DateTimeオブジェクトをタイムゾーンをTokyoとして生成
date_default_timezone_set('Asia/Tokyo');
$t = new DateTime("2012-11-05 21:34:40.0000000");

$t->setTimeZone( new DateTimeZone('UTC'));
echo $t->format(DateTime::ISO8601), "(UTC)<br />";

$t->setTimeZone( new DateTimeZone('Asia/Tokyo'));
echo $t->format(DateTime::ISO8601), "(Tokyo)<br />";

$t->setTimeZone( new DateTimeZone('Europe/Berlin'));
echo $t->format(DateTime::ISO8601), "(Berlin)<br />";

実行結果は以下
2012-11-05T21:34:40+0000(UTC)
2012-11-06T06:34:40+0900(Tokyo)
2012-11-05T22:34:40+0100(Berlin)
-----------------------------------------------
2012-11-05T12:34:40+0000(UTC)
2012-11-05T21:34:40+0900(Tokyo)
2012-11-05T13:34:40+0100(Berlin)

同じ時刻でDateTimeを生成しているのに、結果が全く異なっていますね。
冷静に考えればそのままなのですが、あくまでDateTimeオブジェクトを生成した時のタイムゾーンを元に時刻が計算される、という事を覚えておかないとはまってしまいます。
なお、DateTimeオブジェクトを生成する際にコンストラクタに渡している文字列の時刻ですが、これはPostgreSQLのTimestamp型からPDOでSELECTした結果の文字列です。

また、DateTimeに渡す文字列の時刻ですが、UNIXタイムスタンプや、今回のサンプル実行結果のようなタイムゾーン付きの文字列( 2012-11-05T21:34:40+0900 <-こんなやつ)の場合には、コンストラクタに渡すタイムゾーンパラメータや現在のタイムゾーンは無視されてしまいます。

// DateTimeオブジェクトをタイムゾーンをTokyoとして生成
// でも、DateTimeコンストラクタにタイムゾーン付き時刻の文字列を渡すと、その時刻のみを元にDateTimeオブジェクトが生成される
date_default_timezone_set('Asia/Tokyo');
$t = new DateTime("2012-11-01T13:34:40+0100");

$t->setTimeZone( new DateTimeZone('UTC'));
echo $t->format(DateTime::ISO8601), "(UTC)<br />";

$t->setTimeZone( new DateTimeZone('Asia/Tokyo'));
echo $t->format(DateTime::ISO8601), "(Tokyo)<br />";

$t->setTimeZone( new DateTimeZone('Europe/Berlin'));
echo $t->format(DateTime::ISO8601), "(Berlin)<br />";

実行結果
2012-11-01T12:34:40+0000(UTC)
2012-11-01T21:34:40+0900(Tokyo)
2012-11-01T13:34:40+0100(Berlin)


参考URL

http://php.net/manual/ja/class.datetime.php
http://www.php.net/manual/ja/datetime.construct.php
http://www.php.net/manual/ja/class.datetimezone.php
http://www.php.net/manual/ja/datetime.formats.php
http://php.net/manual/ja/function.date-default-timezone-set.php


2012年5月18日金曜日

Play framework 2.0のScalaテンプレートのユースケース意訳

Play framework2.0のScala templates common use casesを意訳しました。

先日テンプレートのシンタックス系のマニュアルを意訳しましたが、こちらはもう少し具体的にScalaテンプレートの使い方を説明しているものになります。

例のごとく、もし私の訳が間違っていても責任は負えませんのであしからず・・・
正確なものを読みたい方は原典をどうぞ。

http://www.playframework.org/documentation/2.0.1/ScalaTemplateUseCases

なお、日本Playframeworkユーザー会さんの方で翻訳作業が進行中のようですので、そちらを待たれるという手もあります。

それではどうぞ!

Scalaテンプレートの一般的なユースケース

テンプレートというシンプルな機能は、あなたがやりたい方法で構成することが出来ます。
以下に、いくつかの一般的なシナリオを示します。

レイアウト

メインのレイアウトテンプレートとして機能するviews/main.scala.htmlを宣言しましょう。

@(title: String)(content: Html)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
    <section class="content">@content</section>
  </body>
</html>

ご覧のとおり、このテンプレートは2つのパラメータを必要としています。
タイトルと、HTMLコンテンツです。
では、別のテンプレートであるviews/Application/index.scala.htmlからこのテンプレートを使ってみましょう。

@main(title = "Home") {
    
  <h1>Home page</h1>
    
}

Note:本ドキュメントでは、時々@main("Home")ではなく、@main(title = "Home")という形で、名前付き引数を使用します。
こうすることで、ソースをパッと見ただけで影響範囲がとても明確になります。

時々、サイドバーやパンくずリストといった個別のパーツが必要になるでしょう。
これは、パラメタを追加することで実現できます

@(title: String)(sidebar: Html)(content: Html)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
    <section class="sidebar">@sidebar</section>
    <section class="content">@content</section>
  </body>
</html>

このパラメタを追加したメインテンプレートを、さっき用意したviews/Application/index.scala.htmlから使ってみましょう。

@main("Home") {
  <h1>Sidebar</h1>

} {
  <h1>Home page</h1>

}

また、サイドバーの部分を別々に宣言することもできます。

@sidebar = {
  <h1>Sidebar</h1>
}

@main("Home")(sidebar) {
  <h1>Home page</h1>

}

※注意(公式にない私的メモ)
Play frameworkにおけるScalaテンプレートは関数だということを思い出しましょう。
さらに、テンプレートパラメータ(main.scala.htmlの先頭で宣言されている引数を受け取る部分)は、カリー化された関数になります。
だから、index.scala.htmlから呼び出す際に、コードブロックを{}で囲んで渡してあげることができるわけです。
カリー化されたmain.scala.htmlの方の引数の型が、Html型になっているので、コードブロックとして渡したHTMLはHtml型になるっぽいです。

タグ(これらは関数、でしょ?)

それでは、ちょっとしたインフォメーションを表示するシンプルなタグをviews/tags/notice.scala.htmlに書いてみましょう。

@(level: String = "error")(body: (String) => Html)
 
@level match {
    
  case "success" => {
    <p class="success">
      @body("green")
    </p>
  }

  case "warning" => {
    <p class="warning">
      @body("orange")
    </p>
  }

  case "error" => {
    <p class="error">
      @body("red")
    </p>
  }
    
}

これを別のテンプレートから呼び出してみましょう。

@import tags._
 
@notice("error") { color =>
  Oops, something is <span style="color:@color">wrong</span>
}

※注意(公式にない私的メモ)
テンプレートの方に@import tags._っていきなり出ていますね。
何の説明もなく、さも当たり前に書かれているので、なんじゃこりゃ!?と思いましたが、よくよく考えるとScalaテンプレートは結局ただの関数なので、上記で作成したviews/tags/notice.scala.htmlが、tags.notics()になっているということなんですね。(たぶん)

Includes

再度言いますが、特別なことは何もありません。
どんなテンプレートもどこからでも呼び出すことが出来ます。
普通の関数だってどこかから呼び出しているのと全く変わりありません。

<h1>Home</h1>
 
<div id="side">
  @common.sideBar()
</div>

moreScriptsとmoreStylesと同等の事

Play framework1.xの頃のmoreScriptsやmoreStylesと同じ環境をScalaテンプレートで宣言するには、mainテンプレートの以下のように宣言します。


(title: String, scripts: Html = Html(""))(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
        @scripts
    </head>
    <body>
        <div class="navbar navbar-fixed-top">
            <div class="navbar-inner">
                <div class="container">
                    <a class="brand" href="#">Movies</a>
                </div>
            </div>
        </div>
        <div class="container">
            @content
        </div>
    </body>
</html>

mainテンプレート以外のテンプレートでのみ使いたいJavaScriptなどは以下のように定義します。

@scripts = {
    <script type="text/javascript">alert("hello !");</script>
}

@main("Title",scripts){

   Html content here ...

}

mainテンプレートに渡すべき特別なJavaScriptなどが無い場合は、以下のよう呼び出します。
@main("Title"){

   Html content here ...

}

※注意(公式にない私的メモ)
mainテンプレートのscripts引数を見てみると、デフォルト引数としてHtml=("")がセットされていますよね?
だから、mainテンプレートを呼び出す、別のテンプレートからscriptsを渡さなければ自動的にHtml("")がscriptsに格納されるわけですね。



以上で終了です。

2012年5月17日木曜日

Play framework 2.0のフォームの制御部分意訳

Play framework2.0のHandling form submissionを意訳しました。

ざっと概要を言うと、ブラウザから送られてくるフォームのデータを、どうやってScalaの値にマッピングするのか、ということです。
マッピングの前に、どうやって値を取得するんだ?という疑問もありますが、あまり深く考えずに取得できるようになっています。
言い方を変えると、マッピングする部分を書けば、後はPlayがうまいこと値をrequestから引っ張ってきてくれるということです。

ただ、Play frameworkらしからぬ感じなのですが、具体的なサンプルが少なくて、ちょっとマニュアルが分かりづらかったです。(私の諸々のスキルが低いというのもありますが)
なので、途中でサンプルソースを載せました。(背景が紫色の部分)

例のごとく、もし私の訳が間違っていても責任は負えませんのであしからず・・・
正確なものを読みたい方は原典をどうぞ。

http://www.playframework.org/documentation/2.0.1/ScalaForms

なお、日本Playframeworkユーザー会さんの方で翻訳作業が進行中のようですので、そちらを待たれるという手もあります。

それではどうぞ!

フォームの定義

play.api.dataパッケージは、HTTPフォームのデータを扱い、バリデーションも扱うヘルパーが含まれています。
フォームを扱う最も簡単な方法は、play.api.data.Form構造を定義することです。

import play.api.data._
import play.api.data.Forms._

val loginForm = Form(
  tuple(
    "email" -> text,
    "password" -> text
  )
)

以下のフォームは、結果としてMap[String,String]から、(String, String)の値を生成します。

val anyData = Map("email" -> "bob@gmail.com", "password" -> "secret")
val (user, password) = loginForm.bind(anyData).get

もし、requestスコープの中のソースをコーディングしている場合であれば、直接requestの中身をバインドすることが出来ます。

val (user, password) = loginForm.bindFromRequest.get

複雑なオブジェクトの構築

formは値を構築・分解する機能を使用することが出来ます。
例えば、既存のケースクラスにラップすることが出来ます。

import play.api.data._
import play.api.data.Forms._

case class User(name: String, age: Int)

val userForm = Form(
  mapping(
    "name" -> text,
    "age" -> number
  )(User.apply)(User.unapply)
)

val anyData = Map("name" -> "bob", "age" -> "18")
val user: User = userForm.bind(anyData).get

Note:tupleとmappingを使用することによる違いは、tupleを使用する場合、constructionとdeconstructionを定義する必要が無いということです。
(だって私たちはtupleの構築と分解方法を知っていますよね?)

mapping関数は、あなたに、あなたのカスタマイズされた関数を定義させます。
ケースクラスを構築したり分解したりしたい場合はデフォルトのapplyメソッドとunapplyメソッドを使用するだけです。

※注意(公式にない私的メモ)
ここの訳は良く分からない。
construction and deconstruction functionsをいったいなんと訳せばいいのか・・・

もちろん、Formシグネチャがケースクラスに正確に一致しないことも良くあります。
例として、利用規約に同意するために使われるチェックボックスを含んだformを使ってみましょう。
Userに値を与える必要はありません。
これはフォームのバリデーションに一度使用されますが、役に立つ情報を教えてくれないダミーのフィールドです。
私たちは自分自身の構築・分解のための機能を定義できるので、これを扱うのはとても簡単です。

val userForm = Form(
  mapping(
    "name" -> text,
    "age" -> number,
    "accept" -> checked("Please accept the terms and conditions")
  )((name, age, _) => User(name, age))
   ((user: User) => Some((user.name, user.age, false))
)

Note:formに既存のUserを代入したとき、deconstructionが使用されています。
これは、Userをデータベースからロードして、それを更新するためにフォームを用意する場合に便利です。

制約の定義

それぞれのmappingで、バインディングの過程で値をチェックする、追加のバリデーションの制約を定義することが出来ます。

import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._

case class User(name: String, age: Int)

val userForm = Form(
  mapping(
    "name" -> text.verifying(required),
    "age" -> number.verifying(min(0), max(100))
  )(User.apply)(User.unapply)
)

Note:こういう書き方もできます

mapping(
  "name" -> nonEmptyText,
  "age" -> number(min=0, max=100)
)

このconstructsは上の例と全く同じ制約になります。

また、フィールドにアドホックな制約を定義することもできます。

val loginForm = Form(
  tuple(
    "email" -> nonEmptyText,
    "password" -> text
  ) verifying("Invalid user name or password", fields => fields match { 
      case (e, p) => User.authenticate(e,p).isDefined 
  })
)

バインディングのエラーを制御する

制約を定義した場合には、バインディング時のエラーを制御できるようにする必要があります。
以下のように、foldを使うことが出来ます。

loginForm.bindFromRequest.fold(
  formWithErrors => // binding failure, you retrieve the form containing errors,
  value => // binding success, you get the actual value 
)

※注意(公式にない私的メモ)
実際のサンプルはこんな感じ

def debug = Action { implicit request =>
    // ************************************************************
    // フォームの定義
    // ************************************************************
    // nonEmptyTextやnumberといったプロパティ(とメソッド)はplay.api.data.Formsクラスの中
    val myForm = Form(
        tuple(
            "val1" -> nonEmptyText,
            "val2" -> number
        )
    )
    
    
    // ************************************************************
    // request(POSTされたデータ)を取得する(2パターン)
    // ************************************************************
    /**
     * 最もシンプルなフォームの値の取得方法
     * フォームの定義で 制約(constraints)を指定している値で、実際のフォームに値が
     * 入力されていない場合、この取得方法だと以下のエラーが発生する
     * [NoSuchElementException: None.get]
     * フォームの定義で制約を付けている場合は、必ずfoldを使ってエラーハンドリングをすること!
     * 値が入力されている場合は正常に動いてしまうので、正常系のテストでは問題なく
     * 動作してしまう為、注意すること。
     */
    val(val1, val2) = myForm.bindFromRequest.get
    Ok("bindFormRequest.get" + " -> " + (val1 + 1) + " : " +  (val2 + 1))
    
    
    /**
     * フォームの定義に制約を指定している場合は、foldを使って必ずエラーハンドリングするように!
     * 問題ない場合はタプルとして値が取得でき、型の制約も特に問題なく動作する
     */
    myForm.bindFromRequest.fold(
        errors => Ok("NOOOOO"),
        value => Ok("bindFormRequest.fold" + " -> " + (value._1 + 1) + " : " + (value._2 + 1))
    )
}


formをデフォルト値で初期化する

時々データを編集するために、formに既存のデータを代入したいこともあるでしょう。その場合は以下のようにします。

val filledForm = userForm.fill(User("Bob", 18))

ネストされた値

formのmappingでは、ネストされた値を定義することもできます。

case class User(name: String, address: Address)
case class Address(street: String, city: String)

val userForm = Form(
  mapping(
    "name" -> text,
    "address" -> mapping(
        "street" -> text,
        "city" -> text
    )(Address.apply)(Address.unapply)
  )(User.apply, User.unapply)
)

この方法でネストされたデータを使用する場合、ブラウザから送られるフォームの値は「address.street, address.city, etc」の様な命名にしなければなりません。

繰り返される値

formのmappingでは、繰り返される値(同名の複数の値)を定義することもできます。

case class User(name: String, emails: List[String])

val userForm = Form(
  mapping(
    "name" -> text,
    "emails" -> list(text)
  )(User.apply, User.unapply)
)

このような繰り返される値を使用する場合、ブラウザから送られるフォームの値は「emails[0], emails[1], emails[2], etc」のような命名にしなければなりません。

Optionな値

formのmappingでは、Optionな値を定義することもできます。

case class User(name: String, email: Option[String])

val userForm = Form(
  mapping(
    "name" -> text,
    "email" -> optional(text)
  )(User.apply, User.unapply)
)

Note:emailフィールドは、requestの中に存在しない場合と、ブランク値が入っている場合には、Noneがセットされて無視されます。

無視する値

送られてきた値を無視して、フィールドに静的な値を持たせたい場合は以下のようにします。

case class User(id: Long, name: String, email: Option[String])

val userForm = Form(
  mapping(
    "id" -> ignored(1234),
    "name" -> text,
    "email" -> optional(text)
  )(User.apply, User.unapply)
)


あなたは今、Optionな値、ネストされた値、同名の複数の値を混在させて、複雑なformを作成することが出来ます。


以上で終了です。

2012年5月9日水曜日

Play framework2.0のテンプレートエンジン部分意訳

Play framework2.0のThe template engineを意訳しました。

私は英語が苦手なタイプの人間なので間違っている部分は多々あると思います。
例のごとく、もし私の訳が間違っていても責任は負えませんのであしからず・・・
正確なものを読みたい方は原典をどうぞ。

http://www.playframework.org/documentation/2.0/ScalaTemplates

なお、日本Playframeworkユーザー会さんの方で翻訳作業が進行中のようですので、そちらを待たれるという手もあります。

それではどうぞ!

Play2.0のテンプレートエンジンはScalaがベースです

APS.NETのRazorにインスパイアされました。
コンパクトで簡単でどんなテキストエディタでも編集できます。
新しい言語を覚える必要はありません!

テンプレートはScalaだからコンパイルされます。
つまり、ブラウザで通常のプログラムと同じようにテンプレートのエラーを確認できます。

overview

Play2.0のテンプレートは、ちょっとしたScalaのコードを含んだシンプルなテキストファイルです。
HTMLやXML、CSVといった普通のテキストベースのフォーマットを生成できます。
テンプレートシステムは、デザイナが簡単に扱えるようにして、HTMLを快適に感じるようにしました。

テンプレートはコンパイルされて、普通のScalaの関数になります。
もし、「views/Application/index.scala.html」というテンプレートを作った場合、「views.html.Application.index」という関数が生成されます。

上記のディレクトリにファイルを作ったと仮定して、以下はシンプルなテンプレートの例です。

@(customer: Customer, orders: Seq[Order])
<h1>Welcome @customer.name!</h1>
<ul>
@orders.map { order =>
 
<li>@order.title</li>
}
</ul>

上記のテンプレートは、ただのテンプレートとしてだけではなく、どんなScalaコードからでも呼び出せます。
以下がサンプルです

val html = views.html.Application.index(customer, orders)

※注意(公式にない私的メモ)
Applicationというディレクトリがサンプルにあるけど、なんかこれはデフォルトコントローラのApplication.scalaと混同してしまって分かりにくかった。
viewsディレクトリの下に、「hoge/piyo.scala.html」というファイルを作った場合は、「views.html.hoge.piyo()」になります。
views.htmlは決め打ちで、後は通常のパッケージみたいに.区切りでディレクトリ階層を書いて行って、最後にテンプレートの先頭のファイル名(scala.htmlは不要)を書く感じ

@という魔法

Scalaテンプレートで、@を特殊な文字として扱います。
この@マークがある場所は、Scalaステートメントが始まる場所です。
@で始まるScalaステートメントをを態々閉じる必要はありません。
フレームワークがうまいことやってくれます。

Hello @customer.name!
       ^^^^^^^^^^^^^^^^^^^^^^^
        Scala code

テンプレートエンジンはあなたのコードを解析して、コードの終わりを自動的に検知してくれます。
ただし、もし複数のトークンを使うようなステートメントは明示的に括弧を使ってね。

Hello @(customer.firstName + customer.lastName)!
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    Scala Code

さらに、普通のScalaコードのように中括弧を使ってステートメントを書くこともできます。

Hello @{val name = customer.firstName + customer.lastName; name}!
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                             Scala Code

そう!@は特殊な文字なのです。
時々@をエスケープしたいときもあるでしょう。
その時は@@を使ってください

My email is bob@@example.com


テンプレートパラメータ

テンプレートはシンプルな機能なので、引数が必要です。
テンプレートファイルの先頭行に宣言する必要があります。

@(customer: models.Customer, orders: Seq[models.Order])

さらにデフォルト引数も使えます。

@(title: String = "Home")

あと、いくつかのパラメータグループも使えます。

@(title:String)(body: => Html)

そしてimplictパラメータも使えます。

@(title: String)(body: => Html)(implicit request: play.api.mvc.Request)


イテレータ

Scalaのfor分は当然使えます。
ただし、以下の場合だとテンプレートコンパイラはコードブロックの上にyieldを置くことに注意してください。

<ul>
@for(p <- products) {
 
<li>@p.name ($@p.price)</li>

}
</ul>

恐らく分かるでしょうが、for分は典型的なmapのシンタックスシュガーになっています。


<ul>
@products.map { p =>
 
<li>@p.name ($@p.price)</li>

}
</ul>


ifブロック

ifブロックは特別なものではありません。
普通のScalaのifと同じように使い、以下のようになります。

@if(items.isEmpty) {
 
<h1>Nothing to display</h1>
} else {
 
<h1>@items.size items!</h1>
}


パターンマッチング

テンプレートの中でパターンマッチングを使うこともできます。

@connected match {
   
  case models.Admin(name) => {
    Connected as admin (@name)
  }

  case models.User(name) => {
    Connected as @name
  }
   
}


再利用可能なブロックの宣言

再利用可能なブロックを利用できます。

@display(product: models.Product) = {
  @product.name ($@product.price)
}

<ul>
@products.map { p =>
  @display(product = p)
}
</ul>

さらに、再利用可能なScalaによるコードブロックを宣言することもできます

@title(text: String) = @{
  text.split(' ').map(_.capitalize).mkString(" ")
}

<h1>@title("hello world")</h1>

Note:Scalaによるコードブロックをテンプレートの中で宣言するのは時々は有効な手段です。
しかし、複雑なロジックを記述するのに、テンプレートはベストな場所ではないということを覚えておいてください。
多くの場合、複雑なコードは純粋なScalaソースファイルに外出ししておいた方が良いです。
(views/パッケージの下にそういったソースファイルを格納することもできます。)

※注意(公式にない私的メモ)
つまり普通にテンプレートの中で関数を宣言できるということ。

PlayFramework2.0の決まりによって、再使用可能なコードはimplictという名前で定義されます。

@implicitFieldConstructor = @{ MyFieldConstructor() }


再利用可能な値の宣言

definingヘルパーを使って、スコープ内で再利用できる値を定義できます。

@defining(user.firstName + " " + user.lastName) { fullName =>
 
    Hello @fullName

}


ステートメントのインポート

テンプレートの先頭でインポートしたいものはどんなものでもインポートすることが来ます。
(もしくはサブテンプレート)

@(customer: models.Customer, orders: Seq[models.Order])

@import utils._

...


コメント

サーバサイドのみのコメントを@* *@を使って書くことが出来ます。

@*********************
 * This is a comment *
 *********************@  

テンプレートの最初の行にコメントを書くことで、Scala API docとして扱うことが出来ます

@*************************************
 * Home page.                        *
 *                                   *
 * @param msg The message to display *
 *************************************@
@(msg: String)

<h1>@msg</h1>


エスケープ

デフォルトでは、動的なコンテンツはテンプレートの種類に応じてエスケープされます。(HTMLとかXML)
もしエスケープさせずにそのまま出力したい場合には、出力したい生データをテンプレートのコンテントタイプでラップしてください。

  <p>@Html(article.content) <p> 


以上で、公式のテンプレートエンジン部分の意訳は終了!
PlayFramework1.2の頃に翻訳されたサイトを見ていましたが、チュートリアルが分かりやすいという点が本当に素晴らしいですね。
英語は苦手ですが、難しい表現もないようなので英語の勉強にももってこいなのでは?

2012年4月25日水曜日

PostgreSQLで複数レコードを一つのカラムにまとめる方法

PostgreSQLで、ARRAY関数を使って複数レコードを一つのカラムにまとめて取得する方法です。
この日本語だけだと、正直一体何を言っているの分からないので、実際の業務に絡めてみました。

まず、CDを管理するシステムを運用していると仮定します。
そして、各CDは必ずどこかのレーベルから発売されています。
さらに、各CDには複数のジャンルがあります。
各CDと各ジャンルを紐付けるために、それぞれのIDを格納したテーブルを使用します。

*******************************************************
各テーブルの情報
*******************************************************

・CDテーブル
 cd_id | label_id |     cd_name    
-------+----------+-----------------
     1 |        1 | I am Man part 1
     2 |        1 | I am Man part 2
     3 |        2 | hoge the CD

・レーベルテーブル
 label_id | label_name
----------+------------
        1 | label_A
        2 | label_B

・ジャンルテーブル
 genre_id | genre_name
----------+------------
        1 | Rock
        2 | Blues

・CDとジャンルを紐つけるテーブル
 genre_id | cd_id
----------+-------
        1 |     1
        1 |     2
        2 |     2
        2 |     3

ある日、営業にこんなことを言われました。

「全CDデータのCSV作ってよ。」
「カラムは、["CDのID", "CD名", "レーベル名", "ジャンル名"]でお願いね。」
「あ、各CDのジャンルは一行に纏めてね。とりあえず&区切りとかでお願い。」

よくありますね。こういうの。
まぁこれくらいなら簡単に取得できそうです。
さっそく全データをSELECTだ!

*見やすいようにSQL文に改行を入れています
postgres=# SELECT c.cd_id, c.cd_name, l.label_name, g.genre_name
                  FROM cd c, label l, genre_reference gr, genre g
                  WHERE c.label_id = l.label_id
                          AND c.cd_id = gr.cd_id
                          AND gr.genre_id = g.genre_id;

 cd_id |     cd_name     | label_name | genre_name
-------+-----------------+------------+------------
     1 | I am Man part 1 | label_A    | Rock
     2 | I am Man part 2 | label_A    | Rock
     2 | I am Man part 2 | label_A    | Blues
     3 | hoge the CD     | label_B    | Blues
(4 rows)

ぱっと見て分かりますが、「I am Man part2」が2レコードありますね。
これはジャンルが2つ設定されているためです。
ということは、各CDを1行のCSV行にするためには、何らかの方法で各CDの情報をマージする必要があります。
ジャンルを省いた状態でプログラムでデータを取得して、再度全レコードに対してジャンル情報を問い合わせたり・・・
極端に難しい訳ではなさそうですが、面倒くさいですよね。
余計なSQLの発行にもなるし・・・

そこで、PostgreSQLのARRAY関数の出番です。
SQLを以下のようの書き換えて実行してみます。

*見やすいようにSQL文に改行を入れています
postgres=# SELECT c.cd_id, c.cd_name, l.label_name,
                          ARRAY (
                                  SELECT genre_name
                                  FROM genre g, genre_reference gr
                                  WHERE c.cd_id = gr.cd_id AND g.genre_id = gr.genre_id
                          ) as genre_name
                  FROM cd c, label l
                  WHERE c.label_id = l.label_id
                  ORDER BY cd_id;

 cd_id |     cd_name     | label_name |  genre_name
-------+-----------------+------------+--------------
     1 | I am Man part 1 | label_A    | {Rock}
     2 | I am Man part 2 | label_A    | {Rock,Blues}
     3 | hoge the CD     | label_B    | {Blues}

genre_nameが{}で囲まれて一つのレコードにまとめられました。
これがARRAY関数の機能です。
ジャンルに関連する部分を、SELECT内でARRAY関数を使ってサブクエリっぽくまとめてあげるだけで実現できます。
これで、各CD情報は完全に1行にまとめられました。
あとはプログラムで取得してCSVにするなり、vi等でゴニョゴニョするなり自由自在です。

ちなみにJavaだとjava.sql.Array型として取得できます。

java.sql.Array workCategories = this.resultSet.getArray("genre_name");
String[] categories = (String[]) workCategories.getArray();

参考
*******************************************************
DDL
*******************************************************

-- CDマスタ
CREATE TABLE cd( cd_id int, label_id int, cd_name varchar(64) );

-- レーベルマスタ
CREATE TABLE label( label_id int, label_name varchar(64) );

-- ジャンルマスタ
CREATE TABLE genre( genre_id int, genre_name varchar(64) );

-- CDとジャンルの関連を管理するテーブル
CREATE TABLE genre_reference ( genre_id int, cd_id int );


*******************************************************
テストデータ投入
*******************************************************
-- レーベルマスタのデータ
INSERT INTO label (label_id, label_name) VALUES(1, 'label_A');
INSERT INTO label (label_id, label_name) VALUES(2, 'label_B');

-- CDマスタのデータ
INSERT INTO cd (cd_id, label_id, cd_name) VALUES(1, 1, 'I am Man part 1');
INSERT INTO cd (cd_id, label_id, cd_name) VALUES(2, 1, 'I am Man part 2');
INSERT INTO cd (cd_id, label_id, cd_name) VALUES(3, 2, 'hoge the CD');

-- ジャンルのマスタ
INSERT INTO genre(genre_id, genre_name) VALUES(1, 'Rock');
INSERT INTO genre(genre_id, genre_name) VALUES(2, 'Blues');

-- CDとジャンルの紐付け
INSERT INTO genre_reference(genre_id, cd_id) VALUES(1,1);
INSERT INTO genre_reference(genre_id, cd_id) VALUES(1,2);
INSERT INTO genre_reference(genre_id, cd_id) VALUES(2,2);
INSERT INTO genre_reference(genre_id, cd_id) VALUES(2,3);
INSERT INTO genre_reference(genre_id, cd_id) VALUES(1,4);


*******************************************************
以下テーブル構成
*******************************************************
              Table "public.cd"
  Column  |         Type          | Modifiers
----------+-----------------------+-----------
 cd_id    | integer               |
 label_id | integer               |
 cd_name  | character varying(64) |


              Table "public.label"
   Column   |         Type          | Modifiers
------------+-----------------------+-----------
 label_id   | integer               |
 label_name | character varying(64) |


              Table "public.genre"
   Column   |         Type          | Modifiers
------------+-----------------------+-----------
 genre_id   | integer               |
 genre_name | character varying(64) |


 Table "public.genre_reference"
  Column  |  Type   | Modifiers
----------+---------+-----------
 genre_id | integer |
 cd_id    | integer |

2012年3月8日木曜日

UbuntuのPuttyで日本語を入力する方法

UbuntuのソフトウェアセンターからPuttyをインストールして楽ちんだな~と思いながら使っていると、問題が・・・
まず、日本語入力ができない!これは大問題です。次に、WindowsのPuttyだと出来ていたドラッグした部分が自動的にコピーされて、右クリックでペースト。これもできませんでした。
設定か?それともそういうものなのか?と思って色々調べていましたがPuttyだけで解決する方法が見つかりませんでした。

そこで、Puttyをインストールしたら一緒に付いてくるplinkというputtyのコマンドラインツールを試してみると無事日本語入力が出きるようになりました。
plinkとは、ターミナルから直接Puttyを利用するためのコマンドラインツールです。
そのため、Puttyのウインドウではなくて、標準のターミナルでリモートに接続できます。
なので、文字列のコピーは標準のターミナルと同じ手順になります。

では、実際にどうやって使うかというと、基本はplinkに接続先ホスト名やユーザ名等を指定して実行する形になります。

plink [ユーザ名]@[ホスト] -P [ポート番号] -i [SSHの秘密鍵(必要なら)]

・・・日本語が入力出来るようになるのはうれしいけど毎回接続情報入力するのは面倒くさい・・・
しかしさすがPutty専用のコマンドラインツール!
loadオプションに、Puttyに保存されているセッション名を渡してあげると、その情報を使ってリモートに接続する事が出来ます。
しかもセッション名に日本語を使っていても問題ありませんでした。

例えば、「開発マシーン!」というセッション名でリモートサーバ情報を保存している場合、plinkを以下のように実行します。

plink -load 開発マシーン!

これだけで、Puttyで接続した時とまったく同じ情報でリモートに接続できます。

ただし、plinkだとPuttyのように接続先によって背景色を変えるなどが出来ないようです。
この部分だけ不満ですが実用上問題ないのでま、いっか~という状況です。

実際にログインした際には以下のようになります。

k-kuwana@VirtualBox:~$ plink -load "[セッション名]"
Using username "[接続するユーザ名]".
Passphrase for key "[鍵名]": //鍵を使用していたらその鍵名が表示されて、パスフレーズの入力が求められます
Last login: Wed Mar  7 14:56:08 2012 from xxx.xxx.xxx.xxx
[接続ユーザ名@接続ホスト名 ~]$

今後は、Putty Configuration(Puttyを起動したら表示される画面)で、サーバ情報だけ保存して、実際の接続はすべてplinkで行おうと思っています。

plinkのヘルプは以下の通りです。
k-kuwana@VirtualBox:~$ plink
PuTTY Link: command-line connection utility
Release 0.61
Usage: plink [options] [user@]host [command]
       ("host" can also be a PuTTY saved session name)
Options:
  -V        print version information and exit
  -pgpfp    print PGP key fingerprints and exit
  -v        show verbose messages
  -load sessname  Load settings from saved session
  -ssh -telnet -rlogin -raw -serial
            force use of a particular protocol
  -P port   connect to specified port
  -l user   connect with specified username
  -batch    disable all interactive prompts
The following options only apply to SSH connections:
  -pw passw login with specified password
  -D [listen-IP:]listen-port
            Dynamic SOCKS-based port forwarding
  -L [listen-IP:]listen-port:host:port
            Forward local port to remote address
  -R [listen-IP:]listen-port:host:port
            Forward remote port to local address
  -X -x     enable / disable X11 forwarding
  -A -a     enable / disable agent forwarding
  -t -T     enable / disable pty allocation
  -1 -2     force use of particular protocol version
  -4 -6     force use of IPv4 or IPv6
  -C        enable compression
  -i key    private key file for authentication
  -noagent  disable use of Pageant
  -agent    enable use of Pageant
  -m file   read remote command(s) from file
  -s        remote command is an SSH subsystem (SSH-2 only)
  -N        don't start a shell/command (SSH-2 only)
  -nc host:port
            open tunnel in place of session (SSH-2 only)
  -sercfg configuration-string (e.g. 19200,8,n,1,X)
            Specify the serial configuration (serial only)

2012年3月4日日曜日

Ubuntuのターミナルのパス表記が長い場合の対処方法

Ubuntu11.10だと、標準ではターミナルのパス表記(という言い方が正しいかどうか分かりませんが・・・)がカレントディレクトリまでのパスまで表記されてしまい、階層が深くなると非常に見づらくなってしまいます。
こんな感じ。

k-kuwana@dev:~/usr/work/scala/lernen$ pwd
/home/k-kuwana/usr/work/scala/lernen

そこで、$HOME/.bashrcの55行目付近(else節)にある「\w」という所を「\W」に変更してあげると、カレントディレクトリのみ表示されるようになってスッキリします。

$HOME/.bashrc変更前

52 if [ "$color_prompt" = yes ]; then 53 PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 54 else 55 PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 56 fi

$HOME/.bashrc変更後

52 if [ "$color_prompt" = yes ]; then 53 PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 54 else 55 PS1='${debian_chroot:+($debian_chroot)}\u@\h:\W\$ ' 56 fi


OSを再起動するか、ターミナルを起動して「source ~/.bashrc」を実行すれば上記の修正が反映されます。

2012年3月3日土曜日

PHP5.4のビルトインサーバ起動メモ

PHP5.4のビルトインサーバを使ってみたのでまとめてみました
なお、Windows7のVirtualBox上で動かしているUbuntu11.10で動作確認しました。
1.まずはPHP5.4をインストール
configureする時にapxsとか気にするな!cliさえあればWEBサーバ使えるから!
開発機ではビルトインサーバ上でサクサク作って本番機はApacheとかね!
ちなみにUbuntu11.10で動作確認しました。


2.インストールが終わったら、とりあえず以下にパスを通しとけ!
パスを通しとけば何かと便利なのでやっぱり通しておきましょう
PHPインストールディレクトリ/bin/

ちなみに、makeだけしとけば、make installしなくても、sapi/cliにパスを通せばOK。
どっちでもお好みで。


3.ビルトインサーバ起動!
これを待ってました!Apacheのインストールとか設定とか・・・もううんざり!
PHP5.4からは簡易WEBサーバが標準搭載されました。
実行するのはこんなに簡単!

・どこでもいいから、phpinfo()を書いたindex.phpを作成
・同じ場所で以下を実効
php -S localhost:8080
・ブラウザでhttp://localhost:8080/index.phpにアクセス!


4.DocumentRootはここだよ!
上述のコマンドだと、カレントディレクトリがDocumentRootになるとのこと。
DocumentRootを指定するには以下のコマンドでOK
php -S localhost:8080 -t [DocumentRootのフルパス]


5.php.iniはここだよ!
これまた開発時にいろいろな環境でphp.iniを切り替えたいというときに便利!
起動時に以下のように使用するphp.iniを指定できます。
php -S localhost:8080 -c [php.iniのフルパス]

php.iniのパスが間違っていたりしても、エラーにはならずに単純に読み込まれずにビルトインサーバが起動する。
ちなみに、php.ini-developmentを使用すると、ビルトインサーバが吐き出すログ(コンソール)に色が付いてテンション上がる。


6.まとめ
ドキュメントルート、php.iniの場所を指定するとこんな感じ
php -S localhost:8080 -t /home/k-kuwana/myDocumentRoot -c /home/k-kuwana/php.ini


7.Apacheのmod_rewriteとかっぽいのはどうするの?
そういうのはPHPスクリプトで制御するらしい。
書き方はまだ分からないけど、とりあえずビルトインサーバ起動時に以下のように指定してあげる?
php -S localhost:8080 builtin-server.php

2012年2月7日火曜日

Scalaの関数とメソッドの宣言方法のまとめ

ちなみに、Scalaでメソッドと呼ばれるものは、defキーワードで宣言されたもののことです。
サンプルとして、2つの引数をとり、それらを掛け合わせたものを返す関数とメソッドを作ってみます。

もっともオーソドックスな関数

まず関数の宣言方法は以下です。

scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => { x * y } multiply: (Int, Int) => Int = <function2> # 実行 scala> multiply(5,10) res8: Int = 50

(Int, Int) => Int
この部分が関数の型の宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返す関数という宣言になります。

(x:Int, y:Int) => { x * y }
この部分が実際の関数の宣言です。
なお、=>{...}の部分は、通常コードブロックと呼ばれます。
xとyという変数で値を受け取って、x * yを実行します。
Scalaでは最後に評価された式が戻り値となりますので、x*yの実行結果(Intどうしの掛け算なので当然Int型)が帰ります。

なお、実はこの関数は無名関数です。
multiplyというのはあくまで変数名であり、この関数自体には名前がありません。

「(Int, Int) => Int 」という型の変数に、イコール(=)を使って、関数「 (x:Int, y:Int) => { x * y }」を代入しているということになります。


もっともオーソドックスなメソッド

次はメソッドで同じものを作ってみます。

scala> def multiply(x:Int, y:Int):Int = { x * y } multiply: (x: Int, y: Int)Int # 実行 scala> multiply(5,10) res9: Int = 50

def multiply(x:Int, y:Int):Int
この部分がメソッドの宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返すメソッドという宣言になります。

= { x * y }
この部分が実際のメソッド本体ですです。
xとyという変数で値を受け取って、x * yを実行します。


関数の省略記法

幾つかパターンはありますがとりあえず以下の2パターンが良く使われると思います。

# コードブロックの中括弧{}を省略 scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 思い切って関数の型を省略 scala> val multiply = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 引数なしの関数はこれ scala> val multiply:() => Int = () => 10 * 5 multiply: () => Int = <function0> # 関数の型を省略 scala> val multiply = () => 10 * 5 multiply: () => Int = <function0>


メソッドの省略記法

結構パターンがあります。この省略記法を覚えるのには苦労させられます。

# コードブロックの中括弧{}を省略 scala> def multiply(x:Int, y:Int):Int = x * y multiply: (x: Int, y: Int)Int # 戻り値の型を省略 scala> def multiply(x:Int, y:Int) = x * y multiply: (x: Int, y: Int)Int # 引数も戻り値の型も中括弧も全て省略。クラスのゲッターなどに使える scala> def getName = "Yamada Tarou" getName: java.lang.String # これでも同じ scala> def getName() = "Yamada Tarou" getName: ()java.lang.String # 戻り値の型と中括弧を省略したパターン。クラスのセッターなどに使える scala> def setName(newName:String) = name = newName setName: (newName: String)Unit # 戻り値が無い(Unit)の場合は、コードブロックの前のイコールを書かなくても良い scala> def myPrint { println("Hello World") } myPrint: Unit # もちろん書いても良い scala> def myPrint = { println("Hello World") } myPrint: Unit # これも同じ scala> def myPrint:Unit = { println("Hello World") } myPrint: Unit # ただしこれはNG scala> def myPrint:Unit { println("Hello World") }
まだまだパターンはありますが、とりあえずこれでメソッドの省略記法のメジャーどころはつぶせたかなと思います。


関数の宣言に関する注意

ちゃんと関数として宣言せずに、とりあえずコードブロックで囲めばいいんでしょ?というノリでコーディングすると意図しない動作になってしまいます。

# 以下のような宣言をすると、myPrintにはUnitが入ってしまう scala> val myPrint = { println("Hello World") } Hello World myPrint: Unit = () # なので、何回myPrintを実行しても何も起こらない scala> myPrint() # 意図した関数として宣言するには以下のようにする scala> val myPrint:()=>Unit = () => { println("Hello World") } myPrint: () => Unit = <function0> scala> myPrint() Hello World scala> myPrint() Hello World
また、関数はオブジェクトなので、別の変数に格納することもできます。

# まずは関数定義 scala> val myPrint = () => {println("Hello World")} myPrint: () => Unit = <function0> # 括弧無しで呼び出すと関数本体が返される scala> myPrint res1: () => Unit = <function0> # 別の変数に代入 scala> val myPrint2 = myPrint myPrint2: () => Unit = <function0> scala> myPrint2() Hello World


メソッドの宣言に関する注意

Scalaでは、メソッドに戻り値があっても戻り値の型は省略できますが、メソッド宣言とコードブロックの間のイコールは省略できません。
厳密に言うと省略は出来るのですが、イコールを書略してしまうと値を返してくれないメソッドになってしまいます。

# コードブロックで文字列が評価されてStringが返りそうだけど・・・ scala> def getString { "Helo World" } getString: Unit # 実行すると何も帰ってきません scala> getString
これは、イコールを省略するとメソッドが自動的にUnitになってしまうためです。
イコールを付ければ期待した動作になります。

scala> def getString = { "Helo World" } getString: java.lang.String scala> getString res24: java.lang.String = Helo World
次に、関数の場合は括弧()なしで実行すると関数自体が返されましたが、メソッドは動作が異なります。
メソッドの宣言で、引数リストの括弧()を省略した場合には呼び出し方法が制限されます。

scala> def myPrint { println("Hello World") } myPrint: Unit # こちらはOK scala> myPrint Hello World # 括弧を付けるとエラー・・・ scala> myPrint() <console>:9: error: Unit does not take parameters myPrint() ^
引数なしであっても、引数リストを省略せずに空の括弧()を付けておけばどちらでも実行できます。

scala> def myPrint() { println("Hello World") } myPrint: ()Unit scala> myPrint() Hello World scala> myPrint Hello World

2012年2月2日木曜日

bzr+ssh://で外部のBazaarリポジトリにアクセスする方法(Linux)


以下のコマンドでOK

bzr log bzr+ssh://{接続先ユーザ名}@{接続先ホスト名}:{接続先のSSHのポート}{接続先の共有リポジトリへのフルパス}

なお、接続先のサーバにはSSHですでに接続できていることが前提。
接続先のBazaarに特別な設定などは特に必要ありません。

接続先の情報が以下の場合、

・接続先:example.com
・接続先ユーザ名:hoge
・接続先のSSHのポート:1234
・接続先の共有リポジトリの場所:/home/hoge/myrepo

こうなります。

bzr log bzr+ssh://hoge@example.com:1234/home/hoge/myrepo

Linux系なら「~/.bazaar/authentication.conf」にポートとかの設定を書いておけるよ、と公式に書いているの、なぜか設定が反映されず。
自分の環境では、接続先のSSHのポートは22番以外なので、正しいポートをauthentication.confに書いていたのになぜか22番でアクセスしてタイムアウトしていました。
ということで、直接URLにポート番号を指定することで回避できました。

このあたりの情報が公式以外にあまりないんですけどBazaarユーザって少ないんですかね?

2012年1月30日月曜日

Scalaでシングルトンパターン

Scalaでシングルトンパターンを書いてみました。
ベターJavaの呼び声高いScalaですが、やはりJavaと全く同じという訳にはいきませんでした。
ちょっと悩んだのでメモとして残します。
objectやstaticの扱いのサンプルにもなると思います。

そもそもScalaでstatic使えるの?
まず、Scalaにはstaticが存在しません。
その代わりに、シングルトンオブジェクトを生成するobjectというものがあります。

なにそれ?
シングルトンオブジェクトという名前が表す通り、シングルトンなオブジェクトです。
特徴として、new演算子を使わなくてもメソッドの実行や変数の参照が出来ます。
ClassName.getInstance() ←こんな感じです。

それってJavaのstaticで宣言されたメソッドとか変数呼び出すのと同じじゃん?
その通りです。呼び出しだけでなく、動作自体もJavaのstaticな変数やメソッドと同じです。
そもそもすでに述べたように、Scalaにはstaticが存在せず、その代わりにobjectという仕組みが用意されているのです。
Javaでstaticとして宣言される変数やメソッドを、Scalaではobjectの中に切り出してあげるのです。

なるほど。じゃあ具体的にどうやって宣言するの?
これはソースを見ていただければ一目瞭然です。
通常、クラスを宣言する際は
class className {...}
となりますが、objectの場合は、
object ObjectName {...}
となります。

ではサンプルソースをご覧ください。

class SingletonSample private( var list:List[Int] ) {

         // リストの先頭に値を追加
        def add( x:Int ) {
            list = x :: list
        }
}

object SingletonSample {

    private val sample:SingletonSample = new SingletonSample((1 to 10).toList)

    def getInstance():SingletonSample = {
         sample
    }

    def apply():SingletonSample = {
         getInstance
    }
}


REPLでの実行結果は以下の通りです。

#インスタンスの生成
scala> val sample = SingletonSample.getInstance
sample: SingletonSample = SingletonSample@151fe8a

#取得したインスタンスの中身(list)をチェック
scala> sample.list
res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

#取得したインスタンス内のリストの先頭に0を追加
scala> sample.add(0)

#0が先頭に追加されていることを確認
scala> sample.list
res3: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

#再度インスタンスを取得してみる
scala> val sample2 = SingletonSample.getInstance
sample2: SingletonSample = SingletonSample@151fe8a

#取得したインスタンスのリストの中身の先頭に0が追加されている
#このことから、シングルトンパターンとして正常に動作していることが分かる
scala> sample2.list
res2: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

ほぉ、簡単ジャン!
ただし気を付けなければならない点が一つあります。
Javaで「staticな変数やメソッド」と、「staticでない変数やメソッド」が入り混じっているクラスの場合、Scalaではstaticな部分をobjectの中に切り出さなければならないのは説明しました。
気を付けなければならない唯一の点とはまさにその切り出し方で、class名とobject名を同一のものにしなければならない「場合がある」という点です。
Scalaでは、class名とobject名が同じものをコンパニオンオブジェクトと呼びます。
この状態が、Javaの「staticな変数やメソッド」と、「staticでない変数やメソッド」が入り混じっているクラスと同じ状態になります。

class名とobject名を同じにしなければならない「場合」って?
サンプルソースを見ていただければ分かると思いますが、シングルトンパターンの場合、実際に生成されるクラスのコンストラクタはprivateにします。
これはJavaでも同じです。シングルトーンパターンなので当然です。
ただ、コンパニオンオブジェクトでない場合、privateでコンストラクタが宣言されているクラスのインスタンスを生成できないのです。
これは、Java的に説明すると、objectが別のクラス内のstaticで宣言されたメソッドや変数という扱いになるためです。
コンパニオンオブジェクトの場合であれば、Java的にはclassとobjectが実質同じクラス内と考えて良いので、インスタンスを生成することができます。

・・・よくわからなくなってきた
極論ではありますが、Javaのstatic混じりのクラスをScalaで書く場合に、staticな変数やメソッドは、クラス名と同じ名前で宣言されたobjectの中に書いてしまえばOKです。
staticなメンバが入っているJavaのクラスHogeがある場合、Scalaではstaticな部分のみ切り出して、Hogeという、クラスと同名のobjectの中に移動してあげるだけです。

ちなみに、コンパニオンオブジェクトを生成したい場合は、同名のclassとobjectを用意するのですが、同一ファイル内に記述しなければならないという制約もあります。

2011年11月9日水曜日

Scalaで文字列中の各出現文字をカウントする

Mapを使ったソースを書いてみようと思ってネタを考えてウダウダした結果、タイトルのようなソースが出来上がりました。
ついでなので関数の部分適用なども使ってみました。

下記サンプルソースに盛り込まれている要素は、
  • Map(mutable)
  • 関数の部分適用
  • 関数のオーバロード
になります。

では早速サンプルソースです。


object CharacterCount {     // scala.collection.mutableをインポートしておけば、mutable.Mapの宣言が短くできる     // インポート無 → cala.collection.mutable.Map[Char, Int]()     // インポート有 → mutableMap[Char, Int]()     import scala.collection.mutable
    /**      * 指定された文字列中に出現する各文字をカウントし、Mapに格納する      * @param text 出現する文字をカウントしたい文字列      * @param limit 出現回数がlimit回以上の文字のみ返す      * @return text中、limit回以上出現した文字と出現回数を格納したMap      */     def getMapWithLimit(text:String, limit:Int):mutable.Map[Char,Int] = {         count(text).filter( (kv) => kv._2 >= limit )     }     /**      * getMapWithLimitの部分適用      * getMapWithLimit(text, 1)と指定したのと同じ      * @param text 出現する文字をカウントしたい文字列      * @return text中、1回以上出現した文字と出現回数を格納したMap      */     def getMap = getMapWithLimit(_:String, 1)     /**      * 部分適用したgetMapをオーバロードしたもの      * getMapWithLimitと全く同じ      * @param text 出現する文字をカウントしたい文字列      * @param limit 出現回数がlimit回以上の文字のみ返す      * @return text中、limit回以上出現した文字と出現回数を格納したMap      */     def getMap(text:String, limit:Int):mutable.Map[Char,Int] = {         getMapWithLimit(text, limit)     }     /**      * 指定された文字列に出現する各文字をカウントし、Mapに格納して返す      * @param text 出現する文字をカウントしたい文字列      * @return text中の各文字と出現回数を格納したMap      */     private def count(text:String):mutable.Map[Char, Int] = {         val map = mutable.Map[Char, Int]()         text.foreach ( (char) => {             // MapのgetOrElseは、第1引数に指定したキーが存在しない場合に、第2引数の値を返します             val count = map.getOrElse( char, 0 )             map.update( char, count+1 )         })         map     }     // 関数の部分適用部分は、以下のようにしても結果は同じ     //def getMap(text:String) = { getMapWithLimit(text, 1) } }

getMap関数(メソッド?)を使うと、文字列中の各文字をキーとし、値に出現回数が格納されたmutable.Mapのインスタンスが返されます。
REPLから試すと以下のとおりです。


scala> val text = "となりのきゃくはよくかきくうきゃくだ。"
text: java.lang.String = となりのきゃくはよくかきくうきゃくだ。

// CharacterCountって打つのが面倒くさいのでccという変数に格納
scala> val cc = CharacterCount
cc: CharacterCount.type = CharacterCount$@2e5facbd

// 出現回数か1回以上の文字を取得(つまり全文字)
scala> cc.getMap(text)
res0: scala.collection.mutable.Map[Char,Int] = Map(の -> 1, か -> 1, う -> 1, く -> 4, ゃ -> 2, と -> 1, き -> 3, な -> 1, は -> 1, よ -> 1, 。 -> 1, り -> 1, だ -> 1)

// 出現回数が3回以上の文字のみ取得
scala> cc.getMap(text, 3)
res1: scala.collection.mutable.Map[Char,Int] = Map(く -> 4, き -> 3)


map.getOrElseに関して、これがScalaの目玉機能の一つであるOption型(Some型とNone型がある)に直結する機能です。JavaであればExceptionが発生しそうなところ、Noneが返されるので結果的に処理が無視される or 指定した処理が実行されることになります。 このOptionに関してはまだ勉強中なのでまた今度サンプルソースを書いてみたいと思っています。

これぐらいのプログラムであれば態々戻り値の型を書かなくても把握できるしScalaが理解できているならコメントもいらないくらいだと思います。(私は理解できていないのでコメントを書きました)
コメントと型の記述を省いて型推論に頼るとこんなにスッキリしました。


object CharacterCount2 {

    def getMapWithLimit(text:String, limit:Int) = {
        count(text).filter( (kv) => kv._2 >= limit )
    }

    def getMap = getMapWithLimit(_:String, 1)

    def getMap(text:String, limit:Int) = {
        getMapWithLimit(text, limit)
    }

    private def count(text:String) = {
        val map = scala.collection.mutable.Map[Char, Int]()
        text.foreach ( (char) => {
            map.update( char, map.getOrElse(char,0)+1 )
        })
        map
    }
}


今回は各文字でカウントアップしましたが、ちょっと工夫すれば単語単位でカウントすることも可能だと思います。
英語やドイツ語であればスペースで文章を区切ってしまえば自動的に単語単位になるので、うまいこと考えれば今年生まれた新語一覧なんかも作れるのかな~と思ったりしてます。

2011年11月3日木曜日

ドイツの歴史(超概要)


ドイツの歴史をざっくりまとめてみました。
本当にざっくりなので後々修正予定です。
しかし学生のころと違って興味を持って歴史を調べると楽しいですね~
なお、この超概要はこのサイト様を参考にさせていただいております。
参考というより、元々とても綺麗に整理されていたものをさらに私が理解できるレベルに箇条書きにした感じです。


1.ガリア遠征(BC58~BC51)などでゲルマン人は傭兵や農民としてローマ帝国に溶け込む


2.ゲルマン民族の大移動を経て、乱立した各地の王国をフランク王国が統一


3.843年のヴェルダン条約によって、分割されたうちの1つである東フランク王国(843年~962年)が現在のドイツの原型となった


4.東フランク王国の国王オットー1世は962年にアウグストゥス(古代ローマ帝国皇帝の称号)を得て、神聖ローマ帝国(962年~1806年)と呼ばれる緩やかな連合体を形成した(長い!)


5.中世におけるドイツには国家としての統一や民族意識はほとんどなく、各地に領邦国家が分立した。これが現在に続く連邦主義の基盤となっているといえる


6.宗教改革では、新旧両教に分かれて互いに争い、三十年戦争ではドイツ全土が徹底的に破壊され、1600万人いたドイツの人口が600万人に減少したといわれる


7.1701年に、北部の領邦君主ホーエンツォレルン家がプロイセン王国(1701年~1918年)を形成した


8.1806年まで神聖ローマ皇帝位を世襲していた「大ドイツ主義」派のハプスブルク家、「小ドイツ主義」派のホーエンツォレルン家とドイツ統一の役割を争った。


9.ナポレオンによる侵略を経て、民族意識と統一国家への志向を強め、19世紀前半にはプロイセンに主導的な役割を期待する気運が高まった。神聖ローマ帝国はナポレオンにの攻勢に屈して解体されハプスブルク家のフランツ2世が1806年に退位した。神聖ローマ帝国の滅亡である。


10.1871年、プロイセン国王ヴィルヘルム1世の戴冠によってドイツはドイツ系オーストリアを除く、小ドイツとしてのドイツ国(ドイツ帝国(1871年~1918年))として統一され、ベルリンを首都とした。(ちなみにこの戴冠式はパリのヴェルサイユ宮殿で行われた)


11.1918年、第1次世界大戦の敗北によって共和制(1919年~1933年)に移行。しかし、ヴァイマル共和制は小党乱立により政局は不安定で、第1次世界大戦後の賠償などもあり、ドイツ国内は驚異的なインフレとなる


12.1933年に、インフレなどを経て、アドルフ・ヒトラー率いるナチス党(国家社会主義ドイツ労働者党)が国民からの支持を受け、政権を握る


13.1939年9月に隣国ポーランドを攻略し、第2次世界大戦が勃発


14.第2次世界大戦に敗北し、オーデル・ナイセ線以東やシュレジェン地域を領土として喪失。さらに、アメリカ、イギリス、フランス、ソビエト連邦の四か国に分割統治される。


15.1949年、盆を暫定的な首都とするドイツ連邦共和国(西ドイツ)と、ベルリンの東部地区(東ベルリン)を首都とするドイツ民主共和国(東ドイツ)に分裂した。


16.1989年ソビエト連邦のペレストロイカに端を発した東ドイツの民主化運動(東欧革命)をきっかけにベルリンの壁が崩壊し、翌1990年に東西ドイツの再統一を果たし、首都を再びベルリンとした。


17.ボンからの連邦政府機関移転(実質的な首都機能移転)や東ベルリンを中心とするベルリンの再開発・インフラ整備を進め、2001年5月2日に、ベルリンへの首都機能移転が完了した。


18.現在ではヨーロッパで最大の国家の一つになる。しかし、東西分裂を原因とする東西の経済格差が影を落としている。
それでもなお、ドイツはフランスとともに欧州連合(EU)の中核国として発言力を増し続けている。
これを再びドイツが周辺国の脅威となると懸念している国もある。

2011年10月27日木曜日

Scalaのファイル入出力のサンプル

Scalaでファイルを入出力するサンプルを書いてみました。
ファイルの入力(読み込み)は専用のクラスがあるのですが、ファイルの出力に関してはなんだか適切な物が無いようです。
なので、ファイルの出力に関してはJavaのクラスを使っています。

ファイルを新規作成して文字列を出力(すでに存在する場合は追記)
その後、作成したファイルを読み込んでいます。
/**
 * Scalaでファイルの入出力
 * Javaのクラスを使ってファイルを作成し、Scalaのクラスを使ってファイルを読み込む
 */
object FileIO {
    def main( args:Array[String] ) {

        // JavaのFileOutputStreamクラスとOutputStreamWriterを別名でimport
        // それぞれインスタンス生成時の1回のみの使用なので別名にする意味は特にないです。
        import java.io.{ FileOutputStream=>FileStream, OutputStreamWriter=>StreamWriter };

        val fileName = "test.txt"
        val encode = "UTF-8"
        val append = true
        
        // 書き込み処理
        val fileOutPutStream = new FileStream(fileName, append)
        val writer = new StreamWriter( fileOutPutStream, encode )

        writer.write("あいうえお\r\n")
        writer.write("かきくけこ\r\n")
        writer.write("さしすせそ\r\n")
        writer.close


        // 読み込み処理(コンソールに出力)
        // 1行で書くとこんな感じ
        // scala.io.Source.fromFile(fileName, encode).getLines.foreach{ println _ }
        val source = scala.io.Source.fromFile(fileName, encode)
        val lines = source.getLines
        lines.foreach{ println _ }


    }
}

2011年10月26日水曜日

Scalaのアクセサメソッドの挙動

Scalaのアクセサメソッド(getterとsetter)について調べたことをまとめます。

下記ページをかなり参考にさせていただきました。
[Scala][Java] Scalaの統一アクセス(プロパティ構文)がなかなかイカしてる件

Scalaはvarかvalを使ってプロパティを宣言すると、自動的にアクセサメソッドを生成してくれます。
さらに、プロパティを直接参照するような書き方をすると、自動的にアクセサメソッドが実行されるようになっています。
そのため、

オブジェクト名.name で、自動生成されたゲッターが実行される
オブジェクト名.name = "abc" で自動生成されたゲッターが実行される

となります。

コンパイルした後に、javapを使って中身を覗いてみると宣言していないメソッドが生成されていることが分かります。

サンプルソース
class Hoge1 {
    var name:String = _
}

object Hoge1Runner {
    def main(args:Array[String]) {
        val obj = new Hoge1
        obj.name = "Tarou";

        // 実行結果は、「Hoge1:Tarou」となる
        println("Hoge1:" + obj.name);
    }
}
javap -private Hoge1の実行結果
  public class Hoge1 extends java.lang.Object implements scala.ScalaObject{
      private java.lang.String name;
      public java.lang.String name();
      public void name_$eq(java.lang.String);
      public Hoge1();
  }

自動的に生成されたアクセサメソッド使ってるって言ってもこれじゃJavaのpublicなプロパティと一緒じゃん。
値のチェックとか必要な場合は結局アクセサメソッド自前で作らなきゃいけないんでしょ?オーバライドできるの?
ということでアクセサをオーバライドしてみました。(オーバライドって言い方が正しいかどうかは分かりませんが)
getterでは、先頭に「Mr.」を、setterでは末尾に「.」を付与するようにしました。

Hoge1Runnerのrun()と、Hoge2Runerのrun()を見比べてみてください。呼び出し側の実行方法は全く変わっていません。
つまり、サクっと開発しておいて、後々呼び出されるクラスの方を修正してしまえば、呼び出し側を変更する必要が無いのです。
Javaだとこうはいきません。オブジェクト名.プロパティ名と書いたコードが山ほどあった場合、呼び出されているクラスでアクセサメソッドを追加した際、呼び出しているプログラムを全て修正する必要があります。
IDEを使っていればアクセサメソッドを自動生成してくれたり、一括置換など便利な機能があるので、特に困ることは無いかもしれませんが、ちょっとしたツールを作りたいという時にはこう言った機能が真価を発揮すると思います。

問題点として、以下のjavap実行結果を見て分かる通り、自動生成されたgetterであるname_()が定義されています。
ということは、実用上問題はないでしょうが、Hoge2#name_ = "aaa" などが実行されると意図したgetterが利用されないということになります。

サンプルソース
class Hoge2 {
    var name_ :String = _

    // getter
    def name  = {
        "Mr." + name_
    }

    // setter
    def name_= (value:String) = {
        name_ = value + "."
    }
}

object Hoge2Runner {
    def main(args:Array[String]) {
        val obj = new Hoge2
        obj.name = "Tarou";

        // 実行結果は「Hoge2:Mr.Tarou.」となる
        println("Hoge2:" + obj.name);

        // 以下の方法だと、用意したgetterが使われないので、実行結果は「Hoge2:Tarou」となります
        //(用意したsetterを使ってないのでピリオドがつかない)
        // val obj2 = new Hoge2
        // obj2.name_ = "Tarou"
        // println("Hoge2:" + obj2.name);
    }
}
javap -private Hoge2の実行結果
public class Hoge2 extends java.lang.Object implements scala.ScalaObject{
    private java.lang.String name_;
    public java.lang.String name_();
    public void name__$eq(java.lang.String);
    public java.lang.String name();
    public void name_$eq(java.lang.String);
    public Hoge2();
}

Hoge2の、意図しないgetterが利用できる問題を解決するためには、プロパティ自体をprivateにする必要があります。
プロパティをprivateにすると、自動生成されるgetterとsetterもprivateになります。
そのため、自動生成されたアクセサには、外部からアクセスでできなくなるため、意図しないアクセサ(自動生成されたアクセサ)が利用できてしまう問題を解消できます。
ただ、いきなりこの形で記述してしまうとScalaでプロパティが自動生成されるメリットが無くなってしまいます。
まずはpublicでプロパティを作っておいて、問題が出たりするタイミングで修正した方が良いかも。
setter自体を用意しなければ外部からの更新を制御することもできます。

サンプルソース
class Hoge3 {
    private var name_ :String = _

    // getter
    def name  = {
        "Mr." + name_
    }

    // setter
    def name_= (value:String) = {
        name_ = value + "."
    }
}

object Hoge3Runner {
    def main(args:Array[String]) {
        val obj = new Hoge3
        obj.name = "Tarou";

        // 実行結果は「Hoge3:Mr.Tarou.」となる
        println("Hoge3:" + obj.name);
        // 以下の方法だと、name_はprivateなのでコンパイルエラー
        // (error: variable name_ in class Hoge3 cannot be accessed in Hoge3)

        // val obj2 = new Hoge3
        // obj2.name_ = "Tarou"
        // println("Hoge3:" + obj2.name);
    }
}

javap -private Hoge3の実行結果
public class Hoge3 extends java.lang.Object implements scala.ScalaObject{
    private java.lang.String name_;
    private java.lang.String name_();
    private void name__$eq(java.lang.String);
    public java.lang.String name();
    public void name_$eq(java.lang.String);
    public Hoge3();
}

Scalaではプロパティのアクセサは自動生成されるなら、そのアクセサはどうやって修正すればいいんだ?と思って調べた結果をまとめました。
間違っている点などあればご指摘頂頂けるとありがたいです。
ちなみに、プロパティをvalで宣言すると、Javaのfinalになり、setterは自動生成されず、getterのみ自動生成されます。
Javaのfinalなので、同じクラス内からも当然値を変更できなくなります。
なんでプロパティ名にアンダーバーが必要なの?というのはまだ理解できていません。
アクセサ自体の書き方も、普通の関数とは違うようなのでまだまだ勉強しなければという感じです。

2011年10月6日木曜日

Eclipse3.7にGoogle Plugin for Eclipseをインストールしようとするとエラーが発生する

Eclipse3.7(Indigo)にGoogle Plugin for Eclipseをインストールしようとすると下記のエラーが発生しました。

1 つ以上の必須項目が見つからないため、インストールを完了できません。 Software being installed: Google App Engine Tools for Android 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android.feature.feature.group 2.4.1.r37v201109211906) Missing requirement: Android Cloud Tooling 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android 2.4.1.r37v201109211906) requires 'bundle com.android.ide.eclipse.adt 12.0.0' but it could not be found Cannot satisfy dependency: From: Google App Engine Tools for Android 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android.feature.feature.group 2.4.1.r37v201109211906) To: com.google.gdt.eclipse.mobile.android [2.4.1.r37v201109211906]

なんかAndroid用のプラグインの依存関係が解決できないよ、というようなエラー。

これは、最初の「使用可能なソフトウェア」のところで、Google App Engine Tools for Android (requires ADT)のチェックを外して上げると解決します。
Android使わないし!という人はこれでも問題ないかと。

2011年10月5日水曜日

ScalaでHashtable系を使ってみる

object Hoge {
    def main(args:Array[String]) {
        import java.util.Hashtable

        // ----------------------------
        // 一番シンプルなパターン
        // ----------------------------
        // Javaだと Hashtable <String,String> hTable = new Hashtable <String,String>();
        val hTable = new Hashtable[String,String]()
        hTable.put("key1", 1.toString)
        hTable.put("key2", 2.toString)

        println(hTable.get("key1"))
        println(hTable.get("key2"))

        println("--------")

        // ----------------------------
        // リストに突っ込んでみる
        // ----------------------------
        //var hTableList:List[Hashtable[String, String]] = List()
        var hTableList = List[Hashtable[String, String]]()
        for ( i <- 1 to 10 ) {
            val hTable = new Hashtable[String,String]()
            hTable.put( "key", i.toString )
            hTableList = hTableList ::: List(hTable)
        }
        hTableList.map( record => println( record.get("key") ) )

        println("--------")

        // ----------------------------
        // せっかくなのでScalaのMapを使ってみる
        // ----------------------------
        //var tableList:List[Map[String,String]] = List()
        var tableList = List[Map[String,String]]()
        for ( i <- 1 to 10) {
            tableList = tableList ::: List( Map("key" -> i.toString) )
        }
        tableList.map( record => println( record.get("key") ) )

        println("--------")

        // ----------------------------
        // リストをもう少しかっこよくする
        // ----------------------------
        val listBuffer = new scala.collection.mutable.ListBuffer[Map[String,String]]
        for ( i <- 1 to 10 ) {
            listBuffer += Map( "key" -> i.toString )
        }
        listBuffer.toList.map( record => println(record.get("key")) )
    }
}

2011年8月8日月曜日

Scalaでユニットテスト

ターミナルとかプロンプトで実行する場合。

scalatestと、JUnitのページからそれぞれ必要なjarファイルをダウンロードする。(今回はJUnit4)
結果的にscalatest-1.5.1.zipとjunit-4.9b3.jarが手に入る。
scalatestは解凍するとscalatest-1.5.1.jarがあるので、こいつとjunit-4.9b3.jarをScalaのインストールディレクトリ内にあるlibディレクトリの中に突っ込んで準備完了。
libディレクトリに置いておきたくない場合は、コンパイル時と実行時に毎回クラスパスに該当jarを指定すること。

テストのサンプルソースは以下のとおり。
プログラミング言語Scala 日本語情報サイト01.02.JUnit 4 と ScalaTest の始め方の物をそのまま使わせていただいています。(先頭にコメントを追加)

//import org.scalatest.junit.AssertionsForJUnit
import org.scalatest.junit.JUnitSuite
import scala.collection.mutable.ListBuffer
import org.junit.Assert._
import org.junit.Test
import org.junit.Before


/**
 * /////////////////////////////////////////////////////////////////////////////////////////////
 * Scalaでユニットテストのサンプル
 * クラスパスに、JUnitとscalatestのjarファイルを加えておく必要がある。
 * 通常の開発環境であれば、SCALA_HOME/lib/の下に置いておけば自動的にクラスパスに含まれるので楽。
 * /////////////////////////////////////////////////////////////////////////////////////////////
 * JUnitで実行する場合は以下のコマンド
 * scala org.junit.runner.JUnitCore ExampleSuite
 *
 * scalatestで実行する場合は以下のコマンド
 * scala org.scalatest.tools.Runner -p . -o -s ExampleSuite
 * ちなみに、 -p . の . を省けばSwingウインドウが起動する
 *
 * どちらで実行する場合でも、JUnitSuiteを継承していれば問題無し。JUnitSuiteはAssertionsForJUnitをtraitとして使っている
 */
//class ExampleSuite extends AssertionsForJUnit {
class ExampleSuite extends JUnitSuite {

    var sb:StringBuilder = _
    var lb:ListBuffer[String] = _

    @Before def initialize() {
        sb = new StringBuilder("ScalaTest は")
        lb = new ListBuffer[String]
    }

    // JUnitスタイルのアサーションを使用
    @Test def verifyEasy () {
        sb.append("かんたん!")
        assertEquals("ScalaTest はかんたん!", sb.toString)
        assertTrue(lb.isEmpty)
        lb += "sweet"
        try {
            "verbose".charAt(-1)
            fail()
        } catch {
            case e:StringIndexOutOfBoundsException => // こうなるはず
        }
    }

    // ScalaTest アサーションを使用
    @Test def verifyFun() {
        sb.append("たのしい!")
        assert(sb.toString == "ScalaTest はたのしい!")
        assert(lb.isEmpty)
        lb += "sweeter"
        intercept[StringIndexOutOfBoundsException] {
            "concise".charAt(-1)
        }
    }
}