Kotlin Advent Calendar 2012 (全部俺)

JavaプログラマのためのKotlin入門

11日目:クラスと共に暮らす

f:id:ngsw_taro:20071123141531j:plain

いきなりしょうもないダジャレですみませんw 今日からオブジェクト指向に関する機能について紹介していきます。今日はクラスについてお話しします。

クラスとコンストラクタ

では早速Kotlinでクラスを定義してみます。次のコードはメンバをまったく持たないクラス MyFirstClassを定義しています。

class MyFirstClass()

Javaと同じようにキーワード class によってクラスは定義されます。クラス名の後の括弧はコンストラクタです。そして、クラス本体に何も記述する内容がない場合は波括弧を省略できます。このクラスのインスタンスを生成してみましょう。

val instance = MyFirstClass()
println(instance.javaClass) // class MyFirstClass

キーワード new が見当たりませんが、これで正しいのです。コンストラクタの呼び出しはインスタンス生成の場面でしか登場しないので、new は冗長であると設計者が判断したためでしょう。ちなみに javaClass は、すべてのオブジェクトが持つプロパティで、java.lang.Classを表します。

コンストラクタ

クラス名の直後の括弧がコンストラクタに当たります。このコンストラクタのことを、プライマリ・コンストラクタと言います。プライマリ・コンストラクタでは、主にプロパティに値を設定してインスタンスを初期化します。次のコードは、プロパティ name を持ったクラス Student の例です。

class Student(val name : String)

やはり波括弧の対を置いてブロックを形成する必要はありません。name に付いているキーワード val は必要に応じて var とすることも可能です。このクラスの使用例を次に示します。

fun main(args : Array<String>) {
  val taro = Student("Taro")
  println("My name is ${taro.name}.") // My name is Taro.
}

name に val も var も付けなかった場合、変更不可な変数でかつ、そのスコープはそのクラスの匿名イニシャライザのみです。

匿名イニシャライザ

プライマリ・コンストラクタから直接プロパティの値をセットするのは便利ですが、なんらかの処理を施した後にプロパティを初期化したい場合もあります。そこで使用する機能が匿名イニシャライザです。コンストラクタのようにインスタンスの初期化を行うために使用しますが、匿名イニシャライザが呼び出されてインスタンスを生成する、ということはあり得ません。インスタンス生成の唯一の入口はプライマリ・コンストラクタなのです。

例を交えて匿名イニシャライザの書き方を紹介します。先に登場したクラス Student に匿名イニシャライザを導入しましょう。name は生徒(Student)の氏名を表すため、nullや空文字列が渡されないようにしたいところです。言語機能上、この場合はnullを渡すことはできないので*1、空文字列が渡された場合は例外を投げて初期化を中止します。次のようなコードになります*2

class Student(name : String) {
  
  public val name : String
  {
    if(name.length == 0) throw IllegalArgumentException()
    this.name = name
  }
}
 
fun main(args : Array<String>) {
  val taro = Student("Taro")
  println("My name is ${taro.name}.") // My name is Taro.
  
  Student("") // throw EXCEPTION!!!
}

この様に、波括弧の対で匿名イニシャライザを定義します。

インスタンス関数

クラス内に関数を定義することができます。それはインスタンス関数として、クラスのインスタンスに対して呼び出します。

class Square(val size : Double) {
  fun calcArea() = size * size
}
 
fun main(args : Array<String>) {
  val s = Square(4.0)
  println(s.calcArea()) // 16.0
}

まとめと次回予告

今日はオブジェクト指向の要であるクラスの基本を学びました。クラスはプライマリ・コンストラクタと共に定義します。プライマリ・コンストラクタインスタンスを初期化するためのパラメータを受け取ります。たいていはそのままプロパティとして保持しますが、なんらかの処理が必要な場合は匿名イニシャライザを使用します。また、インスタンス関数としてクラス内部に関数を記述することができます。

今夜、JetBrains勉強会でKotlinの紹介ということで登壇させていただきます。明日は小休止ということで、その発表内や勉強会の感想などを書きます。

日記

勉強会でKotlin布教がんばる。Kotlin、盛り上がって欲しい。

*1:この機能については後日説明します。

*2:Kotlinにはこのような検査を行う便利な関数が標準ライブラリにありますので、通常はこのような記述はしないでしょう。

10日目:関数(後)

f:id:ngsw_taro:20121209130628j:plain

10日目の今日は、昨日に引き続きKotlinにおける関数について紹介したいと思います。高階関数や関数リテラルなど、Javaプログラマには馴染みのあまりない関数の使い方が登場します。

関数リテラル

関数リテラルとは、リテラルという言葉が示す通り、宣言を伴わずそのままコードを記述した関数のことです。次の例では関数をリテラルとして記述し、その場で呼び出しています。

