LoginSignup
4
1

More than 5 years have passed since last update.

「Scalaスケーラブルプログラミング」まとめ ~ 2章&3章 ~

Posted at

この記事は?

Scalaスケーラブルプログラミング第3版を読んで、個人的に気になった箇所や勉強になった箇所のまとめです。
認識誤りなどがあればご指摘いただけると幸いです。

1章のまとめはこちら

第2章 Scalaプログラミングの第一歩

変数は2種類ある

var は何度でも代入可能な変数。
val は初期化後別の値を代入できない変数。

関数定義の仕方

基本的には以下のように定義するが、省略可能なものもある。
関数が1文のみの場合中カッコは省略できる。

Javaではメソッドが返す値の型を「戻り値(return type)」と言うが、Scalaでは「結果型(result type)」と呼ぶ。

def  max(x: Int, y: Int): Int = {
  if (x > y)
     x
  else
     y
}

関数が意味のある値を返さない場合Unit型が返る

Unit型はJavaのvoidに似ており、Javaのvoidを返すメソッドはUnitを返すメソッドにマッピングされる。

scala> def greet() = println("Hello, World!")
greet: ()Unit

配列の要素にアクセスするときは()を使う

Javaでは配列の要素にアクセスする場合は[]を使用するがScalaでは()を使用する

// これはJava
String hoge = args[0]

// これはScala
val hoge = args(0)

whileifは基本的にJavaと同じ

以下のようにScalaのwhileifは基本的にJavaと同じ

var i = 0
while (i < args.length) {
    if (i != 0)  // Java同様に中カッコを省略することもできる
        print(" ")
    print(args(i))
    i += 1
}

foreachは関数リテラル、右矢印、関数本体を使う

関数リテラルの構文は以下のように関数パラメータ、右矢印、関数本体の3つからなる

(x: Int, y: Int) => x + y

下の例ではargが関数リテラルでprintln(arg)が関数本体である。

// 例
args.foreach(arg => println(arg))

また関数リテラルが1個の引数をとる1文から成る場合は引数を省略できる

args.foreach(println)

for式では<-を使う

// 例
for (arg <- args)
   println(arg)

上の例のargは必ずvalになる。

<- は「in」と読むと理解しやすいかもしれない。

for (arg in args)

第3章 Scalaプログラミングの次の一歩

Scalaではnewを使ってインスタンスを生成できる

インスタンスを生成する際に型を指定することもできる

val hoge = new Array[String](3)

Scalaではすべての演算がメソッド呼び出し

1 + 2(1).+(2)と同じで1+メソッドを呼び出し引数に2を渡している

配列の要素アクセスや初期化も同様にメソッドを呼び出している

// 要素アクセス
hoge(0)  hoge.apply(0) は同じ
hoge(0) = "piyo"  hoge.update(0, "piyo") は同じ

// 初期化
val numNames = Array("zero", "one", "two") 
val numNames = Array.apply("zero", "one", "two") と同じ

ScalaのListはイミュータブルである

リストの内容を変更するようなメソッドを呼ぶと、新しい値を持つ新しいリストが作成され、返される。

例えば::はリストの先頭に新しい要素を追加して得られるリストを返す。

scala> val oneTwoThree = 1 :: 2 :: 3 :: Nil
oneTwoThree: List[Int] = List(1, 2, 3)

ちなみにListを初期化する場合は最後にNilをつける必要がある。

scala> val oneTwoThreeFour = 1 :: 2 :: 3 :: 4
<console>:7: error: value :: is not a member of Int
       val oneTwoThreeFour = 1 :: 2 :: 3 :: 4
                                         ^

scala> val oneTwoThreeFour = 1 :: 2 :: 3 :: 4 :: Nil
oneTwoThreeFour: List[Int] = List(1, 2, 3, 4)

タプル(tuple)はリスト同様イミュータブルだが、異なる方の要素を持つことができる

タプルの個々の要素にアクセスするには._と1から始まる要素の番号を指定する

scala> val messi = ("Barcelona", 10)
messi: (String, Int) = (Barcelona,10)

scala> messi._1
res14: String = Barcelona

scala> messi._2
res15: Int = 10

集合(Set)とマップ(map)にはミュータブルなものとイミュータブルなものがある

Setはデフォルトだとイミュータブルになる

//デフォルトだとイミュータブルなSetインスタンスが返る
scala> var realMadrid = Set("Ronald", "Ramos", "Isco", "Modric")
realMadrid: scala.collection.immutable.Set[String] = Set(Ronald, Ramos, Isco, Modric) 

// 新しい要素を追加するとイミュータブルなSetの場合、新しいインスタンスを返す
scala> realMadrid + "Kroos"
res16: scala.collection.immutable.Set[String] = Set(Ronald, Kroos, Ramos, Modric, Isco)

// 元のインスタンスの要素は変わっていない
scala> realMadrid
res17: scala.collection.immutable.Set[String] = Set(Ronald, Ramos, Isco, Modric)

ミュータブルなSetを使うにはimportが必要

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val bigFour = mutable.Set("Federer", "Nadal", "Djokovic")
bigFour: scala.collection.mutable.Set[String] = Set(Djokovic, Nadal, Federer)

scala> bigFour += "Murray"
res18: bigFour.type = Set(Djokovic, Nadal, Murray, Federer)

scala> bigFour
res19: scala.collection.mutable.Set[String] = Set(Djokovic, Nadal, Murray, Federer)

Mapもデフォルトはイミュータブルで、ミュータブルなMapを作るにはimportする必要がある。

scala> val artists = Map(1 -> "Suchmos", 2 -> "BUMP OF CHICKEN", 3 -> "Utada Hikaru") 
artists: scala.collection.immutable.Map[Int,String] = Map(1 -> Suchmos, 2 -> BUMP OF CHICKEN, 3 -> Utada Hikaru)
scala> import scala.collection.mutable
import scala.collection.mutable

scala> val rappers = mutable.Map(1 -> "Jay-Z", 2 -> "Nas")
rappers: scala.collection.mutable.Map[Int,String] = Map(2 -> Nas, 1 -> Jay-Z)

scala> rappers += (3 -> "Q-Tip")
res20: rappers.type = Map(2 -> Nas, 1 -> Jay-Z, 3 -> Q-Tip)

scala> rappers
res21: scala.collection.mutable.Map[Int,String] = Map(2 -> Nas, 1 -> Jay-Z, 3 -> Q-Tip)

関数型のスタイルに近づくにはvarを使わないことが大事

// これは命令型スタイル
def printArgs(args : Array[String]): Unit = {
  var i = 0
  while(i < args.length) {
    println(args.(i))
    i += 1
  }
}

// これは関数型スタイル(純粋な関数型ではない!)
def printArgs(args : Array[String]): Unit = {
  args.foreach(println)
}

純粋な関数型は副作用を持たない。結果型がUnitになっていると副作用があることを意味している。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1