LL脳がscalaの勉強を始めたよ その8


Scalaコップ本の第5章に入りますよー、5章は基本型や演算子ですよー

とりあえず今回は5章の前半として基本型とリテラルについてやっていきます

基本型

型の種類

Scalaの基本型を以下に羅列してみる

  • 数値型
    • 整数値型: (Char以外は2の補数表現の符号付き整数)
      • Byte: 8bit
      • Short: 16bit
      • Int: 32bit
      • Long: 64bit
      • Char: 16bit符号なしUnicode文字
    • Float: 32bitのIEEE 754単精度浮動小数点数
    • Double: 64bitのIEEE 754倍精度浮動小数点数
  • その他
    • String: Charのシーケンス
    • Boolean: True or False
型のパッケージと単純名

ちなみに、Scalaの基本型はString以外はscala.◯◯というパッケージ(Stringはjava.lang)メンバーだけども、scala及びjava.langは事前にインポートされるので単純名が使えるとのこと。

自分メモ

Charが数値型に含まれるのね…なのにCharのシーケンスのStringは別扱い…うーん、ちょっと引っかかりそうだな

リテラル

What's リテラル

プログラムのソースコード中に使用される定数のことらしい、例えば234や”hogehoge”などがそれにあてはまるみたい。とりあえず定数って覚えておけばいいかしら?

リテラルとしては次の種類がでてきますー

うーん、だいたい基本型と対応してるようなイメージでいいかしらね

整数リテラル

Int, Long, Short, Byteの各型のリテラルで10進数、16進数、8進数の3つの形式がある。

まず16進数(よくhexで表される)を表現する場合は先頭が0xまたは0Xにする必要がある。例として16進数"3a"(10進数で58)を定義してみマス。

scala> val hex = 0x3a
hex: Int = 58

次に8進数を表現する場合は先頭が0にする必要がある。例として8進数"75"(10進数で61)を定義してみマス。

scala> val oct = 075       
oct: Int = 61

先頭が0以外なら10進数での表現になるのです

scala> val num = 123
num: Int = 123


また、Long型にしたかったら整数リテラルの末尾にLまたはlを付加する

scala> val iLong =123L      
iLong: Long = 123

scala> val hLong = 0xab834dl
hLong: Long = 11240269

ShortやByteは型指定をしてIntリテラルを代入したりする。代入する値が各型の範囲に収まっていればShortやByteとして扱われる

// Shortリテラルの定義
scala> val sInt: Short = 321
sInt: Short = 321

//Byteリテラルの定義
scala> val bInt: Byte = 21  
bInt: Byte = 21

// Byteの範囲外の値を突っ込んだら怒られました
scala> val bInt: Byte = 321 
<console>:4: error: type mismatch;
 found   : Int(321)
 required: Byte
       val bInt: Byte = 321
浮動小数リテラル

浮動小数リテラルは10進の数字から構成されているけれども、小数点だったりオプションのeがついていることもある。

例えばこんな感じ

// 小数点表記
scala> val big = 1.2345
big: Double = 1.2345

// 対数表記(っていっていいのかしら?)
scala> val bigger = 1.2345e1
bigger: Double = 12.345

// eやEの後ろは指数部
scala> val biggerStill = 123E45 
biggerStill: Double = 1.23E47


指数部は10のn乗なので[仮数部]e[指数部]の表現だと[仮数部]×(10の[指数部]乗)ってな感じになりマス。うーん、高校数学っぽい。

上の例のように浮動小数リテラルは標準でDoubleになるので、Floatにしたい場合は末尾にFまたはfを付加する。

scala> val little = 3.21f  
little: Float = 3.21

scala> val little = 3.21e2F
little: Float = 321.0

明示的にDoubleにしたい場合は末尾にDまたはdをつければイイんダヨ

scala> var dlittle = 3.21d
dlittle: Double = 3.21

scala> var dlittle = 3.21E2D
dlittle: Double = 321.0

大文字・小文字のどちらでもOKだけども、どちらかに統一した方が混乱は防げそうな塩梅ね。コミュニティー的におすすめはどちらなんだろう?


ちなみに、FloatもDoubleも範囲外の値を突っ込む場合は丸めて格納してるっぽいです

// floatで丸め(四捨五入で丸められてる?)
scala> var fOver = 1.23456786923456786f
fOver: Float = 1.2345679

//Doubleでキリ☆ステ(こっちは切り捨てみたい)
scala> var dOver = 1.23456789123456786d
dOver: Double = 1.234567891234568

floatとDoubleで丸め処理が違うっぽいんだけども、実際のところはどうなんだろう?そのうち調べてみよう。

文字リテラル

文字リテラルはシングルクォートでUnicode文字を囲んだもの。

よく考えたらScalaで初めてシングルクォート使う気がするんだけど、ここでしか使わないのかしら?

とりあえず例をずらーっと表示

// 普通に文字を指定
scala> val a = 'A'
a: Char = A

// 8進数(Unicode文字コードポイント)表記も可能
scala> val c = '\101'
c: Char = A

// 16進数(Unicode文字の一般表現)表記も可能
scala> val c = '\u0041' 
c: Char = A