val result = {
  (a : Int, b : Int) : Int -> a * b
}(3, 4)
  
println(result) // 12

関数リテラルの書き方を一般化すると次のようになります。

{
    ( 引数リスト ) : 返値型 -> 関数本体
}

関数リテラル内では return は使用できません最後の式(や値)がその関数の返り値と見なされます。

引数がない場合は、引数リストから -> までを省略することができます。

{
    println("Hi")
}()

気付いている方もいらっしゃると思いますが、重要なのは関数リテラルは、リテラルであるが故に変数へ代入できるということです!次のコードは、引数として与えられた整数値を2倍にして返す関数リテラルを変数 twice へ代入しています。代入後に変数を介して関数を呼び出しています。

val twice : (Int) -> Int = {
  (n : Int) -> n * 2
}
  
println(twice(123)) // 246

変数 twice には型が宣言されています。関数リテラルの型は、

( 引数の型リスト ) -> 返値型

と記述します。もちろん型推論が働くので、この場合は型を省略することが可能です。型を明示して、かつ関数リテラルの引数が1つだけの場合、次のような書き方ができます。

val twice : (n : Int) -> Int = {
  it * 2
}

唯一の引数へ it という名前を介して参照することができます。この便利さは高階関数で生きてきます。

高階関数

関数リテラルが変数へ代入できるならば、関数リテラルを関数の引数として渡すこともできるのではないかと推測できます。その通りです!関数を引数として受け取ったり、返り値として返したりするような関数のことを高階関数と呼びます。

fun main(args : Array<String>) {
  sayHello(
    {
      println("$it, world!")
    }
  )
}
 
fun sayHello(f : (String) -> Unit) {
  f("Hello")
}

上記の例のような場合、正確には最後の引数に関数リテラルを取る関数を呼び出す場合は、次のように記述することができます。

fun main(args : Array<String>) {
  sayHello {
    println("$it, world!")
  }
}

ここまでの例では高階関数の良さがわからないと思います(文法にフォーカスして説明して来たので)。高階関数を使うと、関数を抽象化することができます。つまり、高階関数と引数から受け取る関数は別の問題に集中することにより、それぞれの関数を部品化、再利用化することが容易になるのです。おっと、説明が抽象的すぎてしまいましたね。例を示します。

fun IntRange.each(f : (Int) -> Unit) {
  for(i in this) {
    f(i)
  }
}
 
fun main(args : Array<String>) {
  (1..10).each {
    println(it)
  }
}

関数 each を整数値の範囲を表す IntRange の拡張関数として定義しています。この関数では、範囲内の数値を順にループして、個々の数値に関する具体的な処理は、引数 として受け取る関数 f へ転送しています。関数 each の呼び出し側で関数リテラルを引数に渡しているのがわかると思います。この関数リテラルは受け取った数値を表示することに責任を持っています。

重要なのは、繰り返しと表示、それぞれの問題を切り分けて考えることができるということです。また、関数リテラル高階関数の引数として渡す際の構文糖衣により、関数呼び出し + 関数リテラル定義が、あたかも組み込み構文であるかのように見えることにも注目してください。つまり、これらの機能のおかげで、Kotlinは柔軟かつ可読性に優れたコードを容易に書けるのです。

Kotlinの標準ライブラリには、高階関数が多く見られます。特にコレクションの操作では高階関数の恩恵を最大限享受できます。

中置呼び出し

ある条件下で、関数を中置演算子のように呼び出すことができます。このことを中置呼び出しと言います。次の2つの条件を満たす場合、中置呼び出しができます。

  • メンバ関数(または拡張関数)である
  • 取る引数が1つである

中置呼び出しにより、高階関数を呼び出す記述をより簡潔にできます。繰り返しになりますが、それは組み込み構文であるかのような見た目になります。上記の例で登場した関数 each を中置呼び出ししてみます。

1..10 each {
  println(it)
}

クロージャ

Kotlinではクロージャを扱えます。クロージャ自体を説明するのは難しいのですが、要はローカル関数や関数リテラルが、その外側で定義されているローカル変数の値を見たり書いたりすることができるということです。

fun main(args : Array<String>) {
  var count = 0
  
  fun countUp() {
    count++
  }
  
  countUp()
  countUp()
  
  println(count) // 2
}

これはJavaではできません。メソッド内で定義した匿名クラスのメソッドからは、その外側のローカル変数の値を変更することはできません。final宣言されている変数のみ参照することはできます。

インライン関数

関数にアノテーション inline を付けると、その関数はインライン関数となり、コンパイル時に呼び出し側でインライン展開されます。高階関数は呼び出しコストが大きいため、必要に応じてインライン関数として定義することを検討すべきでしょう。ただし、現時点ではインライン関数は実装されていません。

