bussorenre Laboratory

hoge piyo foo bar

Scala におけるList の操作

Programming in Scala 第三版16章 リストの操作

言語によっても仕様が異なり混乱しやすいリスト。 Scala における 配列とリストの違いは

  1. リストはイミュータブルオブジェクトで、リストの要素は代入によって置き換えられない
  2. 内部的にはLinked List

配列と同じ部分でいうと、Scala におけるArray, List は[T]で示される型のみを格納できる。

// String 型しか格納できない
val fruits: List[String] = List("apple", "banana", "cherry")

// nums 型しか格納できない。型を省略した場合、型推論でなんとかしてくれる
val nums = List(1,2,3,4,5)

中間演算子によるこういう書き方も可能。 この場合 :: は右結合の演算子なので、() を省略しない場合こういう順番になる

val nums = 1 :: 2 :: 3 :: 4 :: Nil

val nums = 1 :: ( 2 :: (3 :: (4 :: Nil)))

リスト同士の連結は

val nums = List(1,2,3,4) ::: List(5,6,7)

この時、もちろん 両項の List[T] 型は一致していなければならない。 この演算子も右結合

パターンマッチによるリストの要素の分解が出来る。

scala > val List(a, b, c) = fruit

a: String = apple
b: String = banana
c:String = cherry

上記構文では、List の要素数が3の型に限られてしまうので、要素数が可変の場合は、 (リストの要素数がわからない場合は)先程の中間演算子を用いるともっとあっさり解決する

scala> val a :: b :: remining = fruit

a: String = apple
b: String = banana
remining:List[String] = List(cherry)

Scala を書く時に他言語と混乱しないための文法備忘

Scala 初めてまだ数日勢。毎日ずっと書いていないと、元々書いていた他の言語に邪魔されて思考が途切れてしまう。 特にSwift と混合しやすい。次点で Go, javascript と混合してしまいやすい。

苦しんで自分にscala を叩き込む

宣言

val, var を使う。

val hoge = new Object()
var i = 100

特にval。よく間違えてlet とか const とか書いて怒られている。さすがにauto と書くことはない。

メソッド宣言と呼び出し

まずは宣言

def hogepiyo(arg1: type1, arg2: type2): ReturnType = {
  // write something
}

def foobar(arg1: Type1, arg2: Type2): ReturnType = foo * bar

呼び出しが少しややこしい。

qiita.com

からの抜粋になるが、

class Hoge {
  //引数リストがないメソッド
  def f1 = 1
  //引数がないメソッド
  def f2() = 1
  //引数が1つのメソッド
  def f3(x: Int) = x * 2
  //引数が複数のメソッド
  def f4(x: Int, y: Int) = x * y
}

//引数リストがないメソッド
////hoge.f1()
////hoge f1()
hoge.f1                                           //> res0: Int = 1
hoge f1                                           //> res1: Int = 1
//空行は伊達じゃないよ

//引数がないメソッド
hoge.f2()                                         //> res2: Int = 1
hoge.f2                                           //> res3: Int = 1
hoge f2()                                         //> res4: Int = 1
hoge f2                                           //> res5: Int = 1
//空行は伊達じゃないよ

//引数が1つのメソッド
hoge.f3(1)                                        //> res6: Int = 2
hoge f3(1)                                        //> res7: Int = 2
hoge f3 1                                         //> res8: Int = 2
hoge.f3 { 1 }                                     //> res9: Int = 2
hoge f3 { 1 }                                     //> res10: Int = 2
////hoge.f3 1

//引数が複数のメソッド
hoge.f4(1, 2)                                     //> res11: Int = 2
hoge f4(1, 2)                                     //> res12: Int = 2
////hoge.f4 1, 2
////hoge f4 1, 2

基本的にはjava と同じ書き方をすれば動くっちゃ動く。が、関数型っぽくないらしい。引数がない時に()を省略できるのはまぁruby とかでもやってきたから慣れれば出来る。

後置記法というらしいが、

hoge piyo

みたいな感じで、.()も省略されている場合はその次の行は空行である必要があるみたい。

hoge piyo foo

みたいな感じで、引数が一つの場合は後ろを空行にしなくてもいいらしい。この差異の意図がよくわかってない。 例文として、0 to 3 のような演算子のように使うメソッドは省略することが多い的なことが書いてあった。なんとなくわからんでもない。

あとは、副作用があるかないかでも変わる。副作用がある場合は必ずhoge.piyo(foo) みたいに書く。

コンストラクタ宣言

class Hoge(a: Type1, b:Type2) {
  require(a != b)
  // do something
  
  // auxiliary constructor
  def this(c: Type3) = this(c, c*c)
}

ruby / javascriptに近いかも。インスタンスが生成される時、上から順に実行されている。

requireでバリデーションが出来る。くっそ便利。バリデーションに失敗したら例外が帰る。

Scala ではコンストラクタはただ一つのプライマリコンストラクタを持つことが出来る。それ以外のコンストラクタは補助コンストラクタ(Auxiliary Constructor) と呼ばれ、必ずプライマリコンストラクタ(Primary Constructor)を呼ぶ必要がある。

ここはSwift で苦しんだのでわりとあっさり理解した。

制御構文

厳密にはscala には制御構文はない(本当に?) if 等は全部 if 文ではなく if 式である。 式 = statement なので、必ず結果を返す。

if式