どうやら16進数のUnicode文字列はScalaのあらゆるところで使えるみたいで、こんな変態コードもかけます

scala> val B\u0041\u0044 = 1
BAD: Int = 1

変数にUnicode表記を混ぜてます。べ、別に私が作ったんじゃないんだからね、コップ本のサンプルにのってるんだから //


それとエスケープシーケンスで表現される文字列リテラルは下記の通りです

  • 改行: \n (\u000A)
  • バックスペース: \b (\u0008)
  • タブ: \t (\u0009)
  • 改ページ: \f (\u000C)
  • 復帰: \r (\u000D)
  • ダブルクォート: \" (\u0022)
  • シングルクォート: \' (\u0027)
  • バックスラッシュ: \\ (\u005C)
文字列リテラル

ダブルクォートで囲むだけで、基本は文字リテラルと同じー(Unicode文字は使えないけど)

// 文字列の出力
scala> val hello = "world"
hello: java.lang.String = world

// エスケープシーケンスも使ってみる
scala> val hello = "world \\\"\' wide"
hello: java.lang.String = world \"' wide


ただし、エスケープシーケンスが混ざったり、複数行の文字列だったりするとわかりにくくなるので、その場合は次のように書くことができる…まるでPythonのようだ

// エスケープシーケンスがそのまま出力
scala> val hello = """world \\\"\' wide"""
hello: java.lang.String = world \\\"\' wide

// 改行も許容しますぜ
scala> val hello = """world    
                                wide"""  
hello: java.lang.String = 
world
                      wide


ちなみに、改行を許容するもののスペースもそのままなのでprintlnなんかで出力すると下のように崩れてしまうノデス

scala> println(hello)            
world
                      wide

表記を崩したくない場合は下記のように"|"を各行頭にいれて、stripMarginメソッドを使いますよー

scala> val hello = """|world   
                                |wide""" 
hello: java.lang.String = 
|world
                      |wide

scala> println(hello.stripMargin)
world
wide

ちなみにstripMarginは文字列にかかるメソッドなので、こんな感じでもOK

scala> println("""|world       
                 |wide""".stripMargin)
world
wide
シンボルリテラル

(´ε`;)ウーン…さっぱりわからん、でもそれでは進まないので、ワカランなりに自己解釈をつけていこう


まずはシンボル

シンボルはRubyなんかであるシンボルの概念と同じなのかな?Rubyなんかのリファレンスを見ると

シンボルは任意の文字列と1対1に対応するオブジェクト

…とあるので、思い切り雑に言ってしまえばオブジェクトへの参照文字列が”シンボル”って解釈でいいのかしら?

とりあえず上記解釈があっているとすれば、とあるメソッドにオブジェクト名を引数として指定する場合なんかに、型チェックで叩き落とされないようにするためのリテラル→シンボルリテラルって類推することができるなぁ…あってんのか?


そんなオレオレ超解釈でサンプルを読んでいってみる

  • シンボルリテラルの表現
    • `identのように頭にシングルクォーテンションをつけた形で表現
    • 具体的には'identはscala.Symbolによって、Symbol("ident")に展開される

といった前提をもとに次のような、データベース更新処理を定義する

// レコードフィールドの更新メソッド
def updateRecord(r: Symbol, value: Any) {
    // rで指定されたレコードフィールドについて
    // valueの値で更新するような具体的な処理
}

実際にuser_nameというフィールドを更新したい場合に、動的型付け言語なら次のように書いてもOKだったりするけどもScalaだとアウトになる

// user_nameが宣言されていない識別子なのでエラー
updateRecord(user_name, "Mike")


こういうときにシンボルリテラルを使うのです(`・ω・´) キリ

// user_nameがシンボルだとわかるのでコンパイルを通過
updateRecord('user_name, "Mike")

…ようするにシンボル渡したりなんだりを(動的型付け言語のように)簡潔にするためのリテラルってことでいいですか(´・ω・`)、教えて偉い人


ちなみにシンボルは.nameでアクセスすると名前を見出すことが可能みたい

scala>  val s = 'SampleSymbol 
s: Symbol = 'SampleSymbol

scala> s.name
res18: String = SampleSymbol

ついでに同じシンボルリテラルを2度以上書いたとしても、どの式も同一のシンボルオブジェクトを参照するらしい…例えば下のような定義のときにs1とs2の指し示す先は同じってことかな?

scala>  val s1 = 'SampleSymbol 
s1: Symbol = 'SampleSymbol

scala> val s2 = 'SampleSymbol
s2: Symbol = 'SampleSymbol

(´ε`;)ウーン…シンボルリテラルは若干消化不良気味だなぁ…あとは使って慣れるしかないかしら…

Booleanリテラル

True or False OK?

scala> var bool = true
bool: Boolean = true

scala> var bool = false
bool: Boolean = false
以上ー

普段は読む作業(サンプル実行含む)とまとめ作業を別々にやるんだけども、読みながらまとめると流石に時間がかかるなぁ…と実感。休日だからまあいいかしらね。

ということで5章に突入したので次回は演算子だなー、1回じゃ終わらないだろうなー、なー