inline fun doSomething() {
  // do something
}

関数と関数リテラル

キーワード fun により定義する関数と、関数リテラル別物です。関数リテラルを変数に代入し、引数として渡すという次のコードはうまく行きます。

fun sayHello(f : (String) -> Unit) {
  f("Hello")
}
 
fun main(args : Array<String>) {
  val writeStandardOutput = {
    (str : String) -> println(str)
  }
  
  sayHello(writeStandardOutput)
}

一方、キーワード fun によって定義された関数を使用した次のコードは残念なことにコンパイルエラーとなってしまいます。

fun sayHello(f : (String) -> Unit) {
  f("Hello")
}
 
inline fun writeStandardOutput(str : String) {
  println(str)
}
 
fun main(args : Array<String>) {
  sayHello(writeStandardOutput) // ERROR!!!
}

このことを考えると、Kotlinは関数型プログラミングを意識しつつも、非常に重視しているわけでもないことが伺えます。あくまで関数リテラルは記述量の低減や可読性の向上が目的のようです。

演算子オーバロード

2日目に少し紹介した演算子オーバロードについてです。Kotlinでは既に用意されている演算子記号に対応するシグネチャインスタンス関数(あるいは拡張関数)を定義することにより演算子オーバロードを実現しています。

class MyInt(val value : Int) {
  
  fun plus(that : MyInt) = MyInt(value + that.value)
  
  fun toString() = "$value"
}
 
fun main(args : Array<String>) {
  val five = MyInt(5)
  val seven = MyInt(7)
  
  println(five.plus(seven)) // 通常の関数呼び出し
  println(five plus seven)  // 中置呼び出し
  println(five + seven)       // 演算子
}

演算子とそれに対応する関数シグネチャの一覧は次のURIからご覧できます。

http://confluence.jetbrains.net/display/Kotlin/Operator+overloading

ジェネリック関数

Kotlinの関数はJavaと同様に型引数を宣言することにより、ジェネリック関数となり、型を柔軟に扱えます。

fun <T> head(array : Array<T>) : T = array[0]
 
fun last<T>(array : Array<T>) = array[array.size - 1]

上記の例のように関数 head のようなスタイルと last のようなスタイルがあります。単に型引数 <T> をどこに記述するかの違いに過ぎません。ジェネリクスの詳細は後日お話しします。

まとめと次回予告

今日は関数について多くのことを学びました。関数リテラルは関数をキーワード fun を用いずに直接、使用箇所に記述できる関数です。関数リテラルは変数や引数に渡すことができます。高階関数は、引数や返り値に関数リテラルを取ることができます。高階関数と関数リテラルにより、抽象度の高い関数が定義できるとともに、その呼び出しコードは簡潔で読みやすいです。その他に、中置呼び出し、クロージャ、インライン関数、演算子オーバロード、ジェネリック関数を見ました。

明日からはお待ちかね、オブジェクト指向的な機能について学んでいきます。明日はクラスについて扱います。

日記

昨日は学生時代のアルバイト仲間と忘年会をして来ました。

9日目:関数(前)

f:id:ngsw_taro:20121208142956j:plain

今日はいよいよ関数について学びます。Kotlinでは関数型プログラミング的アプローチによる記述が頻繁に登場します。そのため関数の紹介は量が多くなるので前編と後編の2部構成を採りたいと思います。

関数定義の基本

関数を定義するためには次のように記述します*1

fun 関数名 ( 引数リスト ) : 返値型 {
    関数本体
    return 返値
}

関数の定義にはキーワード fun が必要です。Javaではメソッド(ここでは関数と同義語としておきます)は必ずクラスに属するものでした。Kotlinではクラス内に関数を書くこともできますが、必ずしもその必要はありません

関数定義の具体的な例を次に示します。

fun max(a : Int, b : Int) : Int {
  return if(a > b) a else b
}

Kotlinの関数は必ず値を返します。特に関心を払うべき値を返さないような関数は、返り値としてUnit型Unit.VALUEを返します。それは、Javaのvoidに似ています。

fun greet() : Unit {
  println("Hello, world!")
  return Unit.VALUE
}

実はUnit型を返す関数の場合に限り、返値型、return、またはその両方を省略することができます。上記のコードはすっきりと次のように書けます。

fun greet() {
  println("Hello, world!")
}

単一式関数

例えば、最初に示した例の関数 max は、関数本体の中ですぐに式の評価結果を return しています。このような場合には、単一式関数という関数定義の構文糖衣があります。関数 max を単一式関数で表現すると次のようになります。

fun max(a : Int, b : Int) = if(a > b) a else b

