diff --git a/_data/translations.yml b/_data/translations.yml index 266b039905..56d0fd59cf 100644 --- a/_data/translations.yml +++ b/_data/translations.yml @@ -1,2 +1,2 @@ tour: - languages: [ba, es, ko, pt-br, pl, zh-cn, th, ru] + languages: [ba, es, ko, pt-br, pl, zh-cn, th, ru, ja] diff --git a/_ja/tour/abstract-type-members.md b/_ja/tour/abstract-type-members.md new file mode 100644 index 0000000000..f5821a7b74 --- /dev/null +++ b/_ja/tour/abstract-type-members.md @@ -0,0 +1,79 @@ +--- +layout: tour +title: 抽象型メンバー +language: ja + +discourse: true + +partof: scala-tour +num: 23 +next-page: compound-types +previous-page: inner-classes +topics: abstract type members +prerequisite-knowledge: variance, upper-type-bound + +redirect_from: "/tutorials/tour/abstract-types.html" +redirect_from: "/tour/abstract-types.html" +--- + +トレイトや抽象クラスのような抽象型は抽象型メンバーを持つことができます。 +これは具体的な実装で実際の型を定義するという意味です。 +こちらが例です。 + +```tut +trait Buffer { + type T + val element: T +} +``` +こちらでは、抽象型`type T`を定義しています。それは`element`の型を記述するために使われます。このトレイトを抽象クラスで継承し、より具体的にするために上限型境界を`T`に追加することができます。 + +```tut +abstract class SeqBuffer extends Buffer { + type U + type T <: Seq[U] + def length = element.length +} +``` +`T`の上限型境界の定義に出てきた、更に別の抽象型`U`の使い方に気をつけてください。この`class SeqBuffer`はこのバッファーの中にシーケンスのみを保持することができます。それは型`T`は新しい抽象型`U`を使った`Seq[U]`のサブタイプであると記述しているからです。 + +抽象型メンバーを持つトレイトと[クラス](classes.html)は無名クラスのインスタンス化と組み合わせてよく使われます。 +これを説明するために、今から整数のリストを参照するシーケンスバッファーを扱うプログラムを見てみます。 + +```tut +abstract class IntSeqBuffer extends SeqBuffer { + type U = Int +} + + +def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = + new IntSeqBuffer { + type T = List[U] + val element = List(elem1, elem2) + } +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` +ここで、ファクトリー`newIntSeqBuf`は抽象型`T`を具体的な型`List[Int]`に設定するために、`IntSeqBuf`(つまり`new IntSeqBuffer`)を無名クラスで実装します。 + +抽象型メンバーをクラスの型パラメータに変えることも、その逆も可能です。以下は上記コードの型パラメータのみを使うバージョンです。 + +```tut +abstract class Buffer[+T] { + val element: T +} +abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] { + def length = element.length +} + +def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] = + new SeqBuffer[Int, List[Int]] { + val element = List(e1, e2) + } + +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` +ここでは(`+T <: Seq[U]`)をメソッド`newIntSeqBuf`から戻されるオブジェクトの具体的なシーケンス実装の型を隠すために [変位指定アノテーション](variances.html)を使わなければなりません。さらに、抽象型メンバをパラメータで置換することができないケースがあります。 diff --git a/_ja/tour/annotations.md b/_ja/tour/annotations.md new file mode 100644 index 0000000000..88114954f1 --- /dev/null +++ b/_ja/tour/annotations.md @@ -0,0 +1,130 @@ +--- +layout: tour +title: アノテーション +language: ja + +discourse: true + +partof: scala-tour + +num: 32 +next-page: default-parameter-values +previous-page: by-name-parameters + +redirect_from: "/tutorials/tour/annotations.html" +--- + +アノテーションはメタ情報と定義を関連づけます。例えば、メソッドの前のアノテーション`@deprecated`はメソッドが使われたらコンパイラに警告を出力させます。 +``` +object DeprecationDemo extends App { + @deprecated("deprecation message", "release # which deprecates method") + def hello = "hola" + + hello +} +``` +これはコンパイルされますが、コンパイラは警告"there was one deprecation warning"を出力します。 + +アノテーション句はそれに続く最初の定義か宣言に適用されます。定義と宣言の前には1つ以上のアノテーション句を置くことができます。これらの句の順番は重要ではありません。 + + +## エンコーディングの正確性を保証するアノテーション +条件が一致しない場合にコンパイルを失敗させるアノテーションもあります。例えば、アノテーション`@tailrec`はメソッドが[末尾再帰](https://en.wikipedia.org/wiki/Tail_call)であると保証します。末尾再帰ではメモリ使用量が一定になります。こちらは階乗を計算するメソッドの中での使われ方です。 +```tut +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + + @tailrec + def factorialHelper(x: Int, accumulator: Int): Int = { + if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) + } + factorialHelper(x, 1) +} +``` +`factorialHelper`メソッドは`@tailrec`を持ちます。`@tailrec`はメソッドが実際に末尾再帰であると保証します。もし`factorialHelper`の実装を以下のように変更すれば、失敗し +``` +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + @tailrec + def factorialHelper(x: Int): Int = { + if (x == 1) 1 else x * factorialHelper(x - 1) + } + factorialHelper(x) +} +``` +"Recursive call not in tail position"というメッセージを受け取ります。 + + +## コード生成に影響するアノテーション +`@inline`のようなアノテーションは生成されたコードに影響します(つまり、アノテーションを使わなかった場合とでjarファイルのバイト数が異なる場合があります)。インライン化とは、メソッドを呼び出している箇所にメソッド本体のコードを挿入することを意味します。結果のバイトコードはより長くなりますが、上手くいけば実行が早くなります。アノテーション`@inline`を使ってもメソッドのインライン化が保証されるわけではありません。しかし、生成されたコードのサイズに関するヒューリスティックスが満たされた場合に限りコンパイラにインライン化をさせます。 + +### Javaのアノテーション ### +Javaと相互運用するScalaのコードを書いている時、記述するアノテーション構文は少し違います。 + +**注:** Javaアノテーションを使う場合、`-target:jvm-1.8`オプションを使ってください。 + +Javaには[アノテーション](https://docs.oracle.com/javase/tutorial/java/annotations/)の形をしたユーザー定義メタデータがあります。Javaのアノテーションの特徴は、要素の初期化のために名前と値のペアを指定する必要があることです。例えば、クラスのソースを追いかけるためのアノテーションが必要なとき、以下のように定義するかもしれません。 + +``` +@interface Source { + public String URL(); + public String mail(); +} +``` + +そして、それは以下のように適用されます。 + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends TheirClass ... +``` + +Scalaでのアノテーションの適用はコンストラクタの呼び出しと似ています。Javaのアノテーションをインスタンス化するためには名前付き引数を使う必要があります。 + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +class MyScalaClass ... +``` + +アノテーションが(デフォルト値を除き)要素を1つだけ含む場合、この構文はかなり退屈です。そのため慣例により、名前が`value`と指定されていれば、コンストラクタのような構文でJavaに適用できます。 + +``` +@interface SourceURL { + public String value(); + public String mail() default ""; +} +``` + +そして以下のように適用します。 + +``` +@SourceURL("http://coders.com/") +public class MyClass extends TheirClass ... +``` + +この場合、Scalaでも同じことができます。 + +``` +@SourceURL("http://coders.com/") +class MyScalaClass ... +``` + +`mail`要素はデフォルト値つきで定義されているので、明示的に値を指定する必要がありません。しかしながら、もし値を指定する必要がある場合、Javaでは2つのスタイルを混ぜて組み合わせることはできません。 + +``` +@SourceURL(value = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends TheirClass ... +``` + +Scalaはこの点においてより柔軟です。 + +``` +@SourceURL("http://coders.com/", + mail = "support@coders.com") + class MyScalaClass ... +``` diff --git a/_ja/tour/automatic-closures.md b/_ja/tour/automatic-closures.md new file mode 100644 index 0000000000..29ae0a8c00 --- /dev/null +++ b/_ja/tour/automatic-closures.md @@ -0,0 +1,62 @@ +--- +layout: tour +title: Automatic Type-Dependent Closure Construction +language: ja + +discourse: true + +partof: scala-tour +--- + +Scala allows parameterless function names as parameters of methods. When such a method is called, the actual parameters for parameterless function names are not evaluated and a nullary function is passed instead which encapsulates the computation of the corresponding parameter (so-called *call-by-name* evaluation). + +The following code demonstrates this mechanism: + + object TargetTest1 extends Application { + def whileLoop(cond: => Boolean)(body: => Unit): Unit = + if (cond) { + body + whileLoop(cond)(body) + } + var i = 10 + whileLoop (i > 0) { + println(i) + i -= 1 + } + } + +The function whileLoop takes two parameters `cond` and `body`. When the function is applied, the actual parameters do not get evaluated. But whenever the formal parameters are used in the body of `whileLoop`, the implicitly created nullary functions will be evaluated instead. Thus, our method `whileLoop` implements a Java-like while-loop with a recursive implementation scheme. + +We can combine the use of [infix/postfix operators](operators.html) with this mechanism to create more complex statements (with a nice syntax). + +Here is the implementation of a loop-unless statement: + + object TargetTest2 extends Application { + def loop(body: => Unit): LoopUnlessCond = + new LoopUnlessCond(body) + protected class LoopUnlessCond(body: => Unit) { + def unless(cond: => Boolean) { + body + if (!cond) unless(cond) + } + } + var i = 10 + loop { + println("i = " + i) + i -= 1 + } unless (i == 0) + } +The `loop` function just accepts a body of a loop and returns an instance of class `LoopUnlessCond` (which encapsulates this body object). Note that the body didn't get evaluated yet. Class `LoopUnlessCond` has a method `unless` which we can use as a *infix operator*. This way, we achieve a quite natural syntax for our new loop: `loop { < stats > } unless ( < cond > )`. + +Here's the output when `TargetTest2` gets executed: + + i = 10 + i = 9 + i = 8 + i = 7 + i = 6 + i = 5 + i = 4 + i = 3 + i = 2 + i = 1 diff --git a/_ja/tour/basics.md b/_ja/tour/basics.md new file mode 100644 index 0000000000..b14330f2b3 --- /dev/null +++ b/_ja/tour/basics.md @@ -0,0 +1,325 @@ +--- +layout: tour +title: 基本 +language: ja + +discourse: true + +partof: scala-tour + +num: 2 +next-page: unified-types +previous-page: tour-of-scala + +redirect_from: "/tutorials/tour/basics.html" +--- + +このページでは、Scalaの基本を取り扱います。 + +## Scalaをブラウザで試してみる + +ScalaFiddleを利用することでブラウザ上でScalaを実行することができます。 + +1. [https://scalafiddle.io](https://scalafiddle.io)を開きます。 +2. 左側のパネルに`println("Hello, world!")`を貼り付けます。 +3. "Run"ボタンを押すと、右側のパネルに出力が表示されます。 + +このサイトを使えば、簡単にセットアップせずScalaのコードの一部を試すことができます。 + +このドキュメントの多くのコードの例はScalaFiddleで開発されています。 +そのため、サンプルコード内のRunボタンをクリックするだけで、そのまま簡単にコードを試すことができます。 + +## 式 + +式は計算可能な文です。 + +``` +1 + 1 +``` +`println`を使うことで、式の結果を出力できます。 + +{% scalafiddle %} +```tut +println(1) // 1 +println(1 + 1) // 2 +println("Hello!") // Hello! +println("Hello," + " world!") // Hello, world! +``` +{% endscalafiddle %} + +### 値 + +`val`キーワードを利用することで、式の結果に名前を付けることができます。 + +```tut +val x = 1 + 1 +println(x) // 2 +``` + +ここでいうところの `x` のように、名前をつけられた結果は 値 と呼ばれます。 +値を参照した場合、その値は再計算されません。 + +値は再代入することができません。 + +```tut:fail +x = 3 // この記述はコンパイルされません。 +``` + +値の型は推測可能ですが、このように型を明示的に宣言することもできます。 + +```tut +val x: Int = 1 + 1 +``` + +型定義では`Int` は識別子`x`の後にくることに注意してください。そして`:`も必要となります。 + +### 変数 + +再代入ができることを除けば、変数は値と似ています。 +`var`キーワードを使うことで、変数は定義できます。 + +```tut +var x = 1 + 1 +x = 3 // "x"は"var"キーワードで宣言されているので、これはコンパイルされます。 +println(x * x) // 9 +``` + +値と同様に、型を宣言したければ、明示的に型を宣言することができます。 + +```tut +var x: Int = 1 + 1 +``` + + +## ブロック + +`{}`で囲むことで式をまとめることができます。これをブロックと呼びます。 + +ブロックの最後の式の結果はブロック全体の結果にもなります。 + +```tut +println({ + val x = 1 + 1 + x + 1 +}) // 3 +``` + +## 関数 + +関数はパラメーターを受け取る式です。 +ここでは与えられた数値に1を足す無名関数(すなわち名前が無い関数)を宣言しています。 + +```tut +(x: Int) => x + 1 +``` +`=>` の左側はパラメーターのリストです。右側はパラメーターを含む式です。 + +関数には名前をつけることもできます。 + +{% scalafiddle %} +```tut +val addOne = (x: Int) => x + 1 +println(addOne(1)) // 2 +``` +{% endscalafiddle %} + +関数は複数のパラメーターをとることもできます。 + +{% scalafiddle %} +```tut +val add = (x: Int, y: Int) => x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +またパラメーターを取らないこともありえます。 + +```tut +val getTheAnswer = () => 42 +println(getTheAnswer()) // 42 +``` + +## メソッド + +メソッドは関数と見た目、振る舞いがとても似ていますが、それらには違いがいくつかあります。 + +メソッドは `def` キーワードで定義されます。 `def` の後ろには名前、パラメーターリスト、戻り値の型、処理の内容が続きます。 + +{% scalafiddle %} +```tut +def add(x: Int, y: Int): Int = x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +戻り値の型は引数リストとコロンの「後ろ」に宣言することに注意してください。`: Int` + +メソッドは複数のパラメーターリストを受け取ることができます。 + +{% scalafiddle %} +```tut +def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier +println(addThenMultiply(1, 2)(3)) // 9 +``` +{% endscalafiddle %} + +また、パラメーターリストを一切受け取らないこともあります。 + +```tut +def name: String = System.getProperty("user.name") +println("Hello, " + name + "!") +``` +メソッドと関数には他にも違いがありますが、今のところは同じようなものと考えて大丈夫です。 + +メソッドは複数行の式も持つことができます。 +```tut +def getSquareString(input: Double): String = { + val square = input * input + square.toString +} +``` +メソッド本体にある最後の式はメソッドの戻り値になります。(Scalaには`return`キーワードはありますが、めったに使われません。) + +## クラス + +`class` キーワードとその後ろに名前、コンストラクタパラメーターを続けることで、クラスを定義することができます。 + +```tut +class Greeter(prefix: String, suffix: String) { + def greet(name: String): Unit = + println(prefix + name + suffix) +} +``` +`greet` メソッドの戻り値の型は`Unit`です。`Unit`は戻り値として意味がないことを示します。 +それはJavaやC言語の`void`と似たような使われ方をします。(`void`との違いは、全てのScalaの式は値を持つ必要があるため、 +実はUnit型のシングルトンで`()`と書かれる値があります。その値には情報はありません。) + +`new` キーワードを使うことで、クラスのインスタンスを生成することができます。 + +```tut +val greeter = new Greeter("Hello, ", "!") +greeter.greet("Scala developer") // Hello, Scala developer! +``` + +クラスについては[後で](classes.html)詳しく取り扱います。 + +## ケースクラス + +Scalaには"ケース"クラスという特別な種類のクラスがあります。デフォルトでケースクラスは不変であり、値で比較されます。 +`case class` キーワードを利用して、ケースクラスを定義できます。 + +```tut +case class Point(x: Int, y: Int) +``` +ケースクラスは、`new` キーワードなしでインスタンス化できます。 + +```tut +val point = Point(1, 2) +val anotherPoint = Point(1, 2) +val yetAnotherPoint = Point(2, 2) +``` + +ケースクラスは値で比較されます。 + +```tut +if (point == anotherPoint) { + println(point + " and " + anotherPoint + " are the same.") +} else { + println(point + " and " + anotherPoint + " are different.") +} // Point(1,2) と Point(1,2) は同じです。 + +if (point == yetAnotherPoint) { + println(point + " and " + yetAnotherPoint + " are the same.") +} else { + println(point + " and " + yetAnotherPoint + " are different.") +} // Point(1,2) と Point(2,2) は異なります。 +``` + +ケースクラスについて紹介すべきことはたくさんあり、あなたはケースクラスが大好きになると確信しています! +それらについては[後で](case-classes.html)詳しく取り扱います。 + +## オブジェクト + +オブジェクトはそれ自体が定義である単一のインスタンスです。そのクラスのシングルトンと考えることもできます。 + +`object`キーワードを利用してオブジェクトを定義することができます。 + +```tut +object IdFactory { + private var counter = 0 + def create(): Int = { + counter += 1 + counter + } +} +``` + +名前を参照してオブジェクトにアクセスすることができます。 + +```tut +val newId: Int = IdFactory.create() +println(newId) // 1 +val newerId: Int = IdFactory.create() +println(newerId) // 2 +``` + +オブジェクトについては [後で](singleton-objects.html)詳しく取り扱います。 + +## トレイト + +トレイトはいくつかのフィールドとメソッドを含む型です。複数のトレイトを結合することもできます。 + +`trait`キーワードでトレイトを定義することができます。 + +```tut +trait Greeter { + def greet(name: String): Unit +} +``` + +トレイトはデフォルトの実装を持つこともできます。 + +{% scalafiddle %} +```tut +trait Greeter { + def greet(name: String): Unit = + println("Hello, " + name + "!") +} +``` + +`extends`キーワードでトレイトを継承することも、`override` キーワードで実装をオーバーライドすることもできます。 + +```tut +class DefaultGreeter extends Greeter + +class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { + override def greet(name: String): Unit = { + println(prefix + name + postfix) + } +} + +val greeter = new DefaultGreeter() +greeter.greet("Scala developer") // Hello, Scala developer! + +val customGreeter = new CustomizableGreeter("How are you, ", "?") +customGreeter.greet("Scala developer") // How are you, Scala developer? +``` +{% endscalafiddle %} + +ここでは、`DefaultGreeter`は一つのトレイトだけを継承していますが、複数のトレイトを継承することもできます。 + +トレイトについては [後で](traits.html)詳しく取り扱います。 + +## メインメソッド + +メインメソッドはプログラムの始点になります。Javaバーチャルマシーンは`main`と名付けられたメインメソッドが必要で、 +それは文字列の配列を一つ引数として受け取ります。 + +オブジェクトを使い、以下のようにメインメソッドを定義することができます。 + +```tut +object Main { + def main(args: Array[String]): Unit = + println("Hello, Scala developer!") +} +``` diff --git a/_ja/tour/by-name-parameters.md b/_ja/tour/by-name-parameters.md new file mode 100644 index 0000000000..86325795ba --- /dev/null +++ b/_ja/tour/by-name-parameters.md @@ -0,0 +1,45 @@ +--- +layout: tour +title: 名前渡しパラメータ +language: ja + +discourse: true + +partof: scala-tour + +num: 31 +next-page: annotations +previous-page: operators + +redirect_from: "/tutorials/tour/by-name-parameters.html" +--- + +*名前渡しのパラメータ*は使用された時に評価されます。それらは*値渡しパラメータ*とは対照的です。名前渡しのパラメータを作るには、単純に`=>`を型の前につけます。 +```tut +def calculate(input: => Int) = input * 37 +``` + +名前渡しパラメータの利点は関数本体の中で使わなければ評価されない点です。一方で、値渡しパラメータの利点は1度しか評価されない点です。 + +こちらはwhileループをどのように実装するかの例です。 + +```tut +def whileLoop(condition: => Boolean)(body: => Unit): Unit = + if (condition) { + body + whileLoop(condition)(body) + } + +var i = 2 + +whileLoop (i > 0) { + println(i) + i -= 1 +} // prints 2 1 +``` + +このメソッド`whileLoop`は条件とループの本体を受け取るために複数パラメータリストを使います。もし`condition`がtrueならば、`body`が実行され、次にwhileLoopが再帰的に呼ばれます。`condition`がfalseならば、bodyは決して評価されません。それは`body`の型の前に`=>`をつけたからです。 + +ここで、`condition`に`i > 0`、`body`に`println(i); i-= 1`を渡した場合、多くの言語で一般的なwhileループと同じ振る舞いをします。 + +パラメータが使われるまで評価を遅延する機能はパフォーマンスの助けになることがあります。それはパラメータを評価するのに多くの計算が必要な場合や、URLの取得のような時間がかかるコードブロックの場合です。 diff --git a/_ja/tour/case-classes.md b/_ja/tour/case-classes.md new file mode 100644 index 0000000000..99a1b6e6f0 --- /dev/null +++ b/_ja/tour/case-classes.md @@ -0,0 +1,66 @@ +--- +layout: tour +title: ケースクラス +language: ja + +discourse: true + +partof: scala-tour + +num: 11 +next-page: pattern-matching +previous-page: multiple-parameter-lists +prerequisite-knowledge: classes, basics, mutability + +redirect_from: "/tutorials/tour/case-classes.html" +--- + +ケースクラスはこれから論じるいくつかの差異はあるものの普通のクラスと似ています。 +ケースクラスは不変なデータを作るのに適しています。 +このツアーの次のステップでは、[パターンマッチング](pattern-matching.html)でのそれらの有用性を解説します。 + +## ケースクラスの宣言 + +最小のケースクラスにはキーワード`case class`、識別子、パラメータリスト(空かもしれません)が必要です。 +```tut +case class Book(isbn: String) + +val frankenstein = Book("978-0486282114") +``` +ケースクラス`Book`をインスタンス化する時キーワード`new`が使われていないことに気をつけてください。 +これはケースクラスがオブジェクトの生成を行う`apply`メソッドを標準で保有するためです。 + +パラメータ有りでケースクラスを作ると、パラメータはパブリックの`val`となります。 +``` +case class Message(sender: String, recipient: String, body: String) +val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?") + +println(message1.sender) // guillaume@quebec.ca が出力されます +message1.sender = "travis@washington.us" // この行はコンパイルされません +``` +`message1.sender` に再代入することはできません、なぜなら`val`(つまりイミュータブル)だからです。 +ケースクラスでは`var`も使うことができますが、推奨されません。 + +## 比較 +ケースクラスは参照ではなく、構造で比較されます。 +``` +case class Message(sender: String, recipient: String, body: String) + +val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val messagesAreTheSame = message2 == message3 // true +``` +たとえ`message2`と`message3`が異なるオブジェクトを参照していたとしても、それぞれのオブジェクトの値は等価となります。 + +## コピー +`copy`メソッドを使うことで簡単にケースクラスのインスタンスの(浅い)コピーを作ることができます。 +必要に応じて、コンストラクタ引数を変更することもできます。 +``` +case class Message(sender: String, recipient: String, body: String) +val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg") +val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr") +message5.sender // travis@washington.us +message5.recipient // claire@bourgogne.fr +message5.body // "Me zo o komz gant ma amezeg" +``` +`message4`のrecipientは`message5`のsenderとして使われますが、`message4`の定数`body`は直接コピーされます。 diff --git a/_ja/tour/classes.md b/_ja/tour/classes.md new file mode 100644 index 0000000000..0253f210f5 --- /dev/null +++ b/_ja/tour/classes.md @@ -0,0 +1,131 @@ +--- +layout: tour +title: クラス +language: ja + +discourse: true + +partof: scala-tour + +num: 4 +next-page: traits +previous-page: unified-types +topics: classes +prerequisite-knowledge: no-return-keyword, type-declaration-syntax, string-interpolation, procedures + +redirect_from: "/tutorials/tour/classes.html" +--- + +Scalaにおけるクラスはオブジェクトを作るための設計図です。 +クラスはメソッド、値、変数、型、オブジェクト、トレイト、クラスを持ち、それらはまとめて _メンバー_ と呼ばれます。 +型、オブジェクト、トレイトはツアーで後ほど取り扱います。 + +## クラスを定義する + +最小のクラス定義は単純にキーワード`class`と識別子だけというものです。 +クラス名は大文字から始まるべきです。 + +```tut +class User + +val user1 = new User +``` +`new`キーワードはクラスのインスタンスを作るために使われます。 +`User`はコンストラクターが定義されていないので、引数なしのデフォルトコンストラクターを持ちます。 +しかしながら、コンストラクターとクラス本体は頻繁に欲しくなるでしょう。 +こちらは位置情報のクラス定義の例になります。 + +```tut +class Point(var x: Int, var y: Int) { + + def move(dx: Int, dy: Int): Unit = { + x = x + dx + y = y + dy + } + + override def toString: String = + s"($x, $y)" +} + +val point1 = new Point(2, 3) +point1.x // 2 +println(point1) // prints (2, 3) +``` +この`Point`クラスは4つのメンバーを持ちます。 +変数`x` と `y` そしてメソッド `move` と `toString`です。 +多くの他の言語とは異なり、プライマリコンストラクタはクラスのシグネチャ`(var x: Int, var y: Int)`です。 +`move` メソッドは2つの整数の引数を受け取り、情報を持たない Unit 値 `()` を返します。 +これは大雑把に言えば、Javaのような言語における`void`に対応します。 +その一方で`toString`は引数を受け取りませんが、`String`の値を返します。 +`toString`は[`AnyRef`](unified-types.html)の`toString`をオーバーライドしているので、`override`キーワードのタグが付いています。 + +## コンストラクター + +コンストラクターは次のようにデフォルト値を与えると省略可能なパラメーターを持つことができます。 + +```tut +class Point(var x: Int = 0, var y: Int = 0) + +val origin = new Point // x と y には共に0がセットされます。 +val point1 = new Point(1) +println(point1.x) // 1 が出力されます。 +``` +このバージョンの`Point`クラスでは、`x` と `y` はデフォルト値0を持ち、引数が必須ではありません。 +しかしながらコンストラクタは引数を左から右に読み込むため、もし`y`の値だけを渡したい場合は、パラメーターに名前をつける必要があります。 + +``` +class Point(var x: Int = 0, var y: Int = 0) +val point2 = new Point(y=2) +println(point2.y) // 2 が出力されます。 +``` + +これは明快さを高めるための良い習慣でもあります。 + +## プライベートメンバーとゲッター/セッター構文 +メンバーはデフォルトではパブリックになります。 +クラスの外から隠したい場合は`private`アクセス修飾詞を使いましょう。 + +```tut +class Point { + private var _x = 0 + private var _y = 0 + private val bound = 100 + + def x = _x + def x_= (newValue: Int): Unit = { + if (newValue < bound) _x = newValue else printWarning + } + + def y = _y + def y_= (newValue: Int): Unit = { + if (newValue < bound) _y = newValue else printWarning + } + + private def printWarning = println("WARNING: Out of bounds") +} + +val point1 = new Point +point1.x = 99 +point1.y = 101 // 警告が出力されます。 +``` +このバージョンの`Point`クラスでは、データはプライベート変数 `_x` と `_y` に保存されます。 +プライベートなデータにアクセスするためのメソッド`def x` と `def y` があります。 +`def x_=` と `def y_=` は `_x` と `_y` の値を検証し設定するためのものになります。 +セッターのための特別な構文に注意してください。 +セッターメソッドはゲッターメソッドの識別子に`_=`を追加し、その後ろにパラメーターを取ります。 + +プライマリコンストラクタの`val` と `var` を持つパラメーターはパブリックになります。 +しかしながら`val` は不変となるため、以下のように記述することはできません。 + +``` +class Point(val x: Int, val y: Int) +val point = new Point(1, 2) +point.x = 3 // <-- コンパイルされません。 +``` + +`val` や `var` が存在しないパラメーターはクラス内でだけで参照できるプライベートな値や変数となります。 +``` +class Point(x: Int, y: Int) +val point = new Point(1, 2) +point.x // <-- コンパイルされません。 +``` diff --git a/_ja/tour/compound-types.md b/_ja/tour/compound-types.md new file mode 100644 index 0000000000..6eeadc481d --- /dev/null +++ b/_ja/tour/compound-types.md @@ -0,0 +1,54 @@ +--- +layout: tour +title: 複合型 +language: ja + +discourse: true + +partof: scala-tour + +num: 24 +next-page: self-types +previous-page: abstract-type-members + +redirect_from: "/tutorials/tour/compound-types.html" +--- +ときどき、あるオブジェクトの型が、複数の他の型のサブタイプであると表現する必要が生じます。 +Scalaでは、これは*複合型*を用いて表現できます。複合型とはオブジェクトの型同士を重ねることです。 + +2つのトレイト`Cloneable`と`Resetable`があるとしましょう。 + +```tut +trait Cloneable extends java.lang.Cloneable { + override def clone(): Cloneable = { + super.clone().asInstanceOf[Cloneable] + } +} +trait Resetable { + def reset: Unit +} +``` + +今、関数`cloneAndReset`を書きたいとします。それはオブジェクトを受け取り、それをクローンして、元のオブジェクトをリセットします。 + +``` +def cloneAndReset(obj: ?): Cloneable = { + val cloned = obj.clone() + obj.reset + cloned +} +``` + +パラメータ`obj`の型は何かという疑問が生じます。もし`Cloneable`であれば、オブジェクトを`clone`することができますが、`reset`することはできません。もし`Resetable`であれば、`reset`することができますが、`clone`の操作はできません。そのような状態で型キャストを回避するために`obj`の型を`Cloneable`と`Resetable`の両方であると指定することができます。Scalaではこの複合型は`Cloneable with Resetable`のように書くことができます。 + +こちらが書き変えた関数です。 + +``` +def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { + //... +} +``` +複合型は複数のオブジェクトの型からなり、一つだけの細別型(refinement)を持てます。細別型は既存オブジェクトのメンバーのシグネチャを絞り込むのに使えます。 +一般的な形は`A with B with C ... { refinement }`です。 + +細別の使い方の例は[ミックスインを用いたクラス合成](mixin-class-composition.html)のページにあります。 diff --git a/_ja/tour/default-parameter-values.md b/_ja/tour/default-parameter-values.md new file mode 100644 index 0000000000..fe2da78c75 --- /dev/null +++ b/_ja/tour/default-parameter-values.md @@ -0,0 +1,50 @@ +--- +layout: tour +title: デフォルト引数 +language: ja + +discourse: true + +partof: scala-tour + +num: 33 +next-page: named-arguments +previous-page: annotations +prerequisite-knowledge: named-arguments, function syntax + +redirect_from: "/tutorials/tour/default-parameter-values.html" +--- + +Scalaはパラメータのデフォルト値を与えることができ、呼び出す側はこれらのパラメータを省略できます。 + +```tut +def log(message: String, level: String = "INFO") = println(s"$level: $message") + +log("System starting") // prints INFO: System starting +log("User not found", "WARNING") // prints WARNING: User not found +``` + +パラメータ`level`はデフォルト値を持つので、省略可能です。最終行では、引数`"WARNING"`はデフォルト値`"INFO"`を上書きします。Javaで同じ効果を得るのに、省略可能なパラメータをもつメソッドを複数使ってメソッドのオーバーロードをしたようなものです。しかしながら呼び出す側が引数をひとつ省略すると、以降全ての引数は名前つきにする必要があります。 + +```tut +class Point(val x: Double = 0, val y: Double = 0) + +val point1 = new Point(y = 1) +``` +ここでは、`y = 1`と書かなければなりません。 + +Scalaで定義したデフォルトパラメータはJavaのコードから呼び出される時はオプショナルではありません。 + +```tut +// Point.scala +class Point(val x: Double = 0, val y: Double = 0) +``` + +```java +// Main.java +public class Main { + public static void main(String[] args) { + Point point = new Point(1); // コンパイルされません + } +} +``` diff --git a/_ja/tour/extractor-objects.md b/_ja/tour/extractor-objects.md new file mode 100644 index 0000000000..2b12283764 --- /dev/null +++ b/_ja/tour/extractor-objects.md @@ -0,0 +1,72 @@ +--- +layout: tour +title: 抽出子オブジェクト +language: ja + +discourse: true + +partof: scala-tour + +num: 16 +next-page: for-comprehensions +previous-page: regular-expression-patterns + +redirect_from: "/tutorials/tour/extractor-objects.html" +--- + +抽出子オブジェクトは`unapply`メソッドを持つオブジェクトです。 +`apply`メソッドが引数を取り、オブジェクトを作るコンストラクタであるように、`unapply`は1つのオブジェクトを受け取り、引数を返そうとします。 +これはパターンマッチングと部分関数で最も頻繁に使われます。 + +```tut +import scala.util.Random + +object CustomerID { + + def apply(name: String) = s"$name--${Random.nextLong}" + + def unapply(customerID: String): Option[String] = { + val stringArray: Array[String] = customerID.split("--") + if (stringArray.tail.nonEmpty) Some(stringArray.head) else None + } +} + +val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908 +customer1ID match { + case CustomerID(name) => println(name) // prints Sukyoung + case _ => println("Could not extract a CustomerID") +} +``` + +`apply`メソッドは`name`から`CustomerID`文字列を作ります。`unapply`は逆に`name`を返します。 + `CustomerID("Sukyoung")`は、`CustomerID.apply("Sukyoung")`を短く書く構文です。 + `case CustomerID(name) => println(name)`では、unapplyメソッドを呼んでいます。 + +値を定義する文で、パターン中に新しい変数を使うことができるので、抽出子は変数を初期化するのに使えます。この場合unapplyメソッドが初期値を与えます。 + +```tut +val customer2ID = CustomerID("Nico") +val CustomerID(name) = customer2ID +println(name) // prints Nico +``` +これは `val name = CustomerID.unapply(customer2ID).get`.と同じです。 + +```tut +val CustomerID(name2) = "--asdfasdfasdf" +``` +もし一致しない場合`scala.MatchError`が投げられます。 + +```tut:fail +val CustomerID(name3) = "-asdfasdfasdf" +``` + +`unapply`の戻り値型は以下のように選ばれなければなりません。 + +* ただのテストであれば、`Boolean`を返します。例えば`case even()`。 +* T型のサブバリュー1つを返すのであれば、`Option[T]`を返します。 +* いくつかのサブバリュー`T1,...,Tn`を返したいのであれば、オプショナルタプル`Option[(T1,...,Tn)]`でグループ化します。 + +時々、抽出する値の数が確定せず、入力に応じて任意の数の値を返したいことがあります。 +この場合は、`Option[Seq[T]]`を返す`unapplySeq`メソッドを持つ抽出子を定義することができます。 +これらのパターンと同様の例として以下のものがあります。 +`case List(x, y, z) =>`を使って`List`を分解する例や`case r(name, remainingFields @ _*) =>`.のように正規表現`Regex`を使って`String`を分解する例です。 diff --git a/_ja/tour/for-comprehensions.md b/_ja/tour/for-comprehensions.md new file mode 100644 index 0000000000..89bce4d42a --- /dev/null +++ b/_ja/tour/for-comprehensions.md @@ -0,0 +1,77 @@ +--- +layout: tour +title: for内包表記 +language: ja + +discourse: true + +partof: scala-tour + +num: 17 +next-page: generic-classes +previous-page: extractor-objects + +redirect_from: "/tutorials/tour/for-comprehensions.html" +--- + +Scalaは*シーケンス内包表記*を表現するための軽量な記法を提供します。 +内含表記は`for (enumerators) yield e`という形をとります。`enumerators`はセミコロンで区切られたEnumeratorのリストを指します。 +1つの*enumerator*は新しい変数を導き出すジェネレータかフィルタのどちらかです。 +内包表記はenumeratorsが生成する束縛一つ一つについて本体`e`を評価し、これらの値のシーケンスを返します。 + +こちらは例です。 + +```tut +case class User(name: String, age: Int) + +val userBase = List(User("Travis", 28), + User("Kelly", 33), + User("Jennifer", 44), + User("Dennis", 23)) + +val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30)) + yield user.name // これをリストに追加する + +twentySomethings.foreach(name => println(name)) // prints Travis Dennis +``` +この`yield`文と一緒に使われている`for`ループは実際には`List`を生成します。 +`yield user.name`を返しているので、型は`List[String]`になります。 +`user <- userBase`はジェネレータであり、`if (user.age >=20 && user.age < 30)`は20代ではないユーザーをフィルターするガードです。 + +こちらは2つのジェネレータを使ったより複雑な例です。 +合計が与えられた値`v`と等しくなる、`0`から`n-1`の全てのペアを計算します。 + +```tut +def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- 0 until n if i + j == v) + yield (i, j) + +foo(10, 10) foreach { + case (i, j) => + println(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1) +} + +``` +この例では`n == 10`で`v == 10`です。最初のイテレーションでは`i == 0`かつ`j == 0`で、`i + j != v`となるので、何も生成されません。 +`i`が`1`にインクリメントされるまでに、`j`はあと9回インクリメントされます。`if`ガードがなければ、単純に以下の内容が表示されます。 + +``` + +(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 0) ... +``` +内包表記はリストだけのものではありません。 +操作 `withFilter`、`map`、`flatMap`を(適切な型で)サポートする全てのデータ型は、シーケンス内包表記で使うことができます。 + +内包表記の中では`yield`を省略することができます。その場合、内包表記は`Unit`を返します。 +これは副作用をもたらす必要があるときに役立ちます。 +こちらは先に出たプログラムと同等のものですが、`yield`を使っていません。 + +```tut +def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- 0 until n if i + j == v) + println(s"($i, $j)") + +foo(10, 10) +``` diff --git a/_ja/tour/generic-classes.md b/_ja/tour/generic-classes.md new file mode 100644 index 0000000000..2535e31695 --- /dev/null +++ b/_ja/tour/generic-classes.md @@ -0,0 +1,65 @@ +--- +layout: tour +title: ジェネリッククラス +language: ja + +discourse: true + +partof: scala-tour + +num: 18 +next-page: variances +previous-page: for-comprehensions +assumed-knowledge: classes unified-types + +redirect_from: "/tutorials/tour/generic-classes.html" +--- +ジェネリッククラスはパラメータとして型を1つ受け取るクラスです。それらはコレクションクラスで特に役立ちます。 + +## ジェネリッククラスの定義 +ジェネリッククラスは角カッコ`[]`の中にパラメータとして型を1つ受け取ります。 +型パラメータの識別子として文字`A`を使う習慣がありますが、任意のパラメータ名を使うことができます。 +```tut +class Stack[A] { + private var elements: List[A] = Nil + def push(x: A) { elements = x :: elements } + def peek: A = elements.head + def pop(): A = { + val currentTop = peek + elements = elements.tail + currentTop + } +} +``` +この`Stack`クラスの実装はパラメータとして任意の型`A`を受け取ります。 +これはメンバーのリスト`var elements: List[A] = Nil`が型`A`の要素のみを格納できることを意味します。 +手続き`def push`は型`A`のオブジェクトのみを受け取ります +(注: `elements = x :: elements`は、`x`を現在の`elements`の先頭に追加した新しいリストを`elements`に割り当て直します)。 + +## 使い方 + +ジェネリッククラスを使うには、角カッコの中に`A`の代わりに型を入れます。 +``` +val stack = new Stack[Int] +stack.push(1) +stack.push(2) +println(stack.pop) // prints 2 +println(stack.pop) // prints 1 +``` +インスタンス`stack`はIntのみを受け取ることができます。 +しかしながら、型がサブタイプを持つ場合、それらは以下のように渡すことができます。 +``` +class Fruit +class Apple extends Fruit +class Banana extends Fruit + +val stack = new Stack[Fruit] +val apple = new Apple +val banana = new Banana + +stack.push(apple) +stack.push(banana) +``` +クラス`Apple`と`Banana`は共に`Fruit`を継承しています。そのため`Fruit`のスタックには`apple`と`banana`のインスタンスを追加できます。 + +_注意: ジェネリック型のサブタイプは*非変(invariant)*です。つまり`Stack[Char]`型の文字スタックがあるとき、それを`Stack[Int]`型の整数スタックとして使うことはできません。文字スタックに整数を入れることはできるので、このことは変に思えるかもしれません。結論としては、`B = A`の場合に限り、`Stack[A]`は`Stack[B]`の唯一のサブタイプとなります。これでは制限が強いので、ジェネリック型のサブタイプの振る舞いをコントロールするために、Scalaは[型引数アノテーションの仕組み](variances.html)を提供します。_ diff --git a/_ja/tour/higher-order-functions.md b/_ja/tour/higher-order-functions.md new file mode 100644 index 0000000000..57714202e8 --- /dev/null +++ b/_ja/tour/higher-order-functions.md @@ -0,0 +1,121 @@ +--- +layout: tour +title: 高階関数 +language: ja + +discourse: true + +partof: scala-tour + +num: 8 +next-page: nested-functions +previous-page: mixin-class-composition + +redirect_from: "/tutorials/tour/higher-order-functions.html" +--- + +高階関数は他の関数をパラメーターとして受け取る、もしくは結果として関数を返します。 +このようなことができるのは、Scalaでは関数が第一級値 (first-classs value) だからです。 +用語が少し紛らわしいかもしれませんが、 +ここでは"高階関数"というフレーズを関数をパラメーターとして受け取る、または関数を返すメソッドと関数の両方に対して使います。 + +もっとも一般的な例の1つは、Scalaのコレクションで利用可能な高階関数`map`です。 +```tut +val salaries = Seq(20000, 70000, 40000) +val doubleSalary = (x: Int) => x * 2 +val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) +``` +`doubleSalary`はInt`x`を1つだけ受け取り、`x * 2`を返す関数です。 +一般的に、アロー`=>`の左側のタプルは引数リストであり、右側の式の値が返されます。 +3行目で、給与のリストのそれぞれの値に`doubleSalary`が適用されます。 + +コードを減らすため、以下のように無名関数を作ることができ、引数として直接mapに渡すことができます +``` +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000) +``` +上記例では`x`をIntとして宣言していないことに注意してください。 +それはmap関数が期待する型を基にコンパイラーが型を推論できるからです。 +さらに言えば、慣用的には同じコードを以下のように書きます。 + +```tut +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(_ * 2) +``` +Scalaコンパイラはパラメーターの型を(Intが1つだけと)既に知っているため、関数の右側を提供するだけでよいです。 +唯一の注意点はパラメータ名の代わりに`_`を使う必要があるということです(先の例では`x`でした)。 + +## メソッドを関数に強制変換 +高階関数には引数としてとしてメソッドを渡すことも可能で、それはScalaコンパイラがメソッドを関数に強制変換するからです。 +``` +case class WeeklyWeatherForecast(temperatures: Seq[Double]) { + + private def convertCtoF(temp: Double) = temp * 1.8 + 32 + + def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- convertCtoFメソッドが渡されます +} +``` +ここで、メソッド`convertCtoF`が`forecastInFahrenheit`に渡されています。 +これはコンパイラが`convertCtoF`を関数`x => convertCtoF(x)`(注意点:`x`はスコープ内でユニークであることが保証された名前になります)に変換することで実現します。 + +## 関数を受け取る関数 +高階関数を使う理由の1つは余分なコードを削減することです。 +たとえば、何通りかの係数で人の給料を上げるメソッドが欲しいとしましょう。 +高階関数を作らないなら、こんな感じになるかもしれません。 + +```tut +object SalaryRaiser { + + def smallPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * 1.1) + + def greatPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * salary) +} +``` + +3つのメソッドはそれぞれ掛け算の係数のみ異なることに気をつけてください。 +簡潔にするため、以下のように繰り返されているコードを高階関数に抽出することができます。 + +```tut +object SalaryRaiser { + + private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] = + salaries.map(promotionFunction) + + def smallPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * 1.1) + + def bigPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * salary) +} +``` +新しいメソッド`promotion`はsalariesと`Double => Double`型の関数(すなわち、Doubleを受け取り、Doubleを返す関数)を受け取り、積を返します。 + +## 関数を返す関数 + +関数を生成したい場合がいくつかあります。 +こちらは関数を返すメソッドの例になります。 + +```tut +def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { + val schema = if (ssl) "https://" else "http://" + (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" +} + +val domainName = "www.example.com" +def getURL = urlBuilder(ssl=true, domainName) +val endpoint = "users" +val query = "id=1" +val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String +``` + +urlBuilderの戻り値型`(String, String) => String`に注意してください。 +これは返される無名関数はStringを2つ受け取り、Stringを1つ返すことを意味します。 +このケースでは返される無名関数は`(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"`です。 diff --git a/_ja/tour/implicit-conversions.md b/_ja/tour/implicit-conversions.md new file mode 100644 index 0000000000..386ed3c500 --- /dev/null +++ b/_ja/tour/implicit-conversions.md @@ -0,0 +1,62 @@ +--- +layout: tour +title: 暗黙の変換 +language: ja + +discourse: true + +partof: scala-tour + +num: 27 +next-page: polymorphic-methods +previous-page: implicit-parameters + +redirect_from: "/tutorials/tour/implicit-conversions.html" +--- + +型`S`から型`T`への暗黙の変換は`S => T`という型のimplicit値や、その型に一致するimplicitメソッドで定義されます。 + +暗黙の変換は2つの状況で適用されます。 + +* もし式`e`が型`S`であり、`S`は式の期待する型`T`に適合しない場合 +* 型`S`の`e`を使う表記`e.m`があって、セレクター`m`が`S`のメンバーではない場合 + +最初のケースでは、`e`を渡せて、戻り値の型が`T`に適合するような変換`c`を検索します。 +2つ目のケースでは、`e`を渡せて、戻り値が`m`というメンバーを持つような変換`c`を検索します。 + +implicitなメソッド`List[A] => Ordered[List[A]]`と`Int => Ordered[Int]`がスコープの中にあれば、`List[Int]`型の2つのリストにおける以下の処理は正当なものになります。 + +``` +List(1, 2, 3) <= List(4, 5) +``` +implicitなメソッド`Int => Ordered[Int]`は`scala.Predef.intWrapper`を通じて自動的に提供されます。implicitなメソッドの例`List[A] => Ordered[List[A]]`は以下にあります。 + +```tut +import scala.language.implicitConversions + +implicit def list2ordered[A](x: List[A]) + (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = + new Ordered[List[A]] { + //replace with a more useful implementation + def compare(that: List[A]): Int = 1 + } +``` +暗黙にインポートされているオブジェクト`scala.Predef`は、頻繁に使われる型(例えば`scala.collection.immutable.Map`は`Map`と別名づけられます)とメソッド(例えば`assert`)といくつかの暗黙の型変換を宣言しています。 + +例えば、`java.lang.Integer`を受け取るようなJavaのメソッドを呼び出す時、自由に`scala.Int`を代わりに渡すことができます。それはPredefオブジェクトが以下の暗黙の変換をを含んでいるからです。 + +```tut +import scala.language.implicitConversions + +implicit def int2Integer(x: Int) = + java.lang.Integer.valueOf(x) +``` + +暗黙の変換は見境なく使われると落とし穴になり得るため、暗黙の変換の定義をコンパイルしている時にコンパイラは警告を出します。 + +警告をオフにするには、次のいずれかの措置を講じてください。 + +* 暗黙の変換定義のスコープに`scala.language.implicitConversions`をインポートする。 +* コンパイラを`-language:implicitConversions`をつけて起動する + +コンパイラにより変換が適用された時、警告は出ません。 diff --git a/_ja/tour/implicit-parameters.md b/_ja/tour/implicit-parameters.md new file mode 100644 index 0000000000..38067f9e27 --- /dev/null +++ b/_ja/tour/implicit-parameters.md @@ -0,0 +1,75 @@ +--- +layout: tour +title: 暗黙のパラメータ +language: ja + +discourse: true + +partof: scala-tour + +num: 26 +next-page: implicit-conversions +previous-page: self-types + +redirect_from: "/tutorials/tour/implicit-parameters.html" +--- + +メソッドは _暗黙の_ パラメータのリストを持つことができ、パラメータリストの先頭には _implicit_ キーワードで印をつけます。 +もしそのパラメータリストの中のパラメータがいつものように渡らなければ、Scalaは正しい型の暗黙値を受け取ることができるかを確認し、可能であればそれを自動的に渡します。 + +Scalaがこれらのパラメータを探す場所は2つのカテゴリに分かれます。 + +* Scalaはまず最初に暗黙のパラメータブロックを持つメソッドが呼び出されている箇所で、直接(プレフィックスなしに)アクセスできる暗黙の定義と暗黙のパラメータを探します。 +* 次に、候補となる型に関連づけられた全てのコンパニオンオブジェクトの中でimplicitと宣言されているメンバーを探します。 + +Scalaがimplicitをどこから見つけるかについてのより詳しいガイドは[FAQ](//docs.scala-lang.org/tutorials/FAQ/finding-implicits.html)で見ることができます。 + +以下の例では、モノイドの`add`と`unit`の演算を使い、要素のリストの合計を計算するメソッド`sum`を定義しています。 +implicitの値をトップレベルには置けないことに注意してください。 + +```tut +abstract class Monoid[A] { + def add(x: A, y: A): A + def unit: A +} + +object ImplicitTest { + implicit val stringMonoid: Monoid[String] = new Monoid[String] { + def add(x: String, y: String): String = x concat y + def unit: String = "" + } + + implicit val intMonoid: Monoid[Int] = new Monoid[Int] { + def add(x: Int, y: Int): Int = x + y + def unit: Int = 0 + } + + def sum[A](xs: List[A])(implicit m: Monoid[A]): A = + if (xs.isEmpty) m.unit + else m.add(xs.head, sum(xs.tail)) + + def main(args: Array[String]): Unit = { + println(sum(List(1, 2, 3))) // intMonoidを暗に使用 + println(sum(List("a", "b", "c"))) // stringMonoidを暗に使用 + } +} +``` +`Monoid`はここでは`add`と呼ばれる処理を定義します。この処理は`A`のペアを結合して、別の`A`を返します。また、(特定の)`A`を作ることができる`unit`と呼ばれる処理も定義します。 + +暗黙のパラメータがどのように働くかを見るため、まずは文字列と整数のためにそれぞれモノイド`stringMonoid`と`intMonoid`を定義します。`implicit`キーワードは対応するオブジェクトが暗黙に使われうることを指し示します。 + +メソッド`sum`は`List[A]`を受け取り、`A`を返します。このメソッドは初期値`A`を`unit`から受け取り、リスト中の`A`を順番に`add`メソッドで結合します。ここでパラメータ`m`をimplicitにしているのは、そのメソッドを呼び出すとき、Scalaが暗黙のパラメータ`m`として暗黙の`Monoid[A]`を見つけることができるなら、私達は`xs`パラメータを提供するだけで良いということです。 + +`main`メソッドでは`sum`を2回呼んでいて、`xs`パラメータだけを渡しています。するとScalaは先に言及したスコープの中でimplicitを探します。最初の`sum`の呼び出しは`xs`として`List[Int]`を渡します。それは `A`が`Int`であることを意味します。暗黙のパラメータリスト`m`が省略されているので、Scalaは暗黙の`Monoid[Int]`を探します。最初の探索ルールはこうでした。 + +> Scalaはまず最初に暗黙のパラメータブロックを持つメソッドが呼び出されている箇所で、直接(プレフィックスなしに)アクセスできる暗黙の定義と暗黙のパラメータを探します。 + +`intMonoid`は`main`の中で直接アクセスできる暗黙の定義です。型も一致しているので、`sum`メソッドに自動的に渡されます。 + +`sum`の2回目の呼び出しは`List[String]`を渡します。それは`A`は`String`であることを意味します。暗黙の値の探索は`Int`の時と同様に動きますが、今回は `stringMonoid`を見つけ、`m`として自動的に渡します。 + +そのプログラムは以下を出力します。 +``` +6 +abc +``` diff --git a/_ja/tour/inner-classes.md b/_ja/tour/inner-classes.md new file mode 100644 index 0000000000..4bc373d8c8 --- /dev/null +++ b/_ja/tour/inner-classes.md @@ -0,0 +1,91 @@ +--- +layout: tour +title: 内部クラス +language: ja + +discourse: true + +partof: scala-tour + +num: 22 +next-page: abstract-type-members +previous-page: lower-type-bounds + +redirect_from: "/tutorials/tour/inner-classes.html" +--- + +Scalaではクラスが他のクラスをメンバーとして保持することが可能です。 +Javaのような、内部クラスが外側のクラスのメンバーとなる言語とは対照的に、Scalaでは、内部クラスは外側のオブジェクトに束縛されます。 +どのノードがどのグラフに属しているのかを私達が混同しないように、コンパイラがコンパイル時に防いでほしいのです。 +パス依存型はその解決策の1つです。 + +その違いを示すために、グラフデータ型の実装をさっと書きます。 + +```tut +class Graph { + class Node { + var connectedNodes: List[Node] = Nil + def connectTo(node: Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` +このプログラムはグラフをノードのリスト(`List[Node]`)で表現しています。いずれのノードも接続している他のノードへのリスト(`connectedNodes`)を保持します。`class Node`は `class Graph`の中にネストしているので、 _パス依存型_ です。 +そのため`connectedNodes`の中にある全てのノードは同じ`Graph`インスタンスから`newNode`を使用して作る必要があります。 + +```tut +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +val node3: graph1.Node = graph1.newNode +node1.connectTo(node2) +node3.connectTo(node1) +``` +わかりやすくするため`node1`、`node2`、`node3`の型を`graph1.Node`と明示的に宣言しましたが、なくてもコンパイラは推論できます。 +これは`new Node`を呼んでいる`graph1.newNode`を呼び出す時、メソッドが`Node`のインスタンス`graph1`を使用しているからです。 + +2つのグラフがあるとき、Scalaの型システムは1つのグラフの中で定義されたノードと別のグラフで定義されたノードを混ぜることを許しません。 +それは別のグラフのノードは別の型を持つからです。 +こちらは不正なプログラムです。 + +``` +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +node1.connectTo(node2) // legal +val graph2: Graph = new Graph +val node3: graph2.Node = graph2.newNode +node1.connectTo(node3) // illegal! +``` +型`graph1.Node`は`graph2.Node`とは異なります。Javaであれば先のプログラム例の最後の行は正しいでしょう。 +2つのグラフのノードに対して、Javaは同じ型`Graph.Node`を指定します。つまりクラス`Graph`は`Node`の接頭辞です。 +Scalaではそのような型も同様に表現することができ、`Graph#Node`と書きます。 +もし他のグラフのノードに接続できるようにしたければ、以下の方法で最初のグラフ実装の定義を変える必要があります。 + +```tut +class Graph { + class Node { + var connectedNodes: List[Graph#Node] = Nil + def connectTo(node: Graph#Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` diff --git a/_ja/tour/lower-type-bounds.md b/_ja/tour/lower-type-bounds.md new file mode 100644 index 0000000000..d461e31e5d --- /dev/null +++ b/_ja/tour/lower-type-bounds.md @@ -0,0 +1,76 @@ +--- +layout: tour +title: 下限型境界 +language: ja + +discourse: true + +partof: scala-tour + +num: 21 +next-page: inner-classes +previous-page: upper-type-bounds +prerequisite-knowledge: upper-type-bounds, generics, variance + +redirect_from: "/tutorials/tour/lower-type-bounds.html" +--- + + [上限型境界](upper-type-bounds.html) は型を別の型のサブタイプに制限しますが、*下限型境界*は型が別の型のスーパータイプであることを宣言します。表現`B >: A`はパラメータ`B`または抽象型`B`が型`A`のスーパータイプであることを表します。ほとんどのケースで`A`はそのクラスの型パラメータであり、`B`はメソッドの型パラメータになります。 + +以下はこれが役立つ場合の例です。 + +```tut:fail +trait Node[+B] { + def prepend(elem: B): Node[B] +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend(elem: B): ListNode[B] = ListNode(elem, this) + def head: B = h + def tail: Node[B] = t +} + +case class Nil[+B]() extends Node[B] { + def prepend(elem: B): ListNode[B] = ListNode(elem, this) +} +``` + +このプログラムは片方向リストを実装します。`Nil`は空の要素(すなわち空のリスト)を意味します。 +`class ListNode`は型`B` (`head`)の要素と、リストの残りの部分(`tail`)への参照を持つノードです。 +`class Node`とそのサブタイプは、`+B`とあるので、共変です。 + +しかしながら、このプログラムはコンパイル _されません_。`prepend`のパラメータ`elem`が、宣言時に*共* 変と宣言した型`B`になっているからです。 +なぜ通らないかというと、関数はそれらの型パラメータに対して*反*変であり、それらの結果型に対して*共*変だからです。 + +これを解決するためには、`prepend`のパラメータ`elem`の型の変位指定を逆転させる必要があります。 +これを実現するには、下限型境界として`B`を持つ新しい型パラメータ`U`を導入します。 + +```tut +trait Node[+B] { + def prepend[U >: B](elem: U): Node[U] +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) + def head: B = h + def tail: Node[B] = t +} + +case class Nil[+B]() extends Node[B] { + def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) +} +``` + +すると、以下のようなことができます。 +```tut +trait Bird +case class AfricanSwallow() extends Bird +case class EuropeanSwallow() extends Bird + + +val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil()) +val birdList: Node[Bird] = africanSwallowList +birdList.prepend(new EuropeanSwallow) +``` +`Node[Bird]`は`africanSwallowList`をアサインできますが、その後`EuropeanSwallow`を受け入れられます。 + diff --git a/_ja/tour/mixin-class-composition.md b/_ja/tour/mixin-class-composition.md new file mode 100644 index 0000000000..9ec9fd65f4 --- /dev/null +++ b/_ja/tour/mixin-class-composition.md @@ -0,0 +1,84 @@ +--- +layout: tour +title: ミックスインを用いたクラス合成 +language: ja + +discourse: true + +partof: scala-tour + +num: 7 +next-page: higher-order-functions +previous-page: tuples +prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types + +redirect_from: "/tutorials/tour/mixin-class-composition.html" +--- +ミックスインはクラスを構成するのに使われるトレイトです。 + +```tut +abstract class A { + val message: String +} +class B extends A { + val message = "I'm an instance of class B" +} +trait C extends A { + def loudMessage = message.toUpperCase() +} +class D extends B with C + +val d = new D +println(d.message) // I'm an instance of class B +println(d.loudMessage) // I'M AN INSTANCE OF CLASS B +``` +クラス`D`は スーパークラスを`B` とし、 ミックスイン`C`を持ちます。 +クラスは1つだけしかスーパークラスを持つことができませんが、ミックスインは複数持つことができます(キーワードはそれぞれ`extends`と`with`を使います)。 +ミックスインとスーパークラスは同じスーパータイプを持つことができます。 + +それでは抽象クラスから始まる興味深い例を見てみましょう。 + +```tut +abstract class AbsIterator { + type T + def hasNext: Boolean + def next(): T +} +``` +クラスは抽象型`T`と標準的なイテレーターのメソッドを持ちます。 +次に、(全ての抽象メンバー`T`, `hasNext`, `next`が実装を持つ)具象クラスを実装します。 + +```tut +class StringIterator(s: String) extends AbsIterator { + type T = Char + private var i = 0 + def hasNext = i < s.length + def next() = { + val ch = s charAt i + i += 1 + ch + } +} +``` +`StringIterator`は`String`を受け取り、そのStringを反復処理するために使われます。 +(例:Stringに特定の文字が含まれているかを確認するために) + +それでは`AbsIterator`を継承したトレイトも作ってみましょう。 + +```tut +trait RichIterator extends AbsIterator { + def foreach(f: T => Unit): Unit = while (hasNext) f(next()) +} +``` +このトレイトは(`while (hasNext)`で)要素がある限り、与えられた関数 `f: T => Unit`を次の要素(`next()`)に対し連続して呼び出す`foreach`メソッドを実装しています。 +`RichIterator`はトレイトなので、`RichIterator`はAbsIteratorの抽象メンバーを実装する必要がありません。 + +`StringIterator`と`RichIterator`の機能を1つのクラスに組み合わせてみましょう。 +```tut +class RichStringIter extends StringIterator("Scala") with RichIterator +val richStringIter = new RichStringIter +richStringIter foreach println +``` +新しいクラス`RichStringIter`は`StringIterator`をスーパークラスとし、`RichIterator`をミックスインとしています。 + +単一継承ではこのレベルの柔軟性を達成することはできないでしょう。 diff --git a/_ja/tour/multiple-parameter-lists.md b/_ja/tour/multiple-parameter-lists.md new file mode 100644 index 0000000000..a35bccf615 --- /dev/null +++ b/_ja/tour/multiple-parameter-lists.md @@ -0,0 +1,79 @@ +--- +layout: tour +title: 複数パラメータリスト(カリー化) +language: ja + +discourse: true + +partof: scala-tour + +num: 10 +next-page: case-classes +previous-page: nested-functions + +redirect_from: "/tutorials/tour/multiple-parameter-lists.html" +--- + +メソッドは複数のパラメータリストを持てます。 + +# 例 + +こちらはScalaのコレクションAPIの `TraversableOnce`トレイトで定義されている実例です。 + +``` +def foldLeft[B](z: B)(op: (B, A) => B): B +``` +`foldLeft`は、2つのパラメータを取る関数`op`を、初期値`z`とこのコレクションの全要素に対して左から右に適用していきます。 +以下はその使い方の例です。 + +初期値0から始まり、`foldLeft`はここではリスト内の各要素とその一つ前の累積値に関数`(m, n) => m + n`を適用します。 + +{% scalafiddle %} +```tut +val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val res = numbers.foldLeft(0)((m, n) => m + n) +println(res) // 55 +``` +{% endscalafiddle %} + +### ユースケース +推奨される複数パラメータリストのユースケースは次の通りです。 + +#### パラメータに関数を一つ渡す場合 +パラメータに関数を一つだけ渡すのであれば、上記の`foldLeft`のケースでの`op`のように、複数パラメータリストを利用して簡潔な構文でメソッドに無名関数を渡すことができます。 +複数パラメータリストがない場合、このコードは以下のようになります。 + + +``` +numbers.foldLeft(0, (m: Int, n: Int) => m + n) +``` + +複数パラメータリストを使うことで、Scalaの型インターフェースの利点を享受でき、以下のようにコードをより簡潔にすることができるのです。 + +``` +numbers.foldLeft(0)(_ + _) +``` +単一のパラメータリストではScalaコンパイラが関数のパラメータを型推論できないので、このようなことはできません。 + +#### 暗黙のパラメータ +特定のパラメータだけを`implicit`として指定するには、`implicit`のパラメーターリストに入れなければなりません。 +こちらが例です。 + +``` +def execute(arg: Int)(implicit ec: scala.concurrent.ExecutionContext) = ??? +``` + +#### 部分適用 + +メソッドが少ない数のパラメータリストで呼び出された時、不足しているパラメータリストを引数として受け取る関数が生成されます。 +これは一般的に[部分適用](https://en.wikipedia.org/wiki/Partial_application)として知られています。 + +例えば +```tut +val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val numberFunc = numbers.foldLeft(List[Int]()) _ +val squares = numberFunc((xs, x) => xs :+ x*x) +print(squares) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100) +val cubes = numberFunc((xs, x) => xs :+ x*x*x) +print(cubes) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000) +``` diff --git a/_ja/tour/named-arguments.md b/_ja/tour/named-arguments.md new file mode 100644 index 0000000000..b7f0ca82a2 --- /dev/null +++ b/_ja/tour/named-arguments.md @@ -0,0 +1,36 @@ +--- +layout: tour +title: 名前付き引数 +language: ja + +discourse: true + +partof: scala-tour + +num: 34 +next-page: packages-and-imports +previous-page: default-parameter-values +prerequisite-knowledge: function-syntax + +redirect_from: "/tutorials/tour/named-arguments.html" +--- + +メソッドを呼ぶ時、以下のように引数にパラメータ名でラベル付が可能です。 + +```tut +def printName(first: String, last: String): Unit = { + println(first + " " + last) +} + +printName("John", "Smith") // Prints "John Smith" +printName(first = "John", last = "Smith") // Prints "John Smith" +printName(last = "Smith", first = "John") // Prints "John Smith" +``` + +名前付き引数の順序はどのように並び替えられるかに気をつけましょう。ただし、名前つき引数と名前つきでない引数がある場合は、名前つきでない引数は引数リストの最初に置かれ、かつメソッドシグネチャのパラメーター順でなければなりません。 + +```tut:fail +printName(last = "Smith", "john") // error: positional after named argument +``` + +名前付き引数はJavaメソッドを呼び出す時には使えません。 diff --git a/_ja/tour/nested-functions.md b/_ja/tour/nested-functions.md new file mode 100644 index 0000000000..05a57a2fae --- /dev/null +++ b/_ja/tour/nested-functions.md @@ -0,0 +1,40 @@ +--- +layout: tour +title: ネストしたメソッド +language: ja + +discourse: true + +partof: scala-tour + +num: 9 +next-page: multiple-parameter-lists +previous-page: higher-order-functions + +redirect_from: "/tutorials/tour/nested-functions.html" +--- + +Scalaではメソッドの定義をネストする(_訳注:入れ子にする_)ことができます。 +以下のコードは与えられた数値の階乗を計算するための`factorial`メソッドを提供します。 + +{% scalafiddle %} +```tut + def factorial(x: Int): Int = { + def fact(x: Int, accumulator: Int): Int = { + if (x <= 1) accumulator + else fact(x - 1, x * accumulator) + } + fact(x, 1) + } + + println("Factorial of 2: " + factorial(2)) + println("Factorial of 3: " + factorial(3)) +``` +{% endscalafiddle %} + +このプログラムの出力は以下の通りです。 + +``` +Factorial of 2: 2 +Factorial of 3: 6 +``` diff --git a/_ja/tour/operators.md b/_ja/tour/operators.md new file mode 100644 index 0000000000..b61b229558 --- /dev/null +++ b/_ja/tour/operators.md @@ -0,0 +1,88 @@ +--- +layout: tour +title: 演算子 +language: ja + +discourse: true + +partof: scala-tour + +num: 30 +next-page: by-name-parameters +previous-page: type-inference +prerequisite-knowledge: case-classes + +redirect_from: "/tutorials/tour/operators.html" +--- +Scalaでは演算子はメソッドです。パラメータを1つだけ持つメソッドであれば*中置演算子*として使えます。例えば、`+`はドット記法で呼び出せます。 + +``` +10.+(1) +``` + +しかしながら、中置演算子の方が読みやすいです。 + +``` +10 + 1 +``` + +## 演算子の定義方法と使い方 + +有効な識別子であれば演算子として使用できます。これは `add`のような名前も`+`のような記号も含みます。 +```tut +case class Vec(val x: Double, val y: Double) { + def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y) +} + +val vector1 = Vec(1.0, 1.0) +val vector2 = Vec(2.0, 2.0) + +val vector3 = vector1 + vector2 +vector3.x // 3.0 +vector3.y // 3.0 +``` +クラスVecはメソッド`+`を持ち、 `vector1`と`vector2`を足しわせるのに使います。丸括弧を用いて、複雑な式を読みやすい構文で作ることができます。 +こちらはクラス`MyBool`の定義です。クラス`MyBool`はメソッド`and`と`or`を含みます。 + +```tut +case class MyBool(x: Boolean) { + def and(that: MyBool): MyBool = if (x) that else this + def or(that: MyBool): MyBool = if (x) this else that + def negate: MyBool = MyBool(!x) +} +``` + +この時、`and`と`or`を中置演算子として使えます。 + +```tut +def not(x: MyBool) = x.negate +def xor(x: MyBool, y: MyBool) = (x or y) and not(x and y) +``` + +これにより`xor`の定義がより読みやすくなります。 + +## 優先順位 + +式が複数の演算子を使う時、それらの演算子は最初の文字の(以下に挙げる)優先度に基づき評価されます。 +``` +(以下に表示されていない記号) +* / % ++ - +: += ! +< > +& +^ +| +(全ての文字) +``` +これはあなたが定義した関数にも適用できます。 +たとえば、以下の式 +``` +a + b ^? c ?^ d less a ==> b | c +``` +は以下と同じ意味です。 +``` +((a + b) ^? (c ?^ d)) less ((a ==> b) | c) +``` +`?^`は最も高い優先順位を持ちます。`?^`は`?`から始まるからです。`+`は二番目に高い優先順位を持ち、その後に`==>`、 `^?`、 `|`、 そして`less`が続きます。 diff --git a/_ja/tour/package-objects.md b/_ja/tour/package-objects.md new file mode 100644 index 0000000000..fd7eaced57 --- /dev/null +++ b/_ja/tour/package-objects.md @@ -0,0 +1,73 @@ +--- +layout: tour +title: パッケージオブジェクト +language: ja + +discourse: true + +partof: scala-tour + +num: 36 +previous-page: packages-and-imports +--- + +# パッケージオブジェクト + +Scalaはパッケージ全体を通して共有される便利なコンテナとしてパッケージオブジェクトを提供します。 + +パッケージオブジェクトは、変数やメソッド定義だけでなく、任意の定義を含むことができます。 +例えば、それらはパッケージ全体で使われる型エイリアスと暗黙の変換を保有するためによく使われます。 +パッケージオブジェクトはScalaクラスやトレイトを継承することもできます。 + +慣習として、パッケージオブジェクトのソースコードは通常`package.scala`という名のソースファイルに設置されます。 + +1つのパッケージはパッケージオブジェクトを1つ持てます。パッケージオブジェクト内の全ての定義はパッケージ自体のメンバーと見なされます。 + +以下の例を見てみましょう。まず1つのクラス`Fruit`と3つの`Fruit`オブジェクトがパッケージ`gardening.fruits`にあるとします。 + +``` +// ファイル gardening/fruits/Fruit.scala の中 +package gardening.fruits + +case class Fruit(name: String, color: String) +object Apple extends Fruit("Apple", "green") +object Plum extends Fruit("Plum", "blue") +object Banana extends Fruit("Banana", "yellow") +``` + +ここで、変数`planted`とメソッド`showFruit`を直接パッケージ`gardening.fruits`内に置きたいとします。 +こちらがその方法になります。 + +``` +// ファイル gardening/fruits/package.scala の中 +package gardening +package object fruits { + val planted = List(Apple, Plum, Banana) + def showFruit(fruit: Fruit): Unit = { + println(s"${fruit.name}s are ${fruit.color}") + } +} +``` + +利用側がどのようになるかの例としては、以下のオブジェクト`PrintPlanted`は、ワイルドカードインポートでクラス`Fruit`と全く同様に`planted`と`showFruit`をインポートしています。 + +``` +// ファイル PrintPlanted.scala の中 +import gardening.fruits._ +object PrintPlanted { + def main(args: Array[String]): Unit = { + for (fruit <- fruits.planted) { + showFruit(fruit) + } + } +} +``` + +パッケージオブジェクトは他のオブジェクトのように、継承を利用して構成できます。例えば、一つのパッケージオブジェクトが2つのトレイトをミックスインしている例を示します。 + +``` +package object fruits extends FruitAliases with FruitHelpers { + // ヘルパーと変数がここに続きます。 +} +``` +メソッドオーバーライドはパッケージオブジェクト内では動作しないので気をつけましょう。 diff --git a/_ja/tour/packages-and-imports.md b/_ja/tour/packages-and-imports.md new file mode 100644 index 0000000000..dd2e42f844 --- /dev/null +++ b/_ja/tour/packages-and-imports.md @@ -0,0 +1,87 @@ +--- +layout: tour +title: パッケージとインポート +language: ja + +discourse: true + +partof: scala-tour + +num: 35 +previous-page: named-arguments +next-page: package-objects +--- + +# パッケージとインポート +Scalaは名前空間を作るためにパッケージを使います。名前空間によりプログラムをモジュール化できます。 + +## パッケージの作成 +Scalaファイルの先頭で1つ以上のパッケージ名を宣言することでパッケージは作られます。 + +``` +package users + +class User +``` +パッケージとScalaファイルが含まれるディレクトリは同じ名前をつける習慣があります。しかし、Scalaはファイルのレイアウトには関知しません。`package users`を含むsbtプロジェクトのディレクトリ構成はこのようになるかもしれません。 + +``` +- ExampleProject + - build.sbt + - project + - src + - main + - scala + - users + User.scala + UserProfile.scala + UserPreferences.scala + - test +``` +`users`ディレクトリがどのように`scala`ディレクトリの中にあり、複数のScalaファイルがどのようにパッケージ内にあるのかに注意してください。パッケージ内のScalaファイルは同じパッケージ宣言を持ちます。パッケージ宣言の他の方法は波括弧を使い以下のようにします。 + +``` +package users { + package administrators { + class NormalUser + } + package normalusers { + class NormalUser + } +} +``` +見ての通り、この方法はパッケージのネストができ、スコープとカプセル化をより強くコントロールできます。 + +パッケージ名は全て小文字で書き、もしコードがwebサイトを持つ組織によって開発される場合、慣習として次のフォーマットであるべきです。`<トップレベルドメイン>.<ドメイン名>.<プロジェクト名>`。例えば、Googleが`SelfDrivingCar`と呼ばれるプロジェクトを持っている場合、パッケージ名はこんな風になるでしょう。 +``` +package com.google.selfdrivingcar.camera + +class Lens +``` +これは次のディレクトリ構成に対応します。`SelfDrivingCar/src/main/scala/com/google/selfdrivingcar/camera/Lens.scala` + +## インポート +`import`句は他パッケージのメンバー(クラス、トレイト、関数など)にアクセスするためのものです。同じパッケージのメンバーにアクセスするには`import`句は必要ありません。import句は以下のどれでも使えます。 +``` +import users._ // usersパッケージから全てをインポートする +import users.User // クラスUserをインポートする +import users.{User, UserPreferences} // 選択されたメンバーのみインポートする +import users.{UserPreferences => UPrefs} // インポートし利便性のために名前を変更する +``` +ScalaのJavaと異なる点の1つはインポートがどこでも使える点です。 + +```tut +def sqrtplus1(x: Int) = { + import scala.math.sqrt + sqrt(x) + 1.0 +} +``` +名前競合があり、プロジェクトのルートから何かをインポートする必要がある時、パッケージ名の前に`_root_`をつけます。 +``` +package accounts + +import _root_.users._ +``` + + +注:`scala`と`java.lang` パッケージは`object Predef`と同じように標準でインポートされています。 diff --git a/_ja/tour/pattern-matching.md b/_ja/tour/pattern-matching.md new file mode 100644 index 0000000000..d44d13e41b --- /dev/null +++ b/_ja/tour/pattern-matching.md @@ -0,0 +1,166 @@ +--- +layout: tour +title: パターンマッチング +language: ja + +discourse: true + +partof: scala-tour + +num: 12 + +next-page: singleton-objects +previous-page: case-classes +prerequisite-knowledge: case-classes, string-interpolation, subtyping + +redirect_from: "/tutorials/tour/pattern-matching.html" +--- + +パターンマッチングは値をパターンに照合するための仕組みです。 +マッチに成功すれば、一つの値をその構成要素のパーツに分解することもできます。 +Javaの`switch`文の強化バージョンで、if/else文の連続の代わりとして同様に使うことができます。 + +## 構文 + +マッチ式は値、キーワード`match`と少なくとも1つの`case`句を持ちます。 +```tut +import scala.util.Random + +val x: Int = Random.nextInt(10) + +x match { + case 0 => "zero" + case 1 => "one" + case 2 => "two" + case _ => "other" +} +``` +上記の`val x`は0から10の間のランダムな整数です。`x`は`match`演算子の左オペランドで、右側は4つのケースを持つ式です。 +最後のケース`_`は その他の取りうる整数値のための"全てを捕捉する"ケースです。 +ケースは*オルタナティブ*とも呼ばれます。 + +マッチ式は値を持ちます。 +```tut +def matchTest(x: Int): String = x match { + case 1 => "one" + case 2 => "two" + case _ => "other" +} +matchTest(3) // other +matchTest(1) // one +``` +全てのケースでStringを返しているので、このマッチ式はString型を持ちます。 +そのため関数`matchTest`はStringを返します。 + +## ケースクラスでのマッチング + +ケースクラスはパターンマッチングで特に役立ちます。 + +```tut +abstract class Notification + +case class Email(sender: String, title: String, body: String) extends Notification + +case class SMS(caller: String, message: String) extends Notification + +case class VoiceRecording(contactName: String, link: String) extends Notification + +``` +`Notification`は抽象スーパークラスで、ケースクラスの実装`Email`、 `SMS`、 `VoiceRecording`3つの具象クラスがあります。 +今、これらのケースクラスでパターンマッチングをすることができます。 + +``` +def showNotification(notification: Notification): String = { + notification match { + case Email(sender, title, _) => + s"You got an email from $sender with title: $title" + case SMS(number, message) => + s"You got an SMS from $number! Message: $message" + case VoiceRecording(name, link) => + s"you received a Voice Recording from $name! Click the link to hear it: $link" + } +} +val someSms = SMS("12345", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") + +println(showNotification(someSms)) // You got an SMS from 12345! Message: Are you there? が出力されます。 + +println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123 が出力されます。 +``` +関数`showNotification`は抽象型`Notification`をパラメータとして受け取り、`Notification`の型でマッチします(すなわち`Email`、`SMS`、または `VoiceRecording`のいずれであるかを解決します)。 +`case Email(sender, title, _)` ではフィールド`sender`と`title`が戻り値として使われますが、`_`を使うことでフィールド`body`は無視されます。 + +## パターンガード +パターンガードはケースをより具体的にするために使われる簡単な真偽表現です。 +`if `をパターンの後ろに追加するだけです。 + +``` + +def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { + notification match { + case Email(sender, _, _) if importantPeopleInfo.contains(sender) => + "You got an email from special someone!" + case SMS(number, _) if importantPeopleInfo.contains(number) => + "You got an SMS from special someone!" + case other => + showNotification(other) // 特別なものではなく、オリジナルのshowNotification関数に委譲します。 + } +} + +val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com") + +val someSms = SMS("867-5309", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") +val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!") +val importantSms = SMS("867-5309", "I'm here! Where are you?") + +println(showImportantNotification(someSms, importantPeopleInfo)) +println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) +println(showImportantNotification(importantEmail, importantPeopleInfo)) +println(showImportantNotification(importantSms, importantPeopleInfo)) +``` + +`case Email(sender, _, _) if importantPeopleInfo.contains(sender)`では、パターンは`sender`が重要な人のリストに存在して初めてマッチします。 + +## 型のみでのマッチング + +以下のように型のみでマッチすることができます。 +```tut +abstract class Device +case class Phone(model: String) extends Device{ + def screenOff = "Turning screen off" +} +case class Computer(model: String) extends Device { + def screenSaverOn = "Turning screen saver on..." +} + +def goIdle(device: Device) = device match { + case p: Phone => p.screenOff + case c: Computer => c.screenSaverOn +} +``` +`def goIdle`は`Device`の型によって異なる振る舞いをします。 +これはケースがそのパターンのメソッドを呼び出す必要がある時に役立ちます。 +ケースの識別子には型の最初の一文字(この場合に`p`と`c`)を利用する慣習があります。 + +## シールドクラス +トレイトとクラスに`sealed`をつけると、全てのサブタイプは同一ファイル内で宣言されなければならないという意味になります。 +これは全てのサブタイプが既知であることを保証します。 + +```tut +sealed abstract class Furniture +case class Couch() extends Furniture +case class Chair() extends Furniture + +def findPlaceToSit(piece: Furniture): String = piece match { + case a: Couch => "Lie on the couch" + case b: Chair => "Sit on the chair" +} +``` +これは"全てに対応する"ケースを必要としなくて済むので、パターンマッチングで役立ちます。 + +## 注意 +Scalaのパターンマッチング文は[ケースクラス](case-classes.html)で表現される代数型のマッチングに最も役立ちます。 + + +Scalaでは[抽出子オブジェクト](extractor-objects.html)の`unapply`メソッドを使うと、ケースクラスのパターンを独自に定義することもできます。 diff --git a/_ja/tour/polymorphic-methods.md b/_ja/tour/polymorphic-methods.md new file mode 100644 index 0000000000..0770657246 --- /dev/null +++ b/_ja/tour/polymorphic-methods.md @@ -0,0 +1,39 @@ +--- +layout: tour +title: ポリモーフィックメソッド +language: ja + +discourse: true + +partof: scala-tour + +num: 28 + +next-page: type-inference +previous-page: implicit-conversions +prerequisite-knowledge: unified-types + +redirect_from: "/tutorials/tour/polymorphic-methods.html" +--- + +Scalaのメソッドは値と同様に型によってパラメータ化することができます。構文はジェネリッククラスの構文と似ています。 +値パラメータは丸括弧で囲まれるのに対して、型パラメータは角カッコで囲まれます。 + +こちらが例です。 + +```tut +def listOfDuplicates[A](x: A, length: Int): List[A] = { + if (length < 1) + Nil + else + x :: listOfDuplicates(x, length - 1) +} +println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) +println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La) +``` + +メソッド`listOfDuplicates`は型パラメータ`A`と値パラメータ`x`と`length`を受け取ります。値`x`の型は`A`です。もし`length < 1`なら空のリストを返します。それ以外の場合は`x`を再帰呼び出しで返された複写リストの先頭に追加します。(`::`の意味は、左辺の要素を右辺のリストの先頭に追加することです。) + +最初の呼び出し例では、`[Int]`と書いて明示的に型引数を渡しています。そのため最初の引数は`Int`でなければならず、戻される型は`List[Int]`となります。 + +2つ目の呼び出し例では必ずしも明示的に型パラメータを渡す必要がないことを示しています。コンパイラは文脈または値引数の型に基づき、型パラメータを推論できることが多いです。この例では`"La"`が`String`なので、コンパイラは`A`が`String`に違いないことが分かります。 diff --git a/_ja/tour/regular-expression-patterns.md b/_ja/tour/regular-expression-patterns.md new file mode 100644 index 0000000000..21068d1de1 --- /dev/null +++ b/_ja/tour/regular-expression-patterns.md @@ -0,0 +1,63 @@ +--- +layout: tour +title: 正規表現パターン +language: ja + +discourse: true + +partof: scala-tour + +num: 15 + +next-page: extractor-objects +previous-page: singleton-objects + +redirect_from: "/tutorials/tour/regular-expression-patterns.html" +--- +正規表現はデータの中からパターン(またはその欠如)を探すために使うことができる文字列です。 +どんな文字列も`.r`メソッドを使うことで、正規表現に変換できます。 + +```tut +import scala.util.matching.Regex + +val numberPattern: Regex = "[0-9]".r + +numberPattern.findFirstMatchIn("awesomepassword") match { + case Some(_) => println("Password OK") + case None => println("Password must contain a number") +} +``` +上記の例では、`numberPattern`は`Regex`(正規表現)型で、パスワードに数字が含まれていることを確認するのに使います。 + +括弧を使うことで、正規表現のグループを探すこともできます。 + +```tut +import scala.util.matching.Regex + +val keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r + +val input: String = + """background-color: #A03300; + |background-image: url(img/header100.png); + |background-position: top center; + |background-repeat: repeat-x; + |background-size: 2160px 108px; + |margin: 0; + |height: 108px; + |width: 100%;""".stripMargin + +for (patternMatch <- keyValPattern.findAllMatchIn(input)) + println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}") +``` +ここでは、文字列のキーと値を解析しています。 +それぞれのマッチはサブマッチのグループを持ちます。こちらが出力結果です。 +``` +key: background-color value: #A03300 +key: background-image value: url(img/header100.png) +key: background-position value: top center +key: background-repeat value: repeat-x +key: background-size value: 2160px 108px +key: margin value: 0 +key: height value: 108px +key: width value: 100 +``` diff --git a/_ja/tour/self-types.md b/_ja/tour/self-types.md new file mode 100644 index 0000000000..836e4459ec --- /dev/null +++ b/_ja/tour/self-types.md @@ -0,0 +1,42 @@ +--- +layout: tour +title: 自分型 +language: ja + +discourse: true + +partof: scala-tour + +num: 25 +next-page: implicit-parameters +previous-page: compound-types +topics: self-types +prerequisite-knowledge: nested-classes, mixin-class-composition + +redirect_from: "/tutorials/tour/self-types.html" +--- +自分型は、直接継承していなくてもトレイトが他のトレイトにミックスインされていることを宣言する方法です。 +これにより依存先のメンバーをimportなしで利用できます。 + +自分型は`this`、または`this`の別名となる他の識別子の型を絞り込む方法です。 +その構文は普通の関数構文のように見えますが、全く異なる意味があります。 + +トレイトで自分型を使うには、識別子、ミックスインする他のトレイトの型、`=>`を書きます(例えば `someIdentifier: SomeOtherTrait =>`)。 +```tut +trait User { + def username: String +} + +trait Tweeter { + this: User => // thisが再割り当てされます + def tweet(tweetText: String) = println(s"$username: $tweetText") +} + +class VerifiedTweeter(val username_ : String) extends Tweeter with User { // TweeterがUserを必要とするためミックスインします。 + def username = s"real $username_" +} + +val realBeyoncé = new VerifiedTweeter("Beyoncé") +realBeyoncé.tweet("Just spilled my glass of lemonade") // "real Beyoncé: Just spilled my glass of lemonade"と出力します。 +``` +`trait Tweeter`の中で`this: User =>`と記述したので、今は変数`username`は`tweet`メソッドのスコープ内にあります。これはさらに、`VerifiedTweeter`が`Tweeter`を継承する際、(`with User`を使って)`User`もミックスインしなければならいことを意味します。 diff --git a/_ja/tour/singleton-objects.md b/_ja/tour/singleton-objects.md new file mode 100644 index 0000000000..e920e884c9 --- /dev/null +++ b/_ja/tour/singleton-objects.md @@ -0,0 +1,122 @@ +--- +layout: tour +title: シングルトンオブジェクト +language: ja + +discourse: true + +partof: scala-tour + +num: 13 + +next-page: regular-expression-patterns +previous-page: pattern-matching +redirect_from: "/tutorials/tour/singleton-objects.html" +prerequisite-knowledge: classes, methods, private-methods, packages, option +--- +オブジェクトは丁度1つのインスタンスを持つクラスです。 +それはlazy valのように参照された際に遅れて作られます。 + +トップレベルにあるオブジェクトは、シングルトンです。 +クラスのメンバーやローカル変数としてのオブジェクトは、lazy valと全く同じように振る舞います。 + +# シングルトンオブジェクトの定義 +オブジェクトは値です。オブジェクトの定義はクラスのように見えますが、キーワード`object`を使います。 +```tut +object Box +``` +これはメソッドを持つオブジェクトの例です。 +``` +package logging + +object Logger { + def info(message: String): Unit = println(s"INFO: $message") +} +``` +`info`メソッドはプログラム上のどこからでもimportすることができます。 +このように便利なメソッドを作ることはシングルトンオブジェクトのユースケースと同じです。 + +他のパッケージで`info`がどのように使われるか見てみましょう。 + +``` +import logging.Logger.info + +class Project(name: String, daysToComplete: Int) + +class Test { + val project1 = new Project("TPS Reports", 1) + val project2 = new Project("Website redesign", 5) + info("Created projects") // Prints "INFO: Created projects" +} +``` + +import文`import logging.Logger.info`により、`info`メソッドが見えるようになります。 + +import文には取り込むシンボルへの"変動しないパス"が必要であり、オブジェクトは変動しないパスとなります。 + +注意:`object`がトップレベルではなく他のクラスやオブジェクトにネストされている時、そのオブジェクトは他のメンバーのように"経路依存性"があります。 +これは2種類の飲み物`class 牛乳`と`class オレンジジュース`が与えられた場合、クラスメンバーの`object 栄養素`はそれが属するインスタンス、すなわち牛乳またはオレンジジュースのいずれかに依存することを意味します。 +`milk.NutritionInfo`は`oj.NutritionInfo`とは全く異なります。 + +## コンパニオンオブジェクト + +クラスと同じ名前のオブジェクトは*コンパニオンオブジェクト*と呼ばれます。 +逆にそのクラスはオブジェクトのコンパニオンクラスと呼ばれます。 +コンパニオンクラスやコンパニオンオブジェクトは自身のコンパニオンのプライベートメンバーにアクセスできます。 +コンパニオンクラスのインスタンスに特定されないメソッドや値にはコンパニオンオブジェクトを使います。 + +``` +import scala.math._ + +case class Circle(radius: Double) { + import Circle._ + def area: Double = calculateArea(radius) +} + +object Circle { + private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0) +} + +val circle1 = new Circle(5.0) + +circle1.area +``` + +`class Circle`は各インスタンスの固有のメンバー`area`を持ち、シングルトンオブジェクト`object Circle`は全てのインスタンスで利用できる`calculateArea`メソッドを持ちます。 + +コンパニオンオブジェクトはファクトリーメソッドを含むことができます。 +```tut +class Email(val username: String, val domainName: String) + +object Email { + def fromString(emailString: String): Option[Email] = { + emailString.split('@') match { + case Array(a, b) => Some(new Email(a, b)) + case _ => None + } + } +} + +val scalaCenterEmail = Email.fromString("scala.center@epfl.ch") +scalaCenterEmail match { + case Some(email) => println( + s"""Registered an email + |Username: ${email.username} + |Domain name: ${email.domainName} + """) + case None => println("Error: could not parse email") +} +``` +`object Email`はファクトリー`fromString`を持ち、Stringから`Email`インスタンスを作ります。 +パースエラーの場合も考えて、返り値の型を`Option[Email]`とします。 + +注意:クラスまたはオブジェクトがコンパニオンを持つ場合、クラス、オブジェクトの両方は同じファイルの中に定義されていなければなりません。 +REPL内でコンパニオンを定義する場合は、それらを同じ行で定義するか、`:paste`モードに入ります。 + +## Javaプログラマのための注意事項 ## + +Javaにおける`static`メンバーはScalaではコンパニオンオブジェクトの一般メンバーとして作られています。 + +Javaのコードからコンパニオンオブジェクトを使う場合、メンバーはコンパニオンクラス内で`static`識別子を用いて定義されます。 +これは*static forwarding*と呼ばれます。 +これはコンパニオンクラスを自分で定義していなかったとしても起きます。 diff --git a/_ja/tour/tour-of-scala.md b/_ja/tour/tour-of-scala.md new file mode 100644 index 0000000000..775c66557f --- /dev/null +++ b/_ja/tour/tour-of-scala.md @@ -0,0 +1,86 @@ +--- +layout: tour +title: 前書き +language: ja + +discourse: true + +partof: scala-tour + +num: 1 + +next-page: basics + +redirect_from: "/tutorials/tour/tour-of-scala.html" +--- + +## ようこそツアーへ +このツアーはScalaで最もよく使う機能を一口サイズで紹介をしています。 +これらはScala初心者を対象としています。 + +これはほんの短いツアーで、完全な言語のチュートリアルではありません。 +もしそれを望むなら、[こちらの本](/books.html) を手に入れるか、 +[その他の解決手段](/learn.html) での相談を検討してください。 + +## Scalaとは? +Scalaは一般的なプログラミング方法を簡潔かつエレガントかつ型安全な方法で表現するために設計されたモダンなマルチパラダイム言語です。 +それはオブジェクト指向言語と関数型言語の機能をスムーズに統合しています。 + +## Scalaはオブジェクト指向 ## +Scalaは[全ての値がオブジェクトである](unified-types.html) という意味では純粋オブジェクト指向言語です。 +型とオブジェクトの振る舞いは[クラス](classes.html) と[トレイト](traits.html) によって記述されます。 +クラスはサブクラス化と、多重継承を巧みに置き換える柔軟な[ミックスインを基にした合成](mixin-class-composition.html) 機構により拡張されます。 + +## Scalaは関数型 ## +Scalaは[すべての関数が値である](unified-types.html) という意味で関数型言語でもあります。 +Scalaは無名関数を定義するために[軽量な構文](basics.html#関数)を提供し、 +[高階関数](higher-order-functions.html) をサポートし、関数は[ネスト](nested-functions.html)しても良く、[カリー化](multiple-parameter-lists.html) をサポートします。 +Scalaの[ケースクラス](case-classes.html)には[パターンマッチング](pattern-matching.html)が組み込まれていることにより、多くの関数型プログラミング言語で使われる代数型を作ることができます。 +[シングルトンオブジェクト](singleton-objects.html) はクラスのメンバーではない関数をグループ化する便利な方法を提供します。 + +さらに、Scalaのパターンマッチングの概念は、[抽出子オブジェクト](extractor-objects.html) による一般的な拡張として、[右無視シーケンスパターン](regular-expression-patterns.html)の働きにより、自然に[XMLデータの処理](https://github.com/scala/scala-xml/wiki/XML-Processing) にまで拡張されています。 +(訳者註:現在のバージョンでは右無視シーケンスパターンの説明は正規表現のページから除かれています。[→古いバージョン](https://www.scala-lang.org/old/node/122)) +この文脈では、For内包表記はクエリの設計に役立ちます。 +これらの機能により、ScalaはWebサービスのようなアプリケーション開発に理想的なものとなっています。 + +## Scalaは静的型付け ## +Scalaは抽象化が安全で首尾一貫した方法で使われることをコンパイル時に強制する、表現力豊かな型システムを備えています。 +特にその型システムは以下をサポートします: + +* [ジェネリッククラス](generic-classes.html) +* [変位指定アノテーション](variances.html) +* [上限](upper-type-bounds.html) と [下限](lower-type-bounds.html) 型境界 +* [内部クラス](inner-classes.html) とオブジェクトメンバーとしての[抽象型メンバー](abstract-type-members.html) +* [複合型](compound-types.html) +* [明示的に型指定された自己参照](self-types.html) +* [暗黙パラメーター](implicit-parameters.html) と [暗黙の変換](implicit-conversions.html) +* [ポリモーフィックメソッド](polymorphic-methods.html) + +[型推論](type-inference.html) は、ユーザーがコードに冗長な型情報の注釈をつける必要はないことを意味します。 +これらの機能を組み合わせることにより、プログラミングの抽象化を安全に再利用でき、ソフトウェアを型安全に拡張できる強力な基盤となります。 + +## Scalaは拡張可能 ## + +実際問題として、ドメイン固有のアプリケーションの開発ではよくドメイン固有の言語拡張が必要となります。 +Scalaは言語メカニズムのユニークな組み合わせを提供します。それにより、新しい言語構成要素をライブラリという形で円滑に追加することを簡単にします。 + +多くのケースで、これはマクロのようなメタプログラミングの機能を使わずに実現できます。例えば、 + +* [暗黙のクラス](http://docs.scala-lang.org/overviews/core/implicit-classes.html) で既存の型に拡張メソッドを追加できます。 + +* [文字列補間](/ja/overviews/core/string-interpolation.html) はカスタム補間を使ってユーザー拡張可能です。 + +## Scalaの相互運用 + +Scalaは一般的なJava実行環境 (JRE) と相互運用するように設計されています。 +特に、主流であるオブジェクト指向のJavaプログラミング言語とのやり取りはできるだけスムーズになっています。 +ScalaではSAMs、[ラムダ](higher-order-functions.html) 、[アノテーション](annotations.html) 、[ジェネリクス](generic-classes.html) のようなJavaの新しい機能には直接の類似物があります。 + +[デフォルト引数](default-parameter-values.html) 、[名前付きパラメータ](named-arguments.html) +といった、Javaに類似物がないScalaの機能はできるだけ適切でJavaに近い形にコンパイルされます。 +ScalaはJavaのような同じコンパイルモデル(分割コンパイル、動的クラス読み込み) を持っており、数千の既存の高品質なライブラリにアクセスができます。 + +## ツアーを楽しんで! + +もっと読むには、目次メニューの[次のページ](basics.html) に進んでください。 + diff --git a/_ja/tour/traits.md b/_ja/tour/traits.md new file mode 100644 index 0000000000..1ea0b11809 --- /dev/null +++ b/_ja/tour/traits.md @@ -0,0 +1,89 @@ +--- +layout: tour +title: トレイト +language: ja + +discourse: true + +partof: scala-tour + +num: 5 +next-page: tuples +previous-page: classes +topics: traits +prerequisite-knowledge: expressions, classes, generics, objects, companion-objects + +redirect_from: "/tutorials/tour/traits.html" +--- + +トレイトはクラス間でインターフェースとフィールドを共有するために使います。それらはJava 8のインターフェースと似ています。 +クラスとオブジェクトはトレイトを継承することができますが、トレイトはインスタンス化ができません、したがってパラメータを持ちません。 + +## トレイトを定義する +最小のトレイトはキーワード `trait` と識別子だけというものです。 + +```tut +trait HairColor +``` + +トレイトはジェネリック型として、抽象メソッドとあわせて使うと特に便利です。 +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} +``` + +`trait Iterator[A]` を継承することは `A` 型と、`hasNext` と `next` メソッドの実装を必要とします。 + +## トレイトの使い方 +トレイトを継承するには `extends` キーワードを使います。その際に、 `override` キーワードを利用しすべての抽象メンバーを実装します。 +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} + +class IntIterator(to: Int) extends Iterator[Int] { + private var current = 0 + override def hasNext: Boolean = current < to + override def next(): Int = { + if (hasNext) { + val t = current + current += 1 + t + } else 0 + } +} + + +val iterator = new IntIterator(10) +iterator.next() // returns 0 +iterator.next() // returns 1 +``` +ここでの `IntIterator` クラスは上限として引数 `to` を取ります。 +`extends Iterator[Int]` は `next` メソッドは Int を返さなければならないことを意味します。 + +## サブタイピング +あるトレイトが必要とされている場所に、代りにそのトレイトのサブタイプを使うことができます。 + +```tut +import scala.collection.mutable.ArrayBuffer + +trait Pet { + val name: String +} + +class Cat(val name: String) extends Pet +class Dog(val name: String) extends Pet + +val dog = new Dog("Harry") +val cat = new Cat("Sally") + +val animals = ArrayBuffer.empty[Pet] +animals.append(dog) +animals.append(cat) +animals.foreach(pet => println(pet.name)) // Prints Harry Sally +``` +`trait Pet` が持つ抽象フィールド `name`は、Cat と Dog のコンストラクタで実装されました。 +最終行では、`Pet` トレイトの全てのサブタイプの中で実装される必要がある `pet.name` を呼んでいます。 diff --git a/_ja/tour/tuples.md b/_ja/tour/tuples.md new file mode 100644 index 0000000000..0d67f6c5c1 --- /dev/null +++ b/_ja/tour/tuples.md @@ -0,0 +1,79 @@ +--- +layout: tour +title: タプル +language: ja + +discourse: true + +partof: scala-tour + +num: 6 +next-page: mixin-class-composition +previous-page: traits +topics: tuples + +redirect_from: "/tutorials/tour/tuples.html" +--- + +Scalaではタプルは決まった数の要素を含む値であり、各要素はそれぞれの型を持ちます。 +タプルは不変です。 + +タプルはメソッドから複数の値を返す際に特に役立ちます。 + +2つの要素を持つタプルは以下のように作ることができます。 + +```tut +val ingredient = ("Sugar" , 25) +``` +ここでは`String`要素を1つと`Int`要素を1つ含むタプルを作っています。 + +推論される`ingredient`の型は`(String, Int)`であり、これは`Tuple2[String, Int]`の簡単な表記法です。 + +タプルを表すために、Scalaは`Tuple2`, `Tuple3` … `Tuple22`までのクラス群を使います。 +それぞれのクラスは要素の数と同じ数の型パラメータを持ちます。 + +## 要素へのアクセス + +タプルの要素へのアクセス方法の1つとして、位置があります。 +個々の要素は`_1`、`_2`などと名付けられます。 + +```tut +println(ingredient._1) // Sugar +println(ingredient._2) // 25 +``` +## タプルでのパターンマッチング +タプルはパターンマッチングを使って分解することもできます。 + +```tut +val (name, quantity) = ingredient +println(name) // Sugar +println(quantity) // 25 +``` + +ここでは`name`に推論される型は`String`で、`quantity`に推論される型は`Int`です。 + +こちらはタプルのパターンマッチングの他の例です。 + +```tut +val planets = + List(("Mercury", 57.9), ("Venus", 108.2), ("Earth", 149.6), + ("Mars", 227.9), ("Jupiter", 778.3)) +planets.foreach{ + case ("Earth", distance) => + println(s"Our planet is $distance million kilometers from the sun") + case _ => +} +``` + +また、`for`内包表記では以下のようになります。 + +```tut +val numPairs = List((2, 5), (3, -7), (20, 56)) +for ((a, b) <- numPairs) { + println(a * b) +} +``` + +## タプルとケースクラス +ユーザーは時々、タプルとケースクラスの選択を難しいと思うかもしれません。ケースクラスには名前付き要素があります。それらの名前によってコードの可読性を改善できる場合があります。 +上記の惑星の例ではタプルを使うより、`case class Planet(name: String, distance: Double)`を定義したほうがいいかもしれません。 diff --git a/_ja/tour/type-inference.md b/_ja/tour/type-inference.md new file mode 100644 index 0000000000..5a0dc8c4a7 --- /dev/null +++ b/_ja/tour/type-inference.md @@ -0,0 +1,74 @@ +--- +layout: tour +title: 型推論 +language: ja + +discourse: true + +partof: scala-tour + +num: 29 +next-page: operators +previous-page: polymorphic-methods +--- + +Scalaコンパイラが式の型を推論できることが多いので、明示的に型を宣言する必要はありません。 + +## 型の省略 + +```tut +val businessName = "Montreux Jazz Café" +``` +コンパイラは`businessName`がStringだと検知できます。これはメソッドでも同様に動きます。 + +```tut +def squareOf(x: Int) = x * x +``` +コンパイラは戻り値の型が`Int`だと推論できるので、明示的な戻り値の型は必要ありません。 + +再帰的メソッドでは、コンパイラは結果の型を推論できません。こちらはこの理由でコンパイラが失敗するプログラムです。 + +```tut:fail +def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) +``` + +[ポリモーフフィックメソッド](polymorphic-methods.html)が呼ばれる時や[ジェネリッククラス](generic-classes.html) がインスタンス化される場合も型パラメータの指定は強制ではありません。Scalaコンパイラは文脈あるいはメソッドやコンストラクタの実際の引数から、指定されていない型パラメータを推論します。 + +こちらは2つの例です。 + +```tut +case class MyPair[A, B](x: A, y: B); +val p = MyPair(1, "scala") // 型: MyPair[Int, String] + +def id[T](x: T) = x +val q = id(1) // 型: Int +``` +コンパイラは型`A`と`B`が何であるかを見つけ出すために`MyPair`の引数の型を使用します。`x`の型も同様です。 + +## パラメータ + +コンパイラはメソッドのパラメータ型を決して推論しません。しかし、関数が引数として渡されている場合は、無名関数のパラメータ型を推論できます。 + +```tut +Seq(1, 3, 4).map(x => x * 2) // List(2, 6, 8) +``` + +mapのパラメータは`f: A => B`です。`Seq`の中に整数が入っているので、コンパイラは`A`が`Int`だと知っています(つまり、この`x`は整数です)。したがってコンパイラは`x * 2`から`B`が型`Int`であると推論できます。 + +## 型推論に頼ら*ない*時 + +一般的には、パブリックなAPIで公開されているメンバーの型を宣言したほうが読みやすいと考えられています。そのため、ユーザーに公開するAPIではあなたのコードの型を明示することをお勧めします。 + +また、型推論は特定の型を推論することがあります。次のように書いたとします。 + +```tut +var obj = null +``` + +これ以上進められず、再割り当てができません。 + +```tut:fail +obj = new AnyRef +``` + +こちらはコンパイルできません。`obj`に推論された型は`Null`だからです。その型の唯一の値が`null`なので、他の値を代入できれません。 diff --git a/_ja/tour/unified-types.md b/_ja/tour/unified-types.md new file mode 100644 index 0000000000..98482b1c95 --- /dev/null +++ b/_ja/tour/unified-types.md @@ -0,0 +1,97 @@ +--- +layout: tour +title: 統合された型 +language: ja + +discourse: true + +partof: scala-tour + +num: 3 +next-page: classes +previous-page: basics +prerequisite-knowledge: classes, basics + +redirect_from: "/tutorials/tour/unified-types.html" +--- +Scalaでは数値や関数を含め、全ての値は型を持ちます。 +以下の図は型階層の一部を説明しています。 + +Scala Type Hierarchy + +## Scalaの型階層 ## + +[`Any`](http://www.scala-lang.org/api/2.12.1/scala/Any.html) は全ての型のスーパータイプであり、トップ型とも呼ばれます。 +Anyは `equals`、` hashCode`、そして `toString`のようないくつかの普遍的なメソッドを定義しています。 +そして`AnyVal`と`AnyRef` という2つの直系のサブクラスを持ちます。 + +`AnyVal` は値型に相当します。 +事前に定義された9つの値型が存在し、それら`Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`,`Boolean`は +null非許容です。 +`Unit`は意味のある情報をもたない値型です。`Unit`型のインスタンスはただ1つだけあり、`()`というリテラルで宣言することができます。 +全ての関数は必ず何かを返さなければなりません。そのため`Unit`は戻り値の型として時々役立ちます。 + +`AnyRef` は参照型を意味します。全ての値型でない型は参照型として定義されます。Scalaでは全てのユーザー定義型は`AnyRef`のサブタイプになります。 +もしScalaがJava実行環境上で利用されるなら、`AnyRef` は `java.lang.Object` に相当します。 + +以下にstring、integer、character、boolean、関数が他のオブジェクトと同様に全てオブジェクトであるという例を示します。 + +```tut +val list: List[Any] = List( + "a string", + 732, // integer + 'c', // character + true, // boolean value + () => "文字列を返す無名関数" +) + +list.foreach(element => println(element)) +``` + +これは`List[Any]`型の`list`という値を定義します。 +このlistは様々な型の要素で初期化されています。しかしそれぞれの要素は `scala.Any` のインスタンスなのでlistに追加することができています。 + +こちらは先程のプログラムの出力です。 + +``` +a string +732 +c +true + +``` + +## 型キャスト +値型は以下の順序でキャストできます。 + +Scalaの型階層 + +例えば、 + +```tut +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 (この場合精度が落ちることに注意してください) + +val face: Char = '☺' +val number: Int = face // 9786 +``` + +型変換は一方向です。これはコンパイルができないでしょう。 + +``` +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 +val z: Long = y // 一致しない +``` + +参照型をサブタイプにキャストすることもできます。こちらはツアーの中で後ほど紹介します。 + +## Nothing と Null +`Nothing`は全ての型のサブタイプであり、ボトム型とも呼ばれます。`Nothing`型を持つ値は存在しません。 +一般的に例外のスロー、プログラム終了、無限ループなど終了していないことを示すのに使われます。 +(すなわち、値として評価されない式や正常に返らないメソッドなどです。) + + +`Null` は全ての参照型のサブタイプ(すなわち、全てのAnyRefのサブタイプ)です。`null`というキーワードリテラルが指す値を1つだけもちます。 +`Null` は、ほぼ他のJVM言語との相互運用性のためだけに提供されているので、Scalaのコード内ではほとんどの場合、使われるべきではありません。 +`null`の代替手段については、後のツアーで説明します。 diff --git a/_ja/tour/upper-type-bounds.md b/_ja/tour/upper-type-bounds.md new file mode 100644 index 0000000000..427dbe429f --- /dev/null +++ b/_ja/tour/upper-type-bounds.md @@ -0,0 +1,60 @@ +--- +layout: tour +title: 上限型境界 +language: ja + +discourse: true + +partof: scala-tour +categories: tour +num: 20 +next-page: lower-type-bounds +previous-page: variances + +redirect_from: "/tutorials/tour/upper-type-bounds.html" +--- + +Scalaでは [型パラメータ](generic-classes.html)と[抽象型メンバー](abstract-type-members.html) は型境界による制約をかけることができます。 +型境界は型変数に入れられる具象型を制限して、時にはそれらの型のメンバーについてより多くの情報を与えます。 +_上限型境界_ `T <: A` は型変数`T`が型`A`のサブタイプであるという宣言です。 + +こちらはクラス`PetContainer`の型パラメータの上限型境界を実演する例です。 + +```tut +abstract class Animal { + def name: String +} + +abstract class Pet extends Animal {} + +class Cat extends Pet { + override def name: String = "Cat" +} + +class Dog extends Pet { + override def name: String = "Dog" +} + +class Lion extends Animal { + override def name: String = "Lion" +} + +class PetContainer[P <: Pet](p: P) { + def pet: P = p +} + +val dogContainer = new PetContainer[Dog](new Dog) +val catContainer = new PetContainer[Cat](new Cat) +``` + +```tut:fail +// これはコンパイルされません +val lionContainer = new PetContainer[Lion](new Lion) +``` +`class PetContainer`は型パラメータ`P`を受け取ります。それは`Pet`のサブタイプである必要があります。 + `Dog`と`Cat`は`Pet`のサブタイプなので、新たな`PetContainer[Dog]`と`PetContainer[Cat]`を作ることができます。 + しかしながら、もし`PetContainer[Lion]`を作ろうとすると、以下のエラーが返ってきます。 + +`type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]` + +これは`Lion`は`Pet`のサブタイプではないからです。 diff --git a/_ja/tour/variances.md b/_ja/tour/variances.md new file mode 100644 index 0000000000..35f0d01de8 --- /dev/null +++ b/_ja/tour/variances.md @@ -0,0 +1,184 @@ +--- +layout: tour +title: 変位指定 +language: ja + +discourse: true + +partof: scala-tour + +num: 19 +next-page: upper-type-bounds +previous-page: generic-classes + +redirect_from: "/tutorials/tour/variances.html" +--- + +変位指定は複合型の間の継承関係とそれらの型パラメータ間の継承関係の相関です。 +Scalaは[ジェネリッククラス](generic-classes.html)の型パラメータの変位指定アノテーションをサポートしています。 +変位指定アノテーションにより共変、反変にでき、アノテーション無しなら非変になります。 +型システム上で変位指定を利用すると複合型間の直感的な繋がりを作ることができます。 +もし変位指定が無ければ、クラスを抽象化して再利用しにくくなるでしょう。 + + +```tut +class Foo[+A] // 共変クラス +class Bar[-A] // 反変クラス +class Baz[A] // 非変クラス +``` + +### 共変 + +ジェネリッククラスの型パラメータ`A`はアノテーション`+A`を使うと共変になります。 +`class List[+A]`では、`A`が共変になっているので、`A`が`B`のサブタイプであるような`A`と`B`に対して`List[A]`が`List[B]`のサブタイプであることを示します。 +これによりジェネリックを利用したとても便利で直感的なサブタイプの関係を作ることができます。 + +このシンプルなクラス構成を考えてみましょう。 + +```tut +abstract class Animal { + def name: String +} +case class Cat(name: String) extends Animal +case class Dog(name: String) extends Animal +``` +`Cat`と`Dog`は`Animal`のサブタイプです。 +Scala標準ライブラリにはイミュータブルなジェネリッククラス`sealed abstract class List[+A]`があり、型パラメータ`A`が共変です。 +これは`List[Cat]`は`List[Animal]`であり、`List[Dog]`も`List[Animal]`であることを意味します。 +猫のリストも犬のリストも動物のリストであり、どちらも`List[Animal]`の代わりにできる、というのは直感的に理解できます。 + +以下の例では、メソッド`printAnimalNames`は引数に動物のリストを受け取り、新しい行にそれらの名前をプリントします。 +もし`List[A]`が共変でなければ、最後の2つのメソッド呼び出しはコンパイルされず、`printAnimalNames`メソッドの使い勝手はひどく制限されます。 + +```tut +object CovarianceTest extends App { + def printAnimalNames(animals: List[Animal]): Unit = { + animals.foreach { animal => + println(animal.name) + } + } + + val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) + val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) + + printAnimalNames(cats) + // Whiskers + // Tom + + printAnimalNames(dogs) + // Fido + // Rex +} +``` + +### 反変 + +ジェネリッククラスの型パラメータ`A`はアノテーション`-A`を利用して反変にできます。 +これはクラスとその型パラメータの間で、共変と似ていますが反対の意味のサブタイプ関係を作ります。 + +`class Writer[-A]`では`A`が反変になっているので、`A`が`B`のサブタイプであるような`A`と`B`に対し、`Writer[B]`が`Writer[A]`のサブタイプであることを示します。 + +先に定義された`Cat`、`Dog`、`Animal`クラスを以下の例で検討してみます。 + +```tut +abstract class Printer[-A] { + def print(value: A): Unit +} +``` +`Printer[A]`はある型`A`をどのようにプリントするかを知っている簡単なクラスです。 +特定の型でいくつかのサブクラスを定義してみましょう。 + +```tut +class AnimalPrinter extends Printer[Animal] { + def print(animal: Animal): Unit = + println("The animal's name is: " + animal.name) +} + +class CatPrinter extends Printer[Cat] { + def print(cat: Cat): Unit = + println("The cat's name is: " + cat.name) +} +``` +`Printer[Cat]`はコンソールに任意の`Cat`をプリントする方法を知っています。 +そして`Printer[Animal]`はコンソールに任意の`Animal`をプリントする方法を知っています。 +それは`Printer[Animal]`も任意の`Cat`をプリントする方法を知っていることを意味します。 +逆の関係性は適用されません、それは`Printer[Cat]`がコンソールに任意の`Animal`をプリントする方法を知らないからです。 +したがって、私達は必要であれば`Printer[Animal]`を`Printer[Cat]`代わりに使うことができます。これは`Printer[A]`が反変であるからこそ可能なのです。 + +```tut +object ContravarianceTest extends App { + val myCat: Cat = Cat("Boots") + + def printMyCat(printer: Printer[Cat]): Unit = { + printer.print(myCat) + } + + val catPrinter: Printer[Cat] = new CatPrinter + val animalPrinter: Printer[Animal] = new AnimalPrinter + + printMyCat(catPrinter) + printMyCat(animalPrinter) +} +``` + +この出力結果は以下のようになります。 + +``` +The cat's name is: Boots +The animal's name is: Boots +``` + +### 非変 + +Scalaのジェネリッククラスは標準では非変です。 +これは共変でも反変でもないことを意味します。 +以下の例の状況では、`Container`クラスは非変です。`Container[Cat]`は`Container[Animal]`_ではなく_、逆もまた同様です。 + +```tut +class Container[A](value: A) { + private var _value: A = value + def getValue: A = _value + def setValue(value: A): Unit = { + _value = value + } +} +``` +`Container[Cat]`が `Container[Animal]`でもあることは自然なように思えるかもしれませんが、ミュータブルジェネリッククラスを共変にするのは安全ではありません。 +この例では、`Container`が非変であることは非常に重要です。`Container`が仮に共変だったとするとこのようなことが起こり得ます。 + +``` +val catContainer: Container[Cat] = new Container(Cat("Felix")) +val animalContainer: Container[Animal] = catContainer +animalContainer.setValue(Dog("Spot")) +val cat: Cat = catContainer.getValue // おっと、犬に猫に割り当ててしまった。 +``` + +幸いにも、実行する前にコンパイラが止めてくれます。 + +### 他の例 + +変位指定の理解を助けるもう一つの例はScala標準ライブラリの`trait Function1[-T, +R]`です。 +`Function1`は1つのパラメータを持つ関数を表します。1つ目の型パラメータ`T`はパラメータの型を表します。 +そして2つ目の型パラメータ`R`は戻り値の型を表します。 +`Function1`はその引数の型に対して反変であり、戻り値の型に対して共変です。 +この例では`Function1[A, B]`を表現するために`A => B`という文字での表記をします。 + +先ほど利用された`Cat`, `Dog`, `Animal`の継承ツリーに、以下のものを加えましょう: + +```tut +abstract class SmallAnimal extends Animal +case class Mouse(name: String) extends SmallAnimal +``` + +動物の種類を受け取り、それらが食べる食料の種類を返す関数がいくつかあるとしましょう。 +もし(猫は小動物を食べるので)`Cat => SmallAnimal`が欲しい場合に代わりに`Animal => Mouse`を与えられたとしても、私たちのプログラムはまだ動きます。 +直感的に、`Cat`は`Animal`なので、`Animal => Mouse` は`Cat`を引数として受け取ります。 +そして`SmallAnimal`である`Mouse`を返します。 + +安全に、そのままで前者に代えて後者を用いることができるため、`Animal => Mouse`は`Cat => SmallAnimal`のサブタイプと言うことができます。 + +### 他の言語との比較 + +Scalaに似たいくつかの言語で、変位指定はいろんな方法でサポートされています。 +例えば、Scalaの変位指定アノテーションはC#のそれと非常に似ています。C#ではクラスの抽象性を定義する時にアノテーションが追加されます(宣言時の変位指定)。 +しかしながら、Javaでは、クラスの抽象性を使う時に変位指定アノテーションが利用側のコードから与えられます(使用時の変位指定)。