昭和の日 強化合宿
4月から未経験エンジニアとして就職し、scalaを学んでいます。
本日はドットインストールのscala入門で基礎学習を進めていきたいとおもいます。
これが終了したあとは会社の同僚に教えていただいたN予備校のscala教材を進めたいと思います。
N予備校はscalaを採用しているドワンゴさんが監修していることもあってどんなカリキュラムなのか楽しみです。
最後にはニコニコ動画を実現するカリキュラムとなっているそうな。
最近公式が投稿されたニコニコ体操は久々にわらった
さてはりきっていきましょう!
文字列内で変数を扱う
私は●●です。
この●●に名前の変数を埋め込みたい場合ってありますよね
今回はそんな文字列について学習しましたのでアウトプットします。
今回はこちらを用意
object MyApp {
def main(args: Array[String]): Unit = {
val name = "eve"
val score = 80
val height = 167.0
}
}
変数を埋め込む
文字列内で変数を埋め込む場合には
文字列の前にsをつけます。
そして変数の前に$をつけます。
println(s"私は $name です")
出力結果
私は eve です
文字列の中で式を使用する
式を使用した場合には{}で囲ってあげることで実現できます。
println(s"name: $name, score: ${score + 10}, height: $height")
書式を追加する
埋め込んだ変数をそのまま出力するのではなく、何らかの書式を与えたい場合には
変数名の後に%をつける、その後ろに
文字型ならsを
数字型ならdを
浮動小数ならfを
つける
println(f"name: $name%s, score: $score%d, height: $height%f")
実行結果
name: eve, score: 80, height: 167.000000
浮動少数は第6の位まで表示されていますね
これだけだと何が起きたかわからないと思いますが
書式の追加が可能となっている状態になります。
次をご覧ください
出力する際に余白を追加したい
スペースを使わずに余白を追加することができます。
先ほど追加した%の後に空けたいマス分の数字を記入します
println(f"name: $name%10s, score: $score%10d, height: $height%10f")
出力結果
name: eve, score: 80, height: 167.000000
このように余白が出力されます
ちなみに変数の出力は右寄せになります。
余白出力し、左寄せにする
出力を左寄せにしたい場合には数字の前に-をつけてあげます。
println(f"name: $name%-10s, score: $score%-10d, height: $height%-10.2f")
出力結果
name: eve , score: 80 , height: 167.000000
左寄せになりましたね。
小数第●位までの出力を指定したい
println(f"name: $name%-10s, score: $score%-10d, height: $height%-10.2f")
こうしてあげることで、小数点第2位までの出力にすることを指定することができます。
出力結果
name: eve , score: 80 , height: 167.00
以上、文字列の中で変数を扱うでした。
match
matchの基本形
val signal = "red"
val result = signal match {
case "red" => "stop"
case "blue" => "go"
case "yellow" => "caution"
case _ => "wrong signal"
}
println(result)
出力結果
stop
評価したい変数名の後にmatch {}で記述します。
case ●● => ●●
としてあげることで、変数が●●であれば●●返すという意味になる
今回は信号を表現しており、変数signalがredならstopを返している
一番下の_は他のどれでもない場合を指定している。
また、caseの記述方法で
|を使用するとAまたはBという意味をもつ
case "blue" | "green" => "go"
信号が青または緑ならばgoという意味だ
値を変数として受け取る
resultの値をmatch式の中で変数として受け取り
そのまま変数としてお扱うことができる
def main(args: Array[String]): Unit = {
val name = "eve"
val score = 80
val height = 167.0
val signal = "pink"
val result = signal match {
case "red" => "stop"
case "blue" | "green" => "go"
case "yellow" => "caution"
case other => s"wrong signal: ${other}"
}
println(result)
出力結果
wrong signal: pink
今回の場合だと、他のどれでもなかった場合はotherとして受け取り、その後の式でotherを文字列に埋め込んで使用することができる。
ちなみにこのotherはmatch式の中でのみ使用することができる。
def main(args: Array[String]): Unit = {
val name = "eve"
val score = 80
val height = 167.0
val signal = "pink"
val result = signal match {
case "red" => "stop"
case "blue" | "green" => "go"
case "yellow" => "caution"
case other => s"wrong signal: ${other}"
}
println(result)
println(other)
}
とした場合はコンパイルでエラーが発生
[error] println(other)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed 2020/04/29 11:33:11
otherという値が見つかりませんとのこと
match式を利用することで様々なことを実現できるので
上手に使えるようになりたいですね。
アクセス修飾子
修飾子をつけることでクラスのアクセスに制限をつけることができる。
何もつけない(public)
何もつけていない状態ではどこからでもアクセスできる状態になっている。
javaではpublicと呼ばれる状態。
private
そのクラスの中でのみアクセスが可能な状態にすることができる。
protected
そのクラスとそのクラスのサブクラスの中でのみアクセス可能な状態にすることができる。
final
classにつけた場合は継承できない
menmbersにつけた場合はoverrideできない
という制限をつけることができる。
object
オブジェクトを定義することで、インスタンス化せずに使用できるメンバーを定義することができる。
オブジェクトと同名のクラスがあると様々なことを行うことができる。
クラスとオブジェクトが同名の場合それぞれ
コンパニオンオブジェクト
コンパニオンクラス
と呼ぶ。
object MyApp {
def main(args: Array[String]): Unit = {
}
}
こちらのMyAppオブジェクトはjavaの仮想マシンがインスタンス化せずにmainメソッドを実行したいのでオブジェクトになっている。
抽象クラス
継承されることを前提とした、直接インスタンス化することができないクラスのこと。
抽象クラスを継承してできるクラスのことを具象クラスという。
抽象クラスでメソッド名のみを定義した場合、そのフィールドは具象クラスの方で必ず定義しなくてはならない。
定義がない場合にはエラーになる。
抽象クラスにすることで、必要なメンバーの定義漏れを防ぐことができる。
abstract class User {
def sayHi()
}
class Japanese extends User {
def sayHi() = println("こんにちは")
}
class American extends User {
def sayHi() = println("Hi")
}
object MyApp {
def main(args: Array[String]): Unit = {
val aki = new Japanese
val tom = new American
aki.sayHi()
tom.sayHi()
}
}
トレイト
既存のクラスに機能を合成することができる。
この場合だとprintメソッドを既存のUserクラスに継承して使用可能にすることができている。
継承は一つだけでなく複数可能となっており、その場合は
extends ●● with ○○
とすることで複数の継承が可能。
trait Printable {
def print() = println("now printing...")
}
class User extends Printable
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
user.print()
}
}
traitの問題
トレイトを複数継承した際に、同じ名前のメソッドを継承しているとどちらのメソッドを実行して良いかわからずエラーになることがあります。
この問題を解決する2つの方法
overrideする方法
Userクラスの中でoverrideをしてあげるという方法になります。
override def getInfo() = super[Printable].getInfo()
このようにoverrideを記述し、どのトレイトのメソッドをoverrideするのか
今回は継承元であるPrintableのgetInfo()をoverrideなので、このように記述してあげる。
traitを合成した順番で優先度を決める方法
メソッドをoverride扱いにしてあげることで、後に継承されたtraitのメソッドを使用するという方法。
この場合、各traitにoverrideを記述するので、そのoverride元となるtraitが必要になります。
trait Commonを定義し、そこにメソッドを記述してあげます。
定義した順でoverrideしていくので
CommonをPrintableがoverride
PrintableをSharableがoverride
することでSharableのメソッドが有効になります。
trait Common {
def getInfo()
}
trait Printable extends Common {
def print() = println("now printing...")
override def getInfo() = println("print")
}
trait Sharable extends Common {
override def getInfo() = println("share")
}
class User extends Printable with Sharable {
}
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
user.getInfo()
}
}
型パラメータ
以下のような数字を引数で受け取るとそれを3つ並べて表示するMyIntクラスがあったとします。
class MyInt {
def getThree(i: Int) = println(s"$i $i $i")
}
object MyApp {
def main(args: Array[String]): Unit = {
val i = new MyInt
i.getThree(3)
}
}
これをInt型だけでなく、String型でも実装したい場合もう一度同じようなクラスを定義するのは億劫です。
そこで以下のように実装することで、どんな型でも受け付けるクラスを定義することができる。
class MyData[T] {
def getThree(md: T) = println(s"$md $md $md")
}
object MyApp {
def main(args: Array[String]): Unit = {
val i = new MyData[Int]
val s = new MyData[String]
i.getThree(3)
s.getThree("Hello")
}
}
MyData[T]とすることで、引数に全ての型を受け取れるようになる。
[T]はTypeのイニシャル
mainの初期化の際に型をしてあげる。
関数オブジェクト
関数をオブジェクト化して呼び出してあげる処理
メソッドの中で変数に関数を定義してあげることで呼び出して使用することが可能。
object MyApp {
def mult(a: Int, b: Int) = a * b
// 省略せずに記述するとこの形
// val mulFunc: (Int, Int) => Int = (a: Int, b:Int) => a * b
// 基本の記述、引数に数字を二つ渡して、乗算を返す
// val multFunc = (a: Int, b: Int) => a * b
// 引数をプレースホルダーとして省略して書くことも可能、その場合は型を指定してあげる
val multFunc = (_ :Int) * (_ :Int)
def main(args: Array[String]): Unit = {
println(multFunc(3, 5))
}
}
カリー化
こちらはいまいち使用方法がつかめていないのですが、
関数の中で引数を受け取り、その中で新たな引数を受け取って処理を行うことができる?
今回だと、引数aを受け取ったあとにbを受け取って処理を行う。
関数の中で関数を扱うことができるので、難しい処理などに利用できる?という認識
ここは開発する中で身に付けたいですね
object MyApp {
def main(args: Array[String]): Unit = {
val multFunc = (_ :Int) * (_ :Int)
val multFuncCarried = (a: Int) => ((b: Int) => a * b)
println(multFunc(3, 5))
println(multFuncCarried(3)(5))
}
}
部分適用
関数の中で引数が一緒のものを複数回使用する際には引数を固定にして、他の引数のみを部分的に適用させることができます。
以下のような関数があったとします。
object MyApp {
def msg(from: String, to: String, text: String) = s"($from -> $to): $text"
def main(args: Array[String]): Unit = {
println(msg("taguchi", "koizumi", "ok"))
println(msg("ichikawa", "koizumi", "no"))
}
}
引数toの値であるkoizumiは変わっていないので、ここは固定にしていきたいと思います。
object MyApp {
def msg(from: String, to: String, text: String) = s"($from -> $to): $text"
def main(args: Array[String]): Unit = {
val msgKoizumi = msg(_: String,"Koizumi", _: String)
//全てをプレースホルダーで受け取る方法はこちら
//val msgFunc = msg _
println(msgKoizumi("taguchi", "ok"))
}
}
このようにKoizumiはあらかじめ引数に設定しておき、その他の引数はプレースホルダーで受け取ることができます。
タプル
データをまとまりとして扱うことができる。
大事な点としては一つ
タプルで扱えるデータは23個まで
val tuple = (1, "Hello", 43.5)
コレクション
値のまとまりのこと
Seq: 順番を記憶して保持することができる
Set: 順番を記憶せず、要素が重複しない
Map: キーと値をセットにして保持することができる
Seqの型であるListは以下のようにして初期化できる
val scores = List(100, 200, 300)
空の状態から作ることもできる
//空のリストを作成
val scores = Nil
//コンスを使用する ::
//空のListの一番目に100を追加する
val scores = 100 :: Nil
//空のリストに要素を複数追加する
val scores = 100 :: 200 :: 300 :: Nil
mutableなMap
基本的にscalaは不変なimmutableを推奨していますが、可変なmutableを扱いたい場合もあります。
その場合には以下のように指定して実装する。
val scores = scala.collection.mutable.Map("taguchi" -> 60, "suzuki" -> 40)
//こうすることで
scores("taguchi" -> 100)
//と変更を加えることができる
//直接記述せずにimportする方法もある
case class
一言でimmutableなclassである。
case classを使用するとパターンマッチを用いた条件分岐を行うことができる。
case class Point(x: Int, y: Int)
object MyApp {
def main(args: Array[String]): Unit = {
val points = List(
Point(5, 3),
Point(0, 0),
Point(3, 4)
)
points.foreach(_ match {
case Point(0, 0) => println("origin!")
case Point(5, _) => println("right edge!")
case Point(x, y) => println(s"$x:$y")
})
}
}
Option型はcase classなのでmatchを使用することができる
Either
Either(イーザー)型
Optionでは値があるかないかを判断できたが
Either型ではエラー処理を行うことができる
Either型はRightとLeftを返す
Rightは値が正常な場合
Leftはエラーの場合を表すことが慣習となっている
Either型もmatchを使用することができる
object MyApp {
def div(a: Int, b: Int): Either[String, Int] = {
if (b == 0) Left("Zero div error")
else Right(a / b)
}
def main(args: Array[String]): Unit = {
div(10, 3) match {
case Right(n) => println(n)
case Left(s) => println(s)
}
}
}
合宿終了
以上昭和の日合宿でした!
今回はドットインストールさんのscala入門を取り組ませていただきました。
traitなどいまいち何かわかっていなかったものに触れることができたので大変よかったです。
会社のカリキュラムと比べると内容のボリュームは薄く感じましたが、初学者が概要をちゃっと知るにはとても良いペースで学習をすることができました。
他にもドットインストールには色々なカリキュラムがあるので、余暇時間に触れてみたいと思います。
GWにはN予備校のカリキュラムを進めていきたいと思います!