関数シグネチャと式を等号で結んでいます。より簡潔で直感的な記述になりました。関数の返値型は型推論が働くため省略可能です。単一式関数によってプログラムを(手続き的ではなく)宣言的に書くことができます。

デフォルト引数と名前付き引数

Kotlinでは、関数の引数にデフォルトの値を設定することができます。デフォルト値が設定されている引数は、関数の呼び出し側でのパラメータ設定を省略できます。省略するとデフォルト値がパラメータとして使用されます。

fun greet(name : String = "world") {
  println("Hello, $name!")
}
 
fun main(args : Array<String>) {
  greet("Kotlin") // Hello, Kotlin!
  greet() // Hello, world!
}

ただし、次のような関数呼び出しはコンパイルエラーとなります。

fun add(a : Int = 0, b : Int) = a + b
 
fun main(args : Array<String>) {
  add(123) // ERROR!!!
}

パラメータは、引数の宣言順に指定する必要があるからです。つまり上記の123というパラメータは、引数 a に対するものであり、引数 b に対応するパラメータがないのでコンパイラは文句を言ってきます。これを解決する名前付き引数という仕組みがあります。次のように、引数名とパラメータを対応付けて関数を呼び出すことができます。

add(b = 123) // OK!

可変長引数

Kotlinにも関数が可変長の引数を取る仕組みが備わっています。可変長にしたい引数の名前の前にアノテーション vararg を付けます。すると、その引数には1つ以上のパラメータを設定できるようになります。

fun printlnAll(vararg strs : String) {
  for(any in strs) {
    println(any)
  }
}
 
fun main(args : Array<String>) {
  printlnAll("hoge", "fuga", "piyo")
}

可変長引数に、配列として一気に複数の値を渡すこともできます。その場合、次の例のように配列の前に * を記述する必要があります。

val array = array("hoge", "fuga", "piyo")
printlnAll(*array)

ローカル関数

ローカル関数、つまり関数内に関数を定義することができます。これは関数のスコープを絞りたいときに使用すると便利でしょう。

fun countUpperCase(s : String) : Int {
  fun _countUpperCase(s : List<Char>, n : Int) : Int =
    if(s.size == 0) n
    else _countUpperCase(s.tail, n + if(s.head!!.isUpperCase()) 1 else 0)
  
  return _countUpperCase(s.toCharList(), 0)
}
 
fun main(args : Array<String>) {
  val n = countUpperCase("AaaAaaA")
  println(n) // 3
}

拡張関数

拡張関数は便利で面白い機能です。既存のクラスなどに関数を追加する機能です。通常であれば、そのクラスを継承し、サブクラス内に関数を定義するでしょう。しかし拡張関数は、関数定義の場所を制限していません。つまり、クラスの外に拡張関数を定義することができます。用途としては、継承できないクラスに機能追加するなどです。

ローカル関数の例として挙げた関数 countUpperCase をStringの拡張関数として定義すると次のようになります。

fun String.countUpperCase() : Int {
  fun _countUpperCase(s : List<Char>, n : Int) : Int =
    if(s.size == 0) n
    else _countUpperCase(s.tail, n + if(s.head!!.isUpperCase()) 1 else 0)
  
  return _countUpperCase(toCharList(), 0)
}
 
fun main(args : Array<String>) {
  val n = "AaaAaaA".countUpperCase()
  println(n) // 3
}

実際には、レシーバとなるオブジェクトを第1引数として受け取る static メソッドとしてバイトコードにコンパイルされます。

再帰呼び出し

関数の再帰呼び出しJavaプログラミングでは、あまり見かけないかと思います。再帰呼び出しとは、関数が自分自身を呼び出すことです。上記のローカル関数 _countUpperCase がまさに再帰呼び出しを行っています。再帰呼び出しは、プログラムを宣言的に記述でき、また変数への破壊的代入が起こりにくいなどのメリットがあります。

練習問題解答例の改善

昨日掲載した練習問題の解答例を今日学んだ内容を活かして改善します。

FizzBuzz

fun Int.isFizz() = this % 3 == 0
fun Int.isBuzz() = this % 5 == 0
 
fun Int.fizzbuzz() = when {
      isFizz() && isBuzz() -> "fizzbuzz"
      isFizz() -> "fizz"
      isBuzz() -> "buzz"
      else -> "$this"
    }
 
fun main(args : Array<String>) {
  for(n in 1..100) {
    println(n.fizzbuzz())
  }
}

最大公約数

fun gcd(val a: Int,val b: Int) : Int = if(b == 0) a else gcd(b, a % b)

まとめと次回予告

今日は関数を紹介しました。単一式関数や再帰呼び出しを使えば関数型言語ライクにKotlinを使用できます。デフォルト引数、名前付き引数、ローカル関数など役に立つ仕組みがKotlinには導入されています。拡張関数により、既存クラスを操作する関数を可読性の高い形で提供できます。

