⇤ ← 2013-03-08 06:01:39時点のリビジョン1
11202
コメント:
|
20970
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 327: | 行 327: |
== 制御構造 == === if式 === * if文じゃなくてif「式」です * 値を返すから {{{#!highlight scala if (条件式) { //真の時の式 }else{ //偽の時の式 } }}} * 条件式が真のときは「真の時の式」の結果が返ってくる * 条件式が偽のときは「偽の時の式」の結果が返ってくる * 関数定義の時と同じように式が複数あるときは最後の式の結果が返ってくる * なので次のように書ける {{{#!highlight scala val hoge = "hoge" println(if (!hoge.isempty) hoge else "default") }}} === while式, do-while式 === * CとかJavaと同じ。 * こいつらはUnit型を返す {{{#!highlight scala var a = x var b = y while(a != 0){ val temp = a a = b % a b = temp } }}} * 一応言っておくとwhileを使うような構文はたいていvarを伴うのでうんこ * 使わないようにしましょう * 再帰を使うのが常道 * というわけで練習がてら再帰で上のプログラムを書き換えてみましょう === for式 === * for式を使いこなすのがScalaマスターへの第一歩です * for「式」なのでやっぱり値を返す * forが値を返すとはどういうことなのかは下のyieldに関する所を読んでね ==== for式の基本的な使い方 ==== * コレクションの反復操作 * コレクションというのは配列とかListとかそのあたり {{{#!highlight scala val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu") for(member <- yuruyuri){ println(member) } }}} * 「member <- yuruyuri」の部分をジェネレータと呼ぶ * memberはvalになっていて、ループが1巡するごとにyuruyuriから1つ要素を取り出してmemberという変数を新たに初期化しているイメージ * memberという変数を生成しているのでジェネレータ ==== Range ==== * 順番に並んでいる数値に対して処理をしたいとき {{{#!highlight scala for(i <- 1 to 10) println(i) //(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)という数列が生成される for(i <- 1 until 10) println(i) //(1, 2, 3, 4, 5, 6, 7, 8, 9)という数列が生成される }}} ==== フィルタリング ==== * for式では取り出す要素に対してif式で条件を追加できる * ifはセミコロンで区切って何個でも並べられる {{{#!highlight scala val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu") for(member <- yuruyuri if member.endsWith("i")){ println(member) } }}} ==== 入れ子のfor式 ==== * <-を複数書くことで入れ子のfor式を1つのfor式でまとめて書くことができる。 {{{#!highlight scala for(i <- 1 to 9; j <- 1 to 9) println(i*j) }}} ==== yield ==== * yieldはfor式の中に記述して使う。 * 下の例の場合for文が実行されるごとにyield i+1というのが実行される。これが実行されるとi+1の結果がfor文の返り値として返ってくるようになる。for文は繰り返し実行されるのでyieldによって返ってきた戻り値は蓄積されていって新しいコレクションになる。つまりこの結果hogeには(1, 2, 3, 4, 5, 6)という配列が格納されることになる。 {{{#!highlight scala val hoge = for (i <- 0 to 5) yield i+1 }}} * yieldの後ろで関数を呼び出すこともできるし、ブロックを書くこともできる。 === try-catch-finally === * ScalaではJavaと違って例外をかならずcatchしなくても良い * 次のように書きます {{{#!highlight scala try{ //例外が発生しそうな処理を書く }catch{ case e: 例外名 => 例外処理 } }}} * catch節での例外名の記述を端折るとすべての例外をcatchする式になります * caseは複数並べて複数の例外に対応出来ます * tryもcatchもブロック式なので、最後の式の結果が全体の結果として返る * 例外が発生しなかったときはtry節の最後の式の結果 * 例外が発生したときは対応する例外の処理の最後の式の結果 * finally節には例外の有無にかかわらず行いたい処理を書く * finally節に値を返すような処理を書くべきではない * tryやcatchで返された値を上書きしてしまう可能性があるから * 例外の有無にかかわらず行いたい副作用を伴う処理に限って書くべき === match式 === * こいつも強力な式なのだが、とりあえず最も基本的な使い方であるswitch文の代わりとしての使用法を見てみる {{{#!highlight scala val yuruyuri = "kyoko" yuruyuri match { case "kyoko" => println("so cute") case "akari" => println("who is she?") case _ => println("so so") // _ はdefaultに相当 } }}} * match式は式なので次のように書くこともできる * 副作用を伴う処理をmatch式の外に出してしまえるのでこっちのほうがいいかも {{{#!highlight scala val yuruyuri = "kyoko" val result = yuruyuri match { case "kyoko" => "so cute" case "akari" => "who is she?" case _ => "so so" } println(result) }}} == ローカル関数 == * Scalaでは関数の中に関数を定義することができる→ローカル関数 {{{#!highlight scala def calc(a:Int, b:Int) = { def add() = a + b def sub() = a - b (add, sub) } }}} * 上のaddとsubという関数はcalc関数の中でしか使用できない * ローカル関数からローカル関数の外の変数にアクセスすることは可能 == 関数リテラル == * 「Scalaらしいループ」というところでforeachメソッドを用いた例を示した * そこで以下の様な表現があった {{{#!highlight scala arg => println(arg) }}} * これはargという引数をとって、println(arg)という処理を実行する関数になっている * より関数型っぽく言えばargという引数に対してprintln(arg)というものを返す関数である * Scalaではこのように名前の無い関数をその場で生成して使うことができる。これを関数リテラルとか無名関数などと呼ぶ == 第一級オブジェクトとしての関数リテラル == * 第一級オブジェクトなどというと仰々しいが、要するに関数リテラルは文字列や数値と同じようにScalaでは扱われていますよということ * 同じように扱われているので変数に代入できる {{{#!highlight scala val hoge = (x:Int) => x+1 println(hoge(10)) }}} == プレースホルダー == * ある引数が関数リテラルの中で一度しか使われないときは名前を書かずアンダーバーで代用することができる。 {{{#!highlight scala val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu") yuruyuri.foreach(println(_)) // yuruyuri.foreach(x => println(x))と同じ }}} * 型を推論できないような書き方をすると怒られる {{{#!highlight scala val myadd = _ + _ //エラーが出る }}} * 型を明示すれば良い {{{#!highlight scala val myadd = (_:Int) + (_:Int) println(myadd(2, 3)) }}} * アンダーバーで置き換えられるのは1回だけなので、アンダーバーの個数だけ引数があるとみなされる * だからアンダーバーが例えば2つあれば2引数の関数なのだ、という認識をコンパイラはするわけ == 部分適用 == * アンダーバーによる置き換えは関数の引数リストに対しても適用出来る。 {{{#!highlight scala def sum(a:Int, b:Int, c:Int) = a + b + c val sum2 = sum(_:Int, 3, _:Int) }}} * 上の操作によってsum関数の第2引数に3を与えた引数を2つとる関数を新しく作ることが出来た * このように関数に引数の一部分だけを適用して新しい関数を得ることを部分適用という * 上の操作をした上で下のような操作をすることができる {{{#!highlight scala println(sum2(2, 4)) //9が出力される }}} * 部分適用とは言うものの1つも引数を適用しないこともできる {{{#!highlight scala def sum(a:Int, b:Int, c:Int) = a + b + c val sum2 = sum _ }}} * 上の操作の結果sum2にはsumと同じ挙動をする3引数の関数が格納されている状態になる === 部分適用の内部的動作 === * 次のコードを考える {{{#!highlight scala def sum(a:Int, b:Int) = a + b val sum2 = sum _ }}} * sum2にはsumに引数を0個部分適用した結果の2引数関数のオブジェクトが格納されている * sum2に格納されているものは実は厳密にはsumで定義されている関数そのものではない {{{#!highlight scala sum2(2, 3) }}} * 上の式を評価するとsum2オブジェクトが持っているapplyというメソッドが呼び出され、sum2に対応する関数、すなわちsum関数が取り出され適用されるという形になっている === アンダースコアの省略 === * 「その関数式を記述する箇所が関数呼び出し必須である」ようなときアンダースコアを省略することも可能である {{{#!highlight scala val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu") yuruyuri.foreach(println) // foreachメソッドの引数には呼び出す関数を必ず記述しなければならないため、println(_)のアンダースコアは省略できる }}} == クロージャ == === 束縛変数と自由変数 === == 高階関数 == == カリー化 == == 名前渡しパラメータ == |
目次
なにこれ
- コップ本にだいたい則ってScalaについてまとめたもの。
- Scala勉強会とかで使いたい。
- Scala処理系が入っている前提。
とりあえず触ってみる
- scalaコマンドを実行するとインタプリタが立ち上がる。
- 出力にはprintlnメソッドを使う。
- printlnメソッドはUnit型の唯一のメンバである()を返す→Javaのvoidに相当
変数の定義
- 上の定義の時にわざわざ型名(String)って書かなかった
型が明らかなときは型名を書かなくてもいい→型推論
- もちろん書いてもいい
- 変数の後ろにコロンを書いて型名
varとval
- 変数を定義するときにはvarとvalというキーワードが使える
- varは何度でも変更できる
- valは一度しか代入できない
- varはなるべく使わないほうがいい
- 変数の値をいつでも変えられるというのは強力だけど、同時にどこで変数の値が変わってしまったのかわかりにくくなる
- 極力valを使い、どうしてもパフォーマンスなどでvarのほうが良い時だけvarを使う。
関数定義
1 def 関数名(引数名:引数型, 引数名:引数型, ...):戻り値型 = 関数の式
- 具体的な例
- returnは省略できる
- 最後の式の結果が返される
- returnを省略して、かつ戻り値の型が明らかなら戻り値の方も省略できる
- 一行ならそもそも中括弧もいらない
1 def hello(name:String) = "Hello! " + name
単独で動くプログラムをつくってみる
- テンプレ
- 今はおまじないだと思いましょう
- argsはStringの配列になっていてmainメソッドの引数です。
- かけたらコンパイルします
scalac hoge.scala
- 実行します
scala hoge
- scalacじゃなくてfscを使うと便利です
- fscを使うと2回目以降コンパイルが速くなる
Scalaらしいループ
- argsの中身を順番に表示するプログラムを書いてみる
- こんなプログラムを書いてみよう
- scalaでは配列の添字は丸括弧
- インクリメントの演算子がないのでi += 1で我慢する
- 行末のセミコロンはいらない
- このプログラムは良くない
- varがあるから
- 配列にはforeachメソッドというのが用意されている
- 関数を引数にとってその関数を配列の要素に対して実行する
1 args.foreach((arg:String) => println(arg))
- argの型はStringなのはわかりきってるのでargの型名は省略できる
1 args.foreach(arg => println(arg))
配列
- 同じ型しか要素にとれない
- valで宣言しても要素の変更は自由
配列の生成
1 val hoge = Array[String](3)
配列への代入と参照
便利な配列の生成方法
1 val hoge = Array("Akari", "Kyoko", "Yui", "Chinatsu")
リスト
- 同じ型しか要素にとれない
- 要素の変更は(通常)不可能
- mutableなリストというのが特別に用意してあって、それを使えば良い。もちろん推奨はされない。
リストの生成
1 val hoge = List(2, 3, 5, 7)
リストの結合
リストの先頭に要素を追加
- これは実は下の糖衣構文になっている。つまりListの::というメソッドを呼び出してる
1 hoge.::(2)
- ::や:::のことをconsという
タプル
- 違う型の要素も持てる
- 要素の変更は不可能
- 言語の制約上22個しか要素が持てない
タプルを作る
1 val hoge = (1, "Kyoko", 2, "Akari")
タプルを使う
- タプルの要素番号はHaskellなどの慣習から1番スタートになっている
クラス
- Scalaはオブジェクト指向言語なのでクラスを持っている
- Javaとかと同じようにnewでクラスからオブジェクトを生成できる
1 val a = new Hoge()
- オブジェクトをval宣言の変数に代入しても、そのオブジェクトのフィールドがvar宣言されていれば再代入できる
メソッド
- メソッドの引数は勝手にvalで定義される
- 勝手に変えられないようにするため
- 戻り値がUnitのメソッドは=を省略できる。ただし=を省略すると中括弧を省略できない。
- メソッドをprivateにするには次のように記述する
1 private def hello() = "Hello, World!"
シングルトンオブジェクト
- Scalaは徹底したオブジェクト指向
- オブジェクトに属していないものの存在なんて許せない!!!!
- なのでstaticがありません
- そのかわりにシングルトンオブジェクトというのがある
シングルトンオブジェクトの定義
シングルトンオブジェクトの性質
- シングルトンオブジェクトはコードが実行されるときに自動的に生成される
- またあるスコープの中でひとつしかないことが保証される
- 多分後述
- 先ほど「単独で動くプログラムをつくってみる」の所でobjectうんたらと書いたのは、プログラム実行開始時にシングルトンオブジェクトを生成させてその中のmainメソッドを実行するというふうにScalaが仕組まれているから。
コンパニオンクラスとコンパニオンオブジェクト
- シングルトンオブジェクトと同じ名前のクラスを定義するとそれぞれコンパニオンオブジェクトとコンパニオンクラスと呼ばれるようになって、対で扱われるようになる。
- コンパニオンオブジェクトからはコンパニオンクラスにアクセスすることができる。
- コンパニオンオブジェクトはシングルトンオブジェクトなので、プログラム実行開始時に勝手に生成される。
- なのでstatic代わりに使える。
コンパニオンオブジェクトの例
基本型
- Scalaには次のような型がある
- 数値型
- Byte
- Int
- Long
- Short
- Double
- Float
- Char
- String
- Boolean
- 数値型
基本リテラル
整数リテラル
- Int, Long, Short, Byte型のリテラル。
- 10進、8進、16進表記可能。
浮動小数点リテラル
- 10進表記のみ。
- 指数表記もできる。
文字リテラル
- シングルクォートでUnicode文字を囲んだもの。
文字列リテラル
- 文字列をダブルクォートで囲ったもの。
- 改行するとそれもそのまま文字列として反映される
シンボルリテラル
- シンボルもやはり文字列を表すためのリテラル
- 次のように表記する
- 文字列リテラルとの違いは文字列へのシンボルリテラルであれば必ず同じオブジェクトへの参照になっているということ
Booleanリテラル
- TrueとFalseがある
演算子
- 演算子はメソッドです
- 演算子の種類はJavaに準ずるので適当にググってね
比較演算子について
- 1つだけ注意を要するのが比較演算子の==
- Javaではオブジェクトとしての等価性を判定するものだった
- Scalaでは内部的にJavaのequalsメソッドを呼び出しているので、==はオブジェクトの持っている値そのものの比較になる
演算子の優先順位について
- Scalaでは演算子の優先順位は演算子の名前で決まっている
- 演算子は特別な存在ではなく、ただのメソッドだから
- 例えば*メソッドは+メソッドよりも優先順位が高いとかいう決め方をしている
リッチラッパー
- ScalaではJavaの演算子に加えてもっと賢い演算子が使えるようになっている
- 例えば
- これはリッチラッパーというクラスによって用意されている。ふだん使うぶんにはリッチラッパーという存在を意識せずにそのまま使うことが出来る。
制御構造
if式
- if文じゃなくてif「式」です
- 値を返すから
- 条件式が真のときは「真の時の式」の結果が返ってくる
- 条件式が偽のときは「偽の時の式」の結果が返ってくる
- 関数定義の時と同じように式が複数あるときは最後の式の結果が返ってくる
- なので次のように書ける
while式, do-while式
- CとかJavaと同じ。
- こいつらはUnit型を返す
- 一応言っておくとwhileを使うような構文はたいていvarを伴うのでうんこ
- 使わないようにしましょう
- 再帰を使うのが常道
- というわけで練習がてら再帰で上のプログラムを書き換えてみましょう
for式
- for式を使いこなすのがScalaマスターへの第一歩です
- for「式」なのでやっぱり値を返す
- forが値を返すとはどういうことなのかは下のyieldに関する所を読んでね
for式の基本的な使い方
- コレクションの反復操作
- コレクションというのは配列とかListとかそのあたり
「member <- yuruyuri」の部分をジェネレータと呼ぶ
- memberはvalになっていて、ループが1巡するごとにyuruyuriから1つ要素を取り出してmemberという変数を新たに初期化しているイメージ
- memberという変数を生成しているのでジェネレータ
Range
- 順番に並んでいる数値に対して処理をしたいとき
フィルタリング
- for式では取り出す要素に対してif式で条件を追加できる
- ifはセミコロンで区切って何個でも並べられる
入れ子のfor式
<-を複数書くことで入れ子のfor式を1つのfor式でまとめて書くことができる。
1 for(i <- 1 to 9; j <- 1 to 9) println(i*j)
yield
- yieldはfor式の中に記述して使う。
- 下の例の場合for文が実行されるごとにyield i+1というのが実行される。これが実行されるとi+1の結果がfor文の返り値として返ってくるようになる。for文は繰り返し実行されるのでyieldによって返ってきた戻り値は蓄積されていって新しいコレクションになる。つまりこの結果hogeには(1, 2, 3, 4, 5, 6)という配列が格納されることになる。
1 val hoge = for (i <- 0 to 5) yield i+1
- yieldの後ろで関数を呼び出すこともできるし、ブロックを書くこともできる。
try-catch-finally
- ScalaではJavaと違って例外をかならずcatchしなくても良い
- 次のように書きます
- catch節での例外名の記述を端折るとすべての例外をcatchする式になります
- caseは複数並べて複数の例外に対応出来ます
- tryもcatchもブロック式なので、最後の式の結果が全体の結果として返る
- 例外が発生しなかったときはtry節の最後の式の結果
- 例外が発生したときは対応する例外の処理の最後の式の結果
- finally節には例外の有無にかかわらず行いたい処理を書く
- finally節に値を返すような処理を書くべきではない
- tryやcatchで返された値を上書きしてしまう可能性があるから
- 例外の有無にかかわらず行いたい副作用を伴う処理に限って書くべき
- finally節に値を返すような処理を書くべきではない
match式
- こいつも強力な式なのだが、とりあえず最も基本的な使い方であるswitch文の代わりとしての使用法を見てみる
- match式は式なので次のように書くこともできる
- 副作用を伴う処理をmatch式の外に出してしまえるのでこっちのほうがいいかも
ローカル関数
- Scalaでは関数の中に関数を定義することができる→ローカル関数
- 上のaddとsubという関数はcalc関数の中でしか使用できない
- ローカル関数からローカル関数の外の変数にアクセスすることは可能
関数リテラル
- 「Scalaらしいループ」というところでforeachメソッドを用いた例を示した
- そこで以下の様な表現があった
1 arg => println(arg)
- これはargという引数をとって、println(arg)という処理を実行する関数になっている
- より関数型っぽく言えばargという引数に対してprintln(arg)というものを返す関数である
- Scalaではこのように名前の無い関数をその場で生成して使うことができる。これを関数リテラルとか無名関数などと呼ぶ
第一級オブジェクトとしての関数リテラル
- 第一級オブジェクトなどというと仰々しいが、要するに関数リテラルは文字列や数値と同じようにScalaでは扱われていますよということ
- 同じように扱われているので変数に代入できる
プレースホルダー
- ある引数が関数リテラルの中で一度しか使われないときは名前を書かずアンダーバーで代用することができる。
- 型を推論できないような書き方をすると怒られる
- 型を明示すれば良い
- アンダーバーで置き換えられるのは1回だけなので、アンダーバーの個数だけ引数があるとみなされる
- だからアンダーバーが例えば2つあれば2引数の関数なのだ、という認識をコンパイラはするわけ
部分適用
- アンダーバーによる置き換えは関数の引数リストに対しても適用出来る。
- 上の操作によってsum関数の第2引数に3を与えた引数を2つとる関数を新しく作ることが出来た
- このように関数に引数の一部分だけを適用して新しい関数を得ることを部分適用という
- 上の操作をした上で下のような操作をすることができる
- 部分適用とは言うものの1つも引数を適用しないこともできる
- 上の操作の結果sum2にはsumと同じ挙動をする3引数の関数が格納されている状態になる
部分適用の内部的動作
- 次のコードを考える
- sum2にはsumに引数を0個部分適用した結果の2引数関数のオブジェクトが格納されている
- sum2に格納されているものは実は厳密にはsumで定義されている関数そのものではない
1 sum2(2, 3)
- 上の式を評価するとsum2オブジェクトが持っているapplyというメソッドが呼び出され、sum2に対応する関数、すなわちsum関数が取り出され適用されるという形になっている
アンダースコアの省略
- 「その関数式を記述する箇所が関数呼び出し必須である」ようなときアンダースコアを省略することも可能である
クロージャ
束縛変数と自由変数
高階関数
カリー化