FAQ - プログラミング

    1. どうやってループを書けばいいですか?

    2. どうすれば例外を捕捉できますか?

    3. 何故、Javaと違って、"throws"がメソッドに無いのですか?

    4. どうすれば"break"や"continue"できますか?

    5. ケースクラスは普通のクラスとどのように違いますか?

    6. Scalaでオブジェクトをシリアライズするとき、どのようにstaticフィールド"serialVersionUID" を指定すれば良いですか?

    7. 自分のパッケージ名の中で"scala"という単語を使うと何が起きますか?"_root_"とは何でしょうか?

    8. 何故、ListとStreamで異なる構文を使わなければならないのでしょうか?より正確には、何故、::や:::演算子をStreamクラスに定義できないのでしょうか?

    9. Scalaでカリー化を使うと、パフォーマンスが落ちますか?

どうやってループを書けばいいですか?

整数に対するループを書くには、二つの単純な方法があります。

for (i <- 1 to 1000000) { ... }

var i = 1; while (i <= 1000000){ ...; i += 1 }

どちらでもよいですが、現在の実装では、2番目の方がより効率的です(訳注:Scala 2.9.0では両者の差はかなり縮まっています)。

どうすれば例外を捕捉できますか?

tryとcatchを使ってください。catch節はパターンマッチになっています。

try { // ... } catch { case ioe: IOException => ... // more specific cases first ! case e: Exception => ... }

何故、Javaと違って、"throws"がメソッドに無いのですか?

例外のコンパイル時チェックは理論的には良いように見えます。しかし、実用上はうまく働きません。プログラマは、あまりにも多くの例外をキャッチし過ぎる傾向があります。その結果、それらの例外を飲み込んでしまい、信頼性を損なうことになります。

どうすれば"break"や"continue"できますか?

それらは、Scala 2.7では予約語ではありません。また、異なる方法で実装する必要があります。breakの代わりとなる最も単純な方法は、コードをより小さいメソッドに分割し、メソッドの途中でreturnを使うことです。continueの代わりとなる単純なアプローチは、ループ中でスキップされる部分をifの中に入れることです。Scala 2.8ではbreakが導入されますが、continueは導入されません。

ケースクラスは普通のクラスとどのように違いますか?

    1. ケースクラスに対してパターンマッチングができ、

    2. newキーワード無しにケースクラスのインスタンスを構築でき、

    3. 全てのコンストラクタ引数は、自動的に生成されるアクセッサ関数によって外側からアクセス可能であり、

    4. toStringメソッドは、ケースクラス名と全引数を出力するように自動的に再定義され、

    5. equalsメソッドは、同じケースクラスのインスタンスに対して、同一性ではなく、ケースクラスの構造に基づいて比較するように、自動的に再定義されます。

    6. hashCodeメソッドはコンストラクタ引数のhashCodeメソッドを使うように自動的に再定義されます。

ケースクラスとして宣言する場合のほとんどは、1番目の理由によるものです。つまり、そのインスタンスに対してパターンマッチングができるようにするためです。しかしながら、もちろん、他の理由によってケースクラスとして宣言することもできます。以下のコード例 では、ケースクラスの二つの特性を使っています。

    • a. インスタンスは以下のようにnewキーワード無しに生成できます(2.)。

      • List(MyInt(1), MyInt(2), MyInt(3))

    • b. コンストラクタ引数は、以下のように自動生成されたアクセッサ関数によって"外側から"アクセスできます(3.)。

      • else m.asInstanceOf[MyInt].x == x;

Scalaでオブジェクトをシリアライズするとき、どのようにstaticフィールド"serialVersionUID" を指定すれば良いですか?

Scalaでは、コンパイル時に定数に評価されるprivateな値(訳注:valで宣言されたもの)は、Javaのprivate static finalな変数に変換されます。この機能はドキュメント化されていませんが、このトリックを使う必要があります。ScalaのListの実装をチェックアウトしてください(src/scala/List.java(訳注:List.scalaのtypoだと思われます)。::クラスとNilの両方がフィールドserialVersionUIDを、次のような形で持っています。

private val serialVersionUID = ;

どんな数値リテラルでも値として使うことができますし、Javaと同様に、serialverをその目的のために使うことができます。もし、そのようなIDをmypackage.Fooのようなクラスやトレイトに付加したくなったら、次のようにserialverを呼ぶことができます。

> serialver mypackage.Foo$class

トップレベルのobject mypackage.Objの場合、以下のように書くことができます。

> serialver mypackage.Obj$

自分のパッケージ名の中で"scala"という単語を使うと何が起きますか?"_root_"とは何でしょうか?

Javaと異なり、Scalaは相対パッケージ名を持っています。たとえば、以下のように書くことができます。

package com.mycompany.tools; import mycompany._; // ..

これは、com.mycompanyパッケージ化の全てをインポートしています。問題は、次のようなimport文 The problem is that the import statement

import scala.collection.mutable.ArrayBuffer

があると、あなたのパッケージ中のscalaという名前があった場合、それも探索してしまうことです。

この方法の利点は、パッケージ名は絶対パスである必要がないため、より短く書くことができる上に、何かをあるパッケージから別のパッケージに移動させたいときに、より容易にそれを行うことができる事です。

しかしながら、Scalaでは、絶対パッケージ名を使うことも依然として可能です。それには、以下のように、パッケージ名の先頭に特別な_root_というパスを付けるだけで良いです。

import _root_.scala.collection.mutable.ArrayBuffer

何故、ListとStreamで異なる構文を使わなければならないのでしょうか?より正確には、何故、::や:::演算子をStreamクラスに定義できないのでしょうか?

:::演算子は既にList(1,2,3):::List(4,5,6)(その結果はList(1,2,3,4,5,6)になります)のように、Listの結合に使われています。

また、我々は、どの演算子を右結合にするかを決めなければいけませんでした。我々は、":"で終わる全てのものを右結合にすることにしました。これは、a:::b:::cがメソッド呼び出しc.:::(b).:::(a)に変換される事を意味しています。Streamは通常無限であるため、この方法で右結合の演算子を使うことはあまり意味がありません。というわけで、他の文字を最後の文字として使う必要がありました(たとえば、::+)

Scalaでカリー化を使うと、パフォーマンスが落ちますか?

カリー化によってパフォーマンスは落ちません。fとgが次のように定義されていた場合、

def f(x: Int)(y: Int) = expr def g(x: Int, y: Int) = expr

f(x)(y)とg(x, y)は全く同じコードにコンパイルされます。