明日は関数の紹介の続きをします。関数を抽象化するための仕組みである高階関数などについてお話しします。

日記

昨日はAndroidコミュニティの忘年会に行ってきました。

*1:必要があれば、キーワード fun の前にアノテーションを付けることも可能です。

8日目:ここまでのまとめと練習問題

f:id:ngsw_taro:20121208105508j:plain

8日目の今日は、これまでの内容の簡単な振り返りと、ちょっとした練習問題を通してKotlinともっと仲良くなりましょう。

前回までの総集編

1日目:アドベントカレンダーはっじまっるよー!

Kotlinの概要、学ぶモチベーションについてお話ししました。Kotlinを常套句の羅列で説明するなら次のようになります。

静的型付けはコンパイルタイムで型安全性を保証してくれ、また動的言語(インタプリタ言語)よりも実行速度が早い傾向があります。

オブジェクト指向関数型プログラミング、それぞれの思想が共存することにより、データと操作のカプセル化という馴染み深い方法をとりながら、関数をより抽象化しやすく、記述も簡潔になります。

Java仮想マシン上で動作するため、JVM言語の長所をそのまま引き継ぎます。つまりJavaで記述されたライブラリなどの既存の資産を活用できることや、プラットフォームに依存しない(と言われているw)、安全、高速などが挙げられます。

2日目:Kotlin名所見学ツアー

Kotlinの特徴的な機能を巡るツアーに出かけました。ここで説明するよりも実際に足を運んだ方がわかりやすいかも知れません。

3日目:Kotlinの開発環境

Kotlinの開発環境を2つ紹介しました。Webブラウザ越しに使用できるWeb Demoとマシンにインストールして使うIDEのIntelliJ IDEAです。Kotlinの学習をしたり、ちょっと試したいコードがある場合、たいていはWeb Demoで事足ります。この後取りかかる練習問題もWeb Demoで実行可能な問題です。

4日目:Kotlinで世界に挨拶をしよう

HelloWorldプログラムを通して、Kotlinプログラミングの基本的な部分をざっと眺めました。

5日目:変数と型

変数宣言の方法を学びました。Kotlinには変更可能な変数と変更不可の変数があります。それぞれキーワード var、val を付けて宣言します。変数の型は変数名の後に記述します。が、型推論に頼ることで型を明示的に記述する必要がない場合もあります。

数値型、文字型、真偽値型、文字列、Unit型と、それぞれのリテラルを学びました。Kotlinには暗黙の型変換がないこともここで紹介しました。

6日目:人生は選択の連続

if式とwhen式による条件分岐、when式の簡単なパターンマッチを取り扱いました。式の名の通り、if式とwhen式は値を返します。将来的には、より複雑なパターンマッチもできるようになりそうです。

7日目:コードは繰り返す

for と while によるループ構文を学びました。forループで繰り返しが可能なオブジェクトの特徴を説明しました(ある種のダックタイピングであることは2日目に少し触れました)。

練習問題

昨日も言ったとおり、条件分岐と繰り返しを勉強しましたし、Kotlinは手続き的に処理を記述できるので、これまで紹介した内容であらゆるアルゴリズムを記述できるはずです。関数やクラスの定義方法はまだ紹介してしないので、関数 main がひとつあるだけのコードになると思いますが、そんなことは気にしないでKotlinで遊んでみましょう!美しい方法はクリスマスまでに紹介します。

その1:九九

1 x 1 = 1, 1 x 2 = 2, 1 x 3 = 3, ..., 9 x 7 = 63, 9 x 8 = 72, 9 x 9 = 81という感じの等式を標準出力に書いていきます。フォーマットは自由ですが、等式ひとつ書くたびに改行した方が見やすいかも。

その2:FizzBuzz

1から100までの範囲でFizzBuzzをやりましょう。FizzBuzzをご存じない方はhttp://ja.wikipedia.org/wiki/Fizz_Buzzをご覧ください。

その3:最大公約数

2つの整数値をコマンドライン引数として受け取ります。その2つの整数値の最大公約数を求めて表示してください。

コマンドライン引数はStringの配列として関数 main の引数に渡されます。文字列から整数値に変換するには関数 toInt() を使用します。

ここでは入力チェック等はしなくてもかまいません。

どうしてもわからない方は「ユークリッドの互除法」でぐぐってみてくださいw

解答例

その1:九九

fun main(args : Array<String>) {
  for(a in 1..9) {
    for(b in 1..9) {
      println("$a x $b = ${a * b}")
    }
  }
}

その2:FizzBuzz

