Scalaを触ってみて調べた点。
今後も追記予定。
ど初心者なので、指摘等頂けると喜びます。
検証環境
- Windows10
- IntelliJ IDEA : 2019.1.2
- Scala : 2.12.7
- SBT : 1.3.10
Scalaとは
Scala(スカラ、SKAH-lah[2])はオブジェクト指向言語と関数型言語の特徴を統合したマルチパラダイムのプログラミング言語である。名前の「Scala」は英語の「scalable language」に由来するものである(Wikipediaより)
開発環境作成
IntelliJ IDEA を使用する。
以下に導入方法と簡単なチュートリアルがある。
構文
objectとclassの違いは?
object
・シングルトンオブジェクトを示す。
・シングルトンオブジェクトは自動でインスタンスが生成されるため、明示的にインスタンスを生成することは出来ない。
・メソッドに対しては"オブジェクト名.メソッド名"でメソッドにアクセスする。
・私は最初Javaのstaticみたいだなと思ったが、objectは唯一のインスタンスであることを示すだけであり、Scalaとしてstaticをサポートはしていないとのこと。class
・シングルトンではないクラスを示す。
・シングルトンとは違い自動でインスタンスは生成されないため、明示的にインスタンを生成する必要がある。
・メソッドに対しては"インスタンス名.メソッド名"でメソッドにアクセスする。
ざっくり言うとobjectはシングルトンで、classはそうではないということらしい。
参考
インスタンスの作成方法
Javaと同じで普通にnewで作れるが、apply
メソッドを定義することでnewがなくてもインスタンスを作成できる。
object Sample extends App {
val hoge: Hoge = new Hoge
hoge.hoge()
//Fuga,Piyoはnewがなくてもインスタンスが作成できる
val fuga = Fuga()
fuga.fuga()
val piyo = Piyo()
piyo.piyo()
}
//通常
class Hoge {
def hoge() = println("hogehoge")
}
// case class とすることでapplyが定義されnewが不要となる
case class Fuga() {
def fuga() = println("fuga")
}
//コンパニオンオブジェクト
class Piyo() {
def piyo() = println("piyo")
}
object Piyo {
//applyを定義することでnewが不要となる
def apply(): Piyo = new Piyo()
}
関数の定義方法
以下が関数定義の構文。
戻り値の型は暗黙的に評価できる場合は省略可能。
関数の内容が1行の場合は{}
も不要。
def 関数名(仮引数名1 : 型, ...) : 戻り値の型 = { ... }
Int型を2つ引数に持つ戻り値がInt型のメソッド。
object Sample extends App {
println(plus(2, 3))
def plus(a: Int, b: Int): Int = {
a + b
}
//上記は以下でも同じ
def plus(a: Int, b: Int) = a + b
}
参考
フィールドの宣言
class 直下に val, var で宣言する。
class Card(val _suit: Suit, val _rank: Int) {
val suit = _suit
val rank = _rank
var value : String
}
コンストラクタでまとめることもできる。
上の例ではvalue以外はコンストラクタとまとめられる。
class Card(val suit: Suit, val rank: Int) {
var value : String
}
switch文の書き方
Scalaにswitch文はないので、match式で記述する。
_
が default
と同様の動きをする。
<対象式> match {
case <パターン1> => パターン1結果
case <パターン2> => パターン2結果
case _ => デフォルト結果
}
def toDisplayValue(): String = {
rank match {
case 1 => "A"
case 11 => "J"
case 12 => "Q"
case 13 => "K"
case _ => rank.toString
}
}
参考
三項演算子
Scalaに三項演算子はないので、if式を使う。
val rank: Int = 11
//Scalaではアクセサがあるので getXXX という書き方はあまりしないそう
// def getPoint(): Int = {
// if (rank > 10) 10 else rank
// }
def point = if (rank > 10) 10 else rank
参考
アクセサ
Scalaではpublicなフィールドにはアクセサが自動的に定義される。
publicなフィールドはアクセス出来て当然だと思うのだが、厳密にはフィールドはprivateになり、publicアクセサが生成されているそう。
getXXX
、setXXX
ではなくXXX
でアクセスするので、アクセサの命名は慣習に従う。
参考
文字列の結合
Javaと同じで+演算子でできるが、文字列補完でもできる。
//+演算
override def toString = suit.mark + "の" + toDisplayValue
//文字列補完
override def toString = s"${suit.mark}の${toDisplayValue}"
参考
enum
Scalaにはenum
がない。
言語で定義されているenumeration
は使いづらいらしく、object
を作成して疑似的に作るのが主流のようだ。
sealed abstract class Suit(val mark: String) {
def getMark() :String = mark
}
object Suit{
case object SPADE extends Suit("スペード")
case object HEART extends Suit("ハート")
case object DIAMOND extends Suit("ダイヤ")
case object CLUB extends Suit("クラブ")
}
- ScalaのEnumerationは使うな - Scalaで列挙型を定義するには | Scala Cookbook
- Scalaで擬似列挙型を使うとき、case objectを使うか単純なobjectを使うか - Qiita
Listの使い方
当記事への @ka2kama さんのコメントがとても詳しいのでリンク。
- 型の宣言は[]を使う
var list : List[String]
- 初期化
var list : List[String] = List("hoge","fuga")
- 要素の追加は ::
- イミュータブルなので新しいリストを返す。
- 注意
- . は使わない。
- <要素> :: List で追加する。
- List :+ <要素> でも追加できるが効率が悪いので使わない方が良い。
// . での :: の呼び出しはしない
// list = list.::("piyo")
//要素が先
list = "piyo" :: list
// :+ でも要素追加できるが効率が悪いため使われないそう
// list = list :+ "piyo"
コンストラクタの処理
class直下にそのまま記述する。
参考
ループ
for(要素 <- 配列)
でできる。
「要素 <- 配列」をジェネレータという。
ジェネレーターを複数書くことで多重ループを1つのforで記述することが出来る。
var list: List[Account] = List()
val names: List[String] = List("hoge", "fuga", "piyo")
//ループ
for (name <- names) {
println(name)
}
//2重ループ
for (name <- names) {
for (i <- 1 to 3) {
list = Account(i, name) :: list
}
}
println(list)
//上と同じ
//ジェネレーターを複数書くことで多重ループを1つのforで記述することが出来る
for (name <- names; i <- 1 to 3) list = Account(i, name) :: list
println(list)
参考
Listのシャッフル
Random.shuffleを使う。
Random.shuffle(list)
Listの先頭を取得
以下のどちらでもできる。
list(0)
list.head
val c: Card = bill.head
Listから要素を削除
immutableなので削除したい要素をfilterして新たなlistを作成するという手順で疑似的に行う。
下記はどちらも同じ。
list = list.filter(_ != "hoge")
list = list.filterNot(_ == "hoge")
継承のパラメータ
自身のコンストラクタと同じ値を渡す。
Javaでいうsuper(name)
みたいなことをしたい時には以下のようにする。
class User(name : String) extends AbstractPlayer(name) { ... }
標準入力
readLine
を使う。
Scanner#nextLine()
と同じ感じで使える。
val str: String = readLine()
参考
タイトルをインスパイアさせていただきました。