要するに三項演算子だと思えば理解が早い。

val a = if (a or b) functionA else functionB

さて、else が必要ないときはelse を省略することが出来るが、() というUnit 型が返される(何もしないという副作用だけを返す)

if (a > 0) {
    println("hoge")
} else {
    println("piyo")
}

このようなif文のような事も普通に出来る。この時、計算結果を返さないので副作用のみを扱う関数2つを定義し、条件式でどっちを実行するか決定している。みたいなイメージになる。

if 式 というかif 関数と思えば扱いやすいのかな。3値(条件式, 関数1, 関数2)を元に演算結果を返す。みたいな。

for 式

あとで別記事で書く

まだWIP

Intelli J Idea でScalaを書く環境を整えてく(自分用)

業務用ということで、会社から Inteli J Idea を支給してもらった。この機会にぜひとも乗りこなしたい。

Emacs バインドをある程度使えるようにする

Emacs使いのための IntelliJ IDEAキーマップ チートシート を参考にする。

Emacs+ Patched というプラグインを入れ、 Preferences -> Keymap で Emacs+ を選択し、Duplicate... でコピーを作成

Preferences -> Appearance & Behavior -> Presentation Assistant で、Main keymap に作成したコピーを指定

変更 キーバインド アクション 追加 Ctrl+X, Ctrl+B Switcher

PlantUML

IntelliJ IDEAでPlantUMLを書く

PlantUML でドキュメントを書いているので、同じくプラグインをぶっこむ。

初めて入れてみたけどくっそ便利すぎて今までの人生は何だったのか感

Scala における Unit型と副作用

C系とか Java 系みたいな、手続き型には無い(無くはないけどそんなに重要視されていない)概念に 作用と副作用がある。

関数型言語では、プログラムは値を返す存在とみなされる。

Programming in Scala においては 3.5 あたりに

まずは、val、イミュータブルオブジェクト、副作用のないメソッドを優先させ、それらで出来る限りやってみる。 明確なニーズと正当化できる理由がある時に限り、var、ミュータブルオブジェクト、副作用のあるメソッドを使う

と書いてある。

副作用とはどういうことか。

例えば「標準出力にHello world を出力する」という仕様に対しては、前回書いたHelloworld だと

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, Scala world!")
  }
}

こうなっており、引数を受け取って結果(作用)を返していない(=副作用しか無い)メソッドになっている。 こういうメソッドにはUnit型が使われる。

Hello world in Scala

どんな事もまずは世界に挨拶することから始まる。

Scala で 雑に Hello World を書くとこうなる。

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, Scala world!")
  }
}

基本的にScalaJava class コードに変換されるので、JVM の制約を多少なりとも受ける。例えば、main(args: Array[String]) 型のメソッドを持つクラスをエントリーポイントに持つ等。

型定義は後置。Swift やgo も後置なので違和感はない。

Unit 型 というのは副作用のみを扱う関数(メソッド??)に付けられる型。このへんはまだ良くわかってないのでわかってきたタイミングで書く。

println, Array, String, Unit はどこで定義されて来ているのか?

Scalaパッケージメモ(Hishidama's Scala package/import Memo) によると、

名前 用途 備考
scala._ Scalaの基本クラス
scala.Predef._ 暗黙に使用できる関数が定義されているシングルトンオブジェクト Javaのstaticインポートに相当する方式でインポートされる。Predefに定義されているメソッドを関数として(オブジェクト名の指定なしで)使えるようになる。
java.lang._ Javaの基本クラス

が、自動的に(暗黙的に)import されていて、利用できる。

ScalaStandard Library によると、scala.Predef は

package scala
object Predef {

  // classOf ---------------------------------------------------------

  /** Returns the runtime representation of a class type. */
  def classOf[T]: Class[T] = null
   // this is a dummy, classOf is handled by compiler.

  // Standard type aliases ---------------------------------------------

  type String    = java.lang.String
  type Class[T]  = java.lang.Class[T]

  // 中略
  def println() = Console.println()
  def println(x: Any) = Console.println(x)

みたいな感じで記述されている。

$ scala helloworld.scala

で実行できる。スクリプト言語みたいな動作をする(多分実際には内部的にclass ファイルを作っているとは思うが)

クラスコードに変換して実行するには

# scalac helloworld.scala ではなく scala.helloworld.scala になってたのを修正しました
scalac helloworld.scala
scala HelloWorld

(雑談) Scala はじめました

夏の暑い時期に冷やし中華を始めるラーメン屋の如く、私もScala をはじめました。

最低限の知識だけで、可能な限りJVMの世界を避けてきたのですが、ついに通らずにはいられなくなったのですが、幸い 素のJava6 とかじゃなくて、scala の世界で戦えるのでそれはラッキーだったかも。

とりあえず Scala スケーラブルプログラミング(Programming in Scala)をちょくちょく読んでます。 https://www.amazon.co.jp/Programming-Scala-Comprehensive-Step-Step-ebook/dp/B01EX49FOU/ref=sr_1_1?ie=UTF8&qid=1533103722&sr=8-1&keywords=scala+in+programming

元々プロダクションでruby を書いてましたが、やっぱり言語も進化する。2000年代に生まれている言語のほうが洗練されてる。

まだ全然読み切れていないですが、scala を経てSwift の良さも更に噛み締めてます。

ということで、しばらくscala マンになります。わーい