fun main(args : Array<String>) {
  for(n in 1..100) {
    val fizz = n % 3 == 0
    val buzz = n % 5 == 0
    
    val result = if(fizz && buzz) {
      "fizzbuzz"
    } else if(fizz) {
      "fizz"
    } else if(buzz) {
      "buzz"
    } else {
      "$n"
    }
    
    println(result)
  }
}

これが簡単なら発展問題として、剰余演算子 % を使用しないで解いてみましょう。類似問題として世界のナベアツ問題も面白いです。

その3:最大公約数

fun main(args : Array<String>) {
  var a = args[0].toInt()
  var b = args[1].toInt()
  
  while(true) {
    a %= b
    if(a == 0) break
      
    val w = a
    a = b
    b = w
  }
  
  println(b)
}

うーん、あまり良いコードではないですね。キーワード var による変更可能な変数があり、破壊的代入を行っているのが気になるところです。あと、whileループの条件式が常に true なのもイマイチ...。

まとめと次回予告

今日はKotlin Advent Calendar 2012(全部俺)を振り返りました。それから、簡単な練習問題を解いてKotlinへの理解を深めました。練習問題で書いたコードはあくまで基本的な文法のみを使用しており、Kotlinらしさが非常に少ないです。明日からはKotlinをより便利にする豊富な機能の説明をしていきます。まず手始めに明日は関数のお話です。

日記

コーヒー飲みながらアドベントカレンダー書く土曜の午後...。こういう時間、好きです。

7日目:コードは繰り返す

f:id:ngsw_taro:20121206230928j:plain

アドベントカレンダー7日目ー!7日目は安息日にしたいところですが、頑張ります。

今日はループ、繰り返し構文を紹介します。昨日は分岐を学びました。構造化定理によると、あらゆるアルゴリズムは順次、選択(分岐)、反復(繰り返し)の3種類の制御構文の組み合わせで表現できるらしいです。つまり、今日学び終えた頃には、Kotlinで好きなアルゴリズムを実装することができるってことです!理論的には。

whileループ

whileループから説明するJavaやCの入門書って少ない印象を受けますが、どうでしょうか。KotlinにおけるwhileループはJavaのそれと非常に似ています。

var count = 0
while(count < 10) {
  println(count++)
}

Javaのwhileとの違いは、変数のスコープです。do-whileループもKotlinにはありますが、doブロック内で宣言された変数をwhileの条件式から見ることができます(残念ながらコンパイルのバグか、この機能を使うと実行時にクラッシュします)。

do {
  val a = Math.random()
} while(a < 0.5)

これは非常にありがたいです。という理由は2つあります。まず、変数のスコープを最小限にできる、ということです。もう1つの理由は、var ではなく val を使用できるからです。もし、doブロック内の変数のスコープがJavaと同じだとしたら、上記のコードは次のように書く必要があります。

var a : Double
do {
  a = Math.random()
} while(a < 0.5)

forループ

次にforループを紹介します。2日目のエントリで少し触れましたが、KotlinのforループはJavaの拡張for文のような形でしか機能しません。コレクションとその要素を受け取る変数の間にはキーワード in をはさみます。

fun main(args : Array<String>) {
  for(arg in args) {
    println(arg)
  }
}

「コレクション」という言葉を使いましたが、イテレータを提供するオブジェクトならなんでも繰り返せます。具体的には関数 iterator を持つオブジェクトです。関数 iterator は次のようなオブジェクトを返す必要があります。

  • 関数 next を持っている
  • Boolean型を返す関数 hasNext を持っている

もちろん、java.lang.Iterableやjava.lang.Iteratorを実装しても構いませんが、必須ではありません。それから、ここで言う関数はインスタンス関数でも拡張関数でもどっちでもいいのです。実装の話や関数については後日したいと思います。

レンジで繰り返し

Kotlinにはレンジという範囲を表すオブジェクトがあります。具体的にはクラス IntRangeやDoubleRangeによって表現されています。特に整数値のレンジ(IntRange, LongRange)は、forループで繰り返すことができます。

肝心のレンジオブジェクトの生成方法ですが、開始の値と終了の値を演算子 .. で繋ぎます*1

for(i in 1..10) {
  println(i)
}

このコードをコンパイル・実行すると標準出力に1から10までの整数を1つずつ出力していきます。

breakとcontinue

KotlinでもJavaと同じように、ループ内でbreakcontinueができます。ラベル付きbreak, continueもサポートしています。ラベル名は@か@@、または@から始まる名前にする必要があります。

@hoge
for(i in 1..10) {
  for(j in 1..10) {
    if(j == 5) break @hoge
    print(j) // 1, 2, 3, 4と順に表示される
  }
}

まとめと次回予告

今日は繰り返し構文について学びました。whileループはJavaとほとんど同じですが、do-whileでは変数スコープがJavaと異なります。forループはJavaの拡張for文に似ています。イテレータを提供するオブジェクトであれば何でもforで繰り返せます。java.lang.Iterableやjava.lang.Iteratorを実装する必要はありません。レンジという範囲を表すオブジェクトがあることを学びました。

冒頭にも述べましたが、今日までの内容のKotlin文法で、あらゆるアルゴリズムが書けるはずです。明日はちょっとしたアルゴリズムの練習問題をやります。

日記

昨日はJavaScriptとそのユニットテストツールの勉強会に行ってきました。あ、今朝地震ありましたね。

*1:関数 rangeTo と対応しています。演算子オーバロードの詳細については後日。

6日目:人生は選択の連続

f:id:ngsw_taro:20121205002931j:plain

アドベントカレンダー6日目の今日は、条件分岐式についてお話ししたいと思います。条件分岐文でないことに注意してください。式なのです。何か値を返す、これも関数型プログラミング的アプローチのひとつです。

if式

まず1つ目に紹介するのは馴染みの深いキーワード ifelse による条件分岐式です。前述の通り、Kotlinの if と else は値を返します。そのためそれらはちょうどJavaの ? と : に似ています*1

val str = if(n % 2 == 0) {
    "even"
} else {
    "odd"
}

上記の例の場合、if と else ブロック内の式がたった1つなので波括弧を省略できます。else がなく if のみの場合や、1つでもUnitを返す分岐が存在する場合、その if式全体の結果がUnitとなります。

when式

Kotlinにはwhen式というJavaのswitch文と似た機能がありますが、それよりも強力な仕組みです。

val str = when(number) {
  1 -> "1st"
  2 -> "2nd"
  3 -> "3rd"
  4, 5, 6, 7, 8, 9 -> "${number}th"
  in 10..20 -> "10th+"
  else -> "unknown"
}

when式はswitch文のように、引数の値が分岐条件に合致するまで上から順に比較していきます。分岐条件が満たされると -> の右側の式を返します。switch文のような break はありません。もし次の分岐に飛びたい場合は continue を記述します*2

上記の例において、number が 1 の時、when式は "1st" を返します。number が 4から9までのいずれかである場合は、その数の後に "th" がくっついた文字列が返されます。

in 10..20のような書き方もできます。この意味は「 number が10以上20以下である場合」です。..レンジというオブジェクトの生成演算子です。詳細は後日紹介します。

どの分岐条件にも当てはまらなかった場合には else の先の式が返されます。when式には else が必須であることに注意してください。

when式は次のように記述することでif else ifの長ったらしい分岐を簡潔に表現することができます。

when {
  fizz(a) && buzz(a) -> "fizzbuzz"
  fizz(a) -> "fizz"
  buzz(a) -> "buzz"
  else -> "$a"
}

ここでの関数 fizz と buzz の返り値の型は Boolean です。例えば fizz(a) のみが true を返す場合、このwhen式は "fizz" を返します。

when式は次のような簡単なパターンマッチングをサポートしています。

when(obj) {
  is Int -> "整数値"
  is Double -> "浮動小数値"
  else -> "その他"
}

この例は、obj の型をチェックして、対応する文字列を返すwhen式となっています。将来的には、より複雑なパターンマッチができるようになるらしいです。

まとめと次回予告

今日は条件分岐ということで、if式とwhen式を学びました。「式」なので、両者は値を返します。when式はJavaのswitch文と似ていますが、レンジとの比較や簡単なパターンマッチングなどにも対応しており強力です。

明日はループについてお話しします。

 

*1:三項演算子や条件演算子と呼ばれています。

*2:現時点において、when式での continue は実装されていません。

5日目:変数と型

f:id:ngsw_taro:20121204062934j:plain

アドベントカレンダー5日目の今日はKotlinにおける変数と型について紹介します。型については少し触れる程度にします。

変数

Kotlinで変数を宣言するにはキーワードを伴う必要があります。

var x : Int
これはJavaでは次に相当します。 
// Java
int x;

キーワード var で変数を宣言しており、続けて変数名、コロンを指定します。変数の宣言と同時に初期化することもできます。

var x : Int =

型推論

昨日も少し触れましたが、Kotlinには型推論という機能があります*1。型推論とは文字通り型の推論です。つまり、プログラマが型を明記しなくてもコンパイラが文脈から推論して適切な型として扱ってくれるということです。次の変数宣言・初期化は型推論を利用して簡潔に記述しています。

var x = 

これは素晴らしいことです。1つ前のバージョンから冗長性を排除しました。整数リテラルはInt型なので、それを代入しようとしている変数の型がInt型なのは明記するまでもないのです。型推論は、静的言語の動的言語に対する重要な短所のひとつである型記述の煩わしさを解消する機能です。

変更不可の変数

もう1つ変数宣言のキーワードがあります。val です。次のコードは val を使って不変の変数を宣言・初期化しています。

val x = 0

これは次のJavaコードに相当します。

// Java
final int x = 10;

変数を変更不可にするのは、関数型プログラミング的なアプローチです*2。値が不変であることはいいことです。コードの読み手が、その変数が不変であることによって、他の箇所で値が書き変わることを心配する必要がなく安心してコードを読めるからです。また変数やオブジェクトの内部状態が不変であることによって、関数の返す値を予測しやすくなります。これを参照透過性と言ったりします。オブジェクトの内部状態が変更可能であると、それに属する関数の返す値が内部状態の変更に伴って変わるおそれがあり、予測が困難です。

基本型

Kotlinではすべてオブジェクトです。そのため基本型という呼ぶ方は正確ではないような気がしますが、Javaの基本型(プリミティブ型)に対応する型として聞いていただければと思います。

数値型

Kotlinの基本の数値型はJavaの数値型の頭文字を大文字にしたものと同じです。要するに、ビット幅の大きいものから整数型はLong、Int、Short、Byte、浮動小数型はDouble、Floatです。

注意していただきたいのは、Kotlinには暗黙の型変換がありません。これはJavaよりも慎重な設計です。暗黙な型変換は時にプログラマに複雑なルールを強いることになるからでしょう。

val a = 5
val b : Long = a // ERROR
val c : Long = a.toLong() // OK
val d : Long = a as Long // OK

いわゆる四則演算はいつも通り、+ - * / といった記号が使用できますが、論理演算/ビット演算の記号は用意されていません。これらは関数呼び出しによる演算を行う必要があります。

関数名意味
shl 左シフト
shr 符号付き右シフト
ushr 符号なし右シフト
and 論理積
or 論理和
xor 排他的論理和
inv ビット反転

使用例を示します。

val a = 0b0110 // 0bを先頭に置くと2進数リテラルとなる
val b = 0b1100
println(a and b == 0b0100) // true

上記の例では、関数 and を演算子のように使用しています。つまりレシーバと関数の間にドットがなかったり、引数を括弧で囲まなかったりです。このような関数呼び出しを中置呼び出しと言いますが、詳細は後日お話しします。

文字

文字はChar型によって表現されます。文字リテラルJavaと同様にシングルクォートで囲みます。文字は数値として直接扱うことはできません。明示的な型変換により、Int型の数値に変換することができます。

真偽値

真偽値はBoolean型によって表現されます。取り得る値は true と false です。組み込み演算子として || と && が提供されています(Javaと同様の意味)。

文字列

文字列はクラス String によって表現されます。文字列は不変オブジェクトで、その構成要素は文字です。 Kotlinには2種類の文字列リテラルがあります。1つはJavaと同様の形であるエスケープ文字列("Hello\n"など)です。もう1つは原型文字列です。これは名前の通り、エスケープ文字や改行をそのままの形で解釈します。原型文字列を定義するには、トリプルクォート(""")で文字列を括ります。

文字列に、テンプレート式を含むことができます。テンプレート式とは、式が評価された結果を文字列内に組み込む仕組みです。$変数名もしくは${ 式 }と表現します。例を次に示します。 

println("$str") // hello
println("${str.length}") // 5

Unit型

特に興味深い値を持たないという意味では、Javaの void と似ていますが、型 Unitは void とは異なり値を1つだけ取り得ます。その値は Unit.VALUE です*3。一見、何も返さないような関数でも返り値の型はUnitで Unit.VALUE をきちんと返しています。

まとめと次回予告

今日は変数の宣言・初期化の方法と型推論、そして基本型とそれにまつわる関数や演算子を学びました。Kotlinでは基本的に変数宣言の箇所では型を省略します。また、特別な理由がない限りは不変な変数としてキーワード val を使用して宣言したほうがいいでしょう。Kotlinには暗黙の型変換がありません。明示的に型変換します。一見煩わしいですが、複雑な型変換ルールからプログラマは解放されます。文字列リテラルとしてテンプレート式という便利な機能を見ました。

明日は条件分岐について紹介します。お楽しみに!

日記

昨夜は会社の同期たちと飲みました。久しぶりの再会に盛り上がりました。

*1:Javaの現行バージョンにも型推論機能はありますが、Kotlinのそれの方が強力です。

*2:通常のJavaプログラミングでも大切なことには変わりないです。

*3:古いバージョンのKotlinでは() でした。これは要素が0個のタプルを表しています。現バージョンではタプルが廃止されているのでUnitのクラス定数となったのでしょう。