記事・メモ一覧

default eye-catch image.

kotlin基本構文-クラス,プロパティ,アクセス修飾子,コンストラクタ

言語仕様に拘りがあるタイプではなさそうで、かなり平易。 クラスの定義 public,private修飾子を指定したメソッドを持つクラスを定義してみる。 インスタンス化はnewが要らないんだな。 class Person() { public fun sayHello() { println(\"hello,kotlin\") sayYes() } private fun sayYes() { println(\"yes\") } } val person = Person() person.sayHello() // hello,kotlin yes 気になるのは、アクセス修飾子が無いときの振る舞い。 デフォルトのアクセス修飾子はprivateではなくpublic。 Javaのデフォルトのアクセス修飾子は\"package内のみ\"だった。 class Person() { fun hogehoge() { println(\"hogehoge\") } } val person = Person() person.hogehoge() メンバ変数とプロパティ Delphiで便利だったプロパティ。getter,setterを文法で解決する。 まずメンバ変数は以下の通り普通に定義できる。 これは後述するgetter,setterを省略した書き方。 class Person2() { var name = \"Ann\" fun show() { println(\"My name is ${name}.\") } } val person2 = Person2() person2.show() ageというメンバ変数に対してsetterを定義する。 外部からageというメンバ変数に値を代入する際に定義したsetterが呼ばれる。 異常値の入力値チェックが出来たりする。 getter,setterを文法で解決したメンバ変数をプロパティと言ってるらしい。 getter,setterでメンバ変数を参照する際、selfを使わずfieldという予約語を使う様子。 backing fieldという名前が付いてる。 class Person2() { var name = \"Ann\" var age = 10 set (value) { if (value < 0) { println("指定した値は不正です。") } else { field = value } } fun show() { println("My name is ${name}. My age is ${age}") } } val person2 = Person2() person2.show() // My name is Ann. My age is 10 person2.age = 20 person2.show() // My name is Ann. My age is 20 person2.age = -10 // 指定した値は不正です。 person2.show() // My name is Ann. My age is 20 なお、プロパティを変数(var)ではなく定数(val)で定義すると、読み取り専用プロパティになる。 class Person3() { val age = 20 } var person3 = Person3() person3.age = 100 // Error:(39, 5) Kotlin: Val cannot be reassigned プロパティの初期値を設定せず、コンストラクタでも代入しなかった場合はインスタンス化時にエラーとなる。 abstractクラスならOK。 class Person4() { val age public fun say() { println(\"${age}\") } } var person4 = Person4() person4.say() //Error:(42, 9) Kotlin: Property must be initialized or be abstract プロパティのsetterにアクセス修飾子を指定できる。 public修飾子がついたプロパティのsetterにprivate修飾子をつけると、 クラス内部からはsetter経由で値を変更できるが、外部からは変更できなくなる。 class Person5() { var name = \"Ann\" var age = 10 private set (value) { if (value < 0) { println("指定した値は不正です。") } else { field = value } } fun show() { println("My name is ${name}. My age is ${age}") } } val person5 = Person5() person5.show() // My name is Ann. My age is 10 person5.age = 20 // Error:(34, 5) Kotlin: Cannot assign to 'age': the setter is private in 'Person5' プライマリコンストラクタ コンストラクタはinit予約語の後に書く。コンストラクタのパラメタはinitに書かずclassに書く。 コンストラクタのパラメタがclassに付いているのでinit以外でも使ってしまいそうになるがinitでしか使えない。 コンストラクタに渡した文字列を使ってメンバ変数を初期化してみる。 class Person(name: String) { var name: String init { this.name = name println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() この書き方は、constructor予約語が省略されたものであって、 constructor予約語を省略しない場合は以下のようになる。 class Person constructor(name: String) { var name: String init { this.name = name println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() コンストラクタの実行とメンバ変数の初期化を一度に書くのは以下。 class Person(var name: String) { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() アクセス修飾子までコンストラクタに含められる。 class Person(private var name: String) { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() さらにデフォルト値を含められる。 class Person(private var name: String = \"Tom\") { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person() person.sayHello() セカンダリコンストラクタ 2つ以上のコンストラクタを書くときのやり方。 2つ目以降は順当にconstructor予約語を書いていく。 ほとんどの場合コンストラクタは1個だから1つ目のコンストラクタを簡易な方法でかけるようになっているのかな。 :thisというのは、プライマリコンストラクタを呼び出す書式。 ブロックの先頭に書くのではなく専用の書式がある。 セカンダリコンストラクタはプライマリコンストラクタを必ず呼ばなければならない。 class Person(var name: String = \"Tom\") { init { println(\"My name is ${name}\") } constructor(): this(\"Alice\") {} public fun sayHello() { println(\"say hello\") } } val person = Person() person.sayHello()

default eye-catch image.

kotlin基本構文-関数,引数のデフォルト値,引数の名前付き呼び出し,可変長引数,複数の戻り値,高階関数,ラムダ式

参考にした入門書の内容が薄すぎてトレースする意味がなくなってきた。 いきなり中級書で良かった気がする。 関数 kotlinにおける関数の定義方法。 これもDelphiっぽいけどDelphiの特殊な部分を普通に寄せた感じ。 今思えばDelphiの\"関数内の最終評価値が関数の戻り値になる\"動きはRubyに通じてるような。 関数の呼び出しに使ったスタックをどう解釈するかの違いだから似たパターンになるんだろう。 # kotlin fun getSquare(value: Int): Int { return value * value } println(getSquare(10)) // 100 # Delphi function getSquare(value: Integer): Integer begin result:= value * value end writeln(getSquare(10)); // 100 関数の定義において、引数の型を省略することはできない。 以下はN.G. fun getSquare(value): Int { return value * value } 戻り値が無い関数を定義する際は、戻り値の型をUnit型にする。 なお、関数の戻り値のUnit型のみ省略可能。 fun sayHello(msg: String): Unit { println(msg) } fun sayHello(msg: String) { println(msg) } よくある省略形として、関数本体が1行しかない場合はブラケットを省略できるけど、 kotlinも省略できる。ただし書き方が少し違う。 コンパイラがワンライナーの型を推測できる場合は、関数の戻り値の型自体を省略できる。 // 戻り値の型推論なし fun getSquare(value: Int): Int = value * value // 戻り値の型推論あり fun getSquare(value: Int) = value * value 引数のデフォルト値 よくある引数のデフォルト値定義。普通。 fun getSquare(value: Int = 10): Int { return value * value } println(getSquare()) // 100 デフォルト値ありの引数と無しの引数を混在させることもできる。 必須引数は引数リストの先頭に、省略可能引数は引数リストの後方に置くこと。 fun getMult(value1: Int, value2: Int = 10): Int { return value1 * value2 } println(getSqure(100)) // 1000 引数のデフォルト値にはリテラルだけでなく関数の戻り値を指定することができる。 これは新感覚。 fun getDefaultValue(): Int { return 15 } fun getMult(value1: Int, value2: Int = getDefaultValue()): Int { return value1 * value2 } println(getMult(100)) //1500 引数の名前付き呼び出し 関数の呼び出し時に引数の名前を指定することができる。 ほんと、どっかで見た最近の言語のつまみ食い。 引数の順序に縛られない、引数の順序によらずどの引数も省略できる。 fun getMult(value1: Int, value2: Int = 10): Int { return value1 * value2 } println(getMult(value1=100, value2=200)) // 20000 kotlinはJavaのコードを呼び出せる(はず)だけれども、 Javaのバイトコードは引数の名前を保持していないため、 Javaのメソッドを名前付き引数を使って呼び出すことはできない。 可変長引数 だいたいどの言語にもある可変長引数。 使ったこと無いけど。 他の変数と混在させる場合は、可変長引数を末尾に配置すること。 可変長引数を前に持ってくる場合、呼び出し時にどこまでが可変長引数かわからなくなるので、 可変長引数の後の引数を名前付きで渡す必要がある。(そこまでして順番に拘る必要はないと...) fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } println(getValues(1,2,3,4,5)) // 120 可変長引数として配列を渡すことができる。 その際、配列の先頭にスプレット演算子(*)を置く。 ポインタ、参照とは関係なく、 単に配列を可変長引数に展開することから\"spread\"という名前になっている。 fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } var ary: intArrayOf(1,2,3,4,5) println(getValues(*ary)) //120 スプレッド演算子を使うと、リテラルと配列から1つの可変長引数を作れる。 こんなことしないと思うんだけど。 fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } var ary: intArrayOf(1,2,3,4,5) println(getValues(10,*ary,10)) //12000 複数の戻り値 これは鮮やか。関数が複数の戻り値を返せる。 鮮やかだけども型として常用してはいけない様子。関数の戻り値を受け取る時ぐらいで留めるべき。 以下は、可変長引数で渡した値の算術平均を返す関数。 fun getAvarange(vararg values:Int): Pair { var result = 0 var count = 0.0 for (value in values) { result += value count++ } return Pair(result, result/count) } println(getAvarange(1,2,3,4,5)) // (15,3.0) Pairをそれぞれの変数にバラすのは以下の通り。 val pairs:Pair = Pair(10,5.0) val (intValue, doubleValue) = pairs println(intValue) // 10 println(doubleValue) // 5.0 高階関数 関数の参照を渡すには以下のようにする。 Int型の引数を出力する関数printを、配列のforEachに渡している。 配列のforEachの繰り返しの度にprint関数が評価される。 その際、引数itemとして配列の各要素が渡される。 fun print(item: Int) { println(item) } var ary = arrayOf(1,2,3,4) ary.forEach(::ary) // 1 2 3 4 もちろん、関数をラムダ式に置き換えることができる。 ブラケットで囲まれた部分がラムダ式。 arrow(->)の左辺がラムダ式のパラメタ、右辺が関数の本体。 var ary = arrayOf(1,2,3,4) ary.forEach({ num:Int->println(num) }) ラムダ式のパラメタの型はコンパイラの型推論で自動的にわかるのなら省略できる。 var ary = arrayOf(1,2,3,4) ary.forEach({ num->println(num) }) ラムダ式を与える引数リストにおいて、ラムダ式が最後である場合、 引数リストからラムダ式を除いて以下のようにかける。 var ary = arrayOf(1,2,3,4) ary.forEach(){ num->println(num) } さらに、ラムダ式を与える引数リストにおいて、引数がラムダ式しかない場合、 引数リストの空プラケットさえ省略できる。 var ary = arrayOf(1,2,3,4) ary.forEach { num->println(num) } そして、ラムダ式に与える引数が一つしかない場合、暗黙の引数itを宣言無しで使える。 var ary = arrayOf(1,2,3,4) ary.forEach { println(it) } ラムダ式と匿名関数 あまりラムダ式と匿名関数の違いについて考えたことがなかったのだけども、 ラムダ式の代わりに匿名関数を渡すことができる。同じ意味になる。 ラムダ式は匿名関数の糖衣構文(Syntax sugar)である。 つまり冗長な匿名関数を簡易な書き方で書き直したのがラムダ式である。 下記の通り、匿名関数は冗長。 var ary = arrayOf(1,2,3,4) ary.forEach (fun(num: Int):Unit { println(num) }) ラムダ式内でのreturn Rubyで一つの話題になるラムダ式内でのreturnについて。 kotolinにおいてラムダ式内でreturnすると、該当のラムダ式ではなく直上の関数を抜ける意味になる。 下記はラムダ式を中断するだけでなくary.forEachを包含する関数も中断する。 従って、末尾のprintln(\"OK\")は処理されず、OKは出力されない。 var ary = arrayOf(1,2,3,4) ary.forEach { if (it>3) return println(it) // 1 2 3 } println(\"OK\") ラムダ式のみを中断するにはラベル構文を使用する。 下記のようにloopラベルを書いておくと、ラムダが中断したとき中断の連鎖はloopラベルで終了する。 これは自棄っぱちな感じがするな。Rubyと同等で良いんじゃなかろうか。 var ary = arrayOf(1,2,3,4) ary.forEach loop@ { if (it>3) return println(it) // 1 2 3 } println(\"OK\") // OK 高階関数サンプル 高階関数のサンプル。ある時間がかかる処理を関数として実装し、 高階関数にその関数を渡す。 高階関数側では、その関数の実行前後の時間を計測し経過時間を出力する。 fun calc(action: ()->Unit): Long { var start = System.currentTimeMillis() action() var end = System.currentTimeMillis() return end - start } val time = calc { var x = 1 for (i in 1..10_000_000) { x++ } } println(\"処理時間:${time}\") ローカル関数 関数内で関数を定義することでスコープを限定できる。 内側の関数からは外側の関数のスコープにある変数を参照できる。 fun outerFunction() { val constant = 100 fun innerFunction() { var variable = constant * constant println(variable) } innerFunction() } outerFunction() / 10000

default eye-catch image.

kotlin 基本構文 – 型,リテラル,null許容型,型変換,配列,コレクション,定数

アウトプットによる記憶の定着の意味で超入門書をトレースして書き起こす。 超入門が終わったら中級書に着手。 変数と型宣言 変数は宣言して使う。構文がまんまDelphiみたい。 kotlinは行末セミコロン不要。 こんなところで主張しなくても良いのにと思う。 # kotlin var name: String = \"default value\" println(name) // default value # Delphi var name: String = \"default value\"; writeln(name); // default value 型は以下の通り。Javaみたいに全てはクラスです!とか言わない。 プリミティブはプリミティブ。 データ型概要 Boolean真偽 Byte8bit整数 Short16bit整数 Int32bit整数 Long64bit整数 Float32bit浮動小数点 Double64bit浮動小数点 Char文字 String文字列 静的型付けなので定義した型以外の値を代入できない。 下記はIntelliJ IDEA上でコンパイル前にエラーであることがわかる。 var num: Int num = 100 num = \"hoge\" // Error:(5, 11) Kotlin: Type mismatch: inferred type is String but Int was expected 型推論の挙動は以下の通り。最初に代入した値の型が変数の型となり、 2回目以降の代入で異なる型を代入できない。 型推論のミスマッチはコンパイルしたときにわかる。 var num = 108 num = 100 num = \"hoge\" // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized 変数宣言時に初期値を省略することができるが、型と初期値を両方省略することはできない。 これは変数宣言時に型推論の情報を決めるという仕組みでそうなっている。 var num num = 100 // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized どんな値も入る魔法の型が存在する。Any型 var name: Any name = 100 name = \"hoge\" 数値リテラル 10進数、16進数、2進数、指数。普通。他の言語と同じ。 var name: Int name = 105 name = 0xFF name = 0b11 name = 1.234e+5 Rubyにもあったけども、桁表現ができる。 絶対使わないだろうけど、知ってたら知ってたで無用に使ってみたくなる。 var name: Int name = 105_200_300 println(name) // 105200300 型サフィックス 数値リテラルのデフォルト型はInt、浮動小数点数リテラルのデフォルト型はDouble。 リテラルに型サフィックスを付けることで、型を変更できる。 型サフィックスはLong変数のL、Float型のFのみ。 絶対使わなそうなパターンが用意されていないところの割り切りが、妥当な感じがする。 var intval = 300 println(intval::class) // class kotlin.Int var intval2 = 300L println(intval2:class) // class kotlin.Double var fval fval = 3.14 println(fval::class) // class kotlin.Double fval2 = 3.14F println(fval2::class) // class kotlin.Float 文字列リテラル 文字列リテラルはダブルクォート1つで括るタイプと、ダブルクォート3つで括るタイプの2つ。 前者はPHP,Rubyの流れではなく、あくまでJava。後者はPHPのヒアドキュメント相当。 var msg = \"\"\" コトリンコトリン。コトリンコトリン。 複数行にわたって文字列をかける。 コトリンコトリン。コトリンコトリン。 \"\"\" println(msg) // コトリンコトリン。コトリンコトリン。 // 複数行にわたって文字列をかける。 // コトリンコトリン。コトリンコトリン。 PHPでいつも気持ちが悪い、ヒアドキュメント内のインデントの空白問題。 バー(|)を書き.trimMargin()を呼ぶことで、コード上のインデントを有効として残したまま変数内のインデントを無視できる。 var msg = \"\"\" |コトリンコトリン。コトリンコトリン。 |複数行にわたって文字列をかける。 |コトリンコトリン。コトリンコトリン。 \"\"\".trimMargin() println(msg) //コトリンコトリン。コトリンコトリン。 //複数行にわたって文字列をかける。 //コトリンコトリン。コトリンコトリン。 変数展開はPHPと同じように$を使う。 var value = 100 var msg2 = \"This is value ${value}\" var msg3 = \"This is value $value\" println(msg2) // This is value 100 println(msg3) // This is value 100 PHPの場合、変数の後に文字列を続けても変数分を自動認識してくれたが、kotlinはしてくれない。 変数の後には空白を書くか${}で囲む必要がある。 var value = 100 var msg4 = \"This is value $valuehoge\" // Error:(13, 32) Kotlin: Unresolved reference: valuehoges null許容型 kotlinはデフォルトでnullを不許可。 変数宣言のときに型の末尾に\"?\"を付けることでnullを許容できるようにできる。 var str:String = \"hogehoge\" str = null // Error:(18, 11) Kotlin: Null can not be a value of a non-null type String var str2:String? = \"hogehoge\" str2 = null println(str2) // null null許容型の変数をnull不許可の変数に代入することはできない。 代入される側をAny型にしたとしてもN.G.。 var str:String = \"hogehoge1\" var str2:String? = \"fugafuga\" str = str2 // Error:(19, 11) Kotlin: Type mismatch: inferred type is String? but String was expected var stra:Any = str2 // Error:(19, 20) Kotlin: Type mismatch: inferred type is String? but Any was expected ぬるぽ防止構文 (Safe access operator) Nullableな変数がnullだった場合は、その変数にアクセスするとぬるぽが起こる。 ぬるぽを徹底して排除するために、メンバにアクセスする際にセーフコール演算子を使う。 変数がnullであった場合、セーフコール演算子はnullを返し、nullでない場合に限りメンバを返す。 var str: String = \"hogehoge\" println(str.length) // 8 var str: String? = null println(str.length) // Error:(19, 16) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? var str2: String? = null println(str2?.length) // null 普通にnullable型とnullを比較することが出来るので、if文でぬるぽ回避することもできる。 var str:String? = null if (str != null) println(str.length) 左辺がnullであった場合に限り指定した右辺を返す演算子(?:)も積極的に使える。 var str:String? = null println(str?.length ?:0) // 0 implicitな型変換 kotlinは基本的に暗黙の型変換が効かない。 Javaでは拡大方向の暗黙的型変換が許容されているがkotlinでは不可。 以下、Int型の変数をLomg型の変数に代入しようとしているがN.G.。 var x:Int = 100 var y:Long = x // Error:(4, 18) Kotlin: Type mismatch: inferred type is Int but Long was expected 変数宣言時の初期化の際にも暗黙の型変換が効かないので、 型サフィックスを付けて、変数の型と初期値を一致させる必要がある。 var m:Float = 1.5 // Error:(3, 19) Kotlin: The floating-point literal does not conform to the expected type Float var m:Float = 1.5f println(m) // 1.5 基本的に、というのは、整数リテラルについては暗黙の型変換が行われる。割り切り。 var i:Long = 105 var j:Short = 40 var k:Byte = 10 explicitな型変換 基本的にimplicitな型変換に頼らないで、explicitに型変換する方が良さそう。 var i = 10 var j:Long = i.toLong() 配列型 配列型はC++のジェネリックみたいに書く。 getter/setter,iterator,accessorが準備されていて使いやすい。 nullableも指定できる。 var ary:Array = arrayOf(\"1\",\"2\",\"3\") var ary2:Array = arrayOf(\"1\",\"2\",null) intArrayOf()はArrayを返すので、型推論のヒントとして与えることができる。 var ary3 = intArrayOf(1,2,3) Ruby風に、配列生成の初期値としてラムダ式を与えることができる。 Arrayの第1引数は配列のサイズ、第2引数はラムダ式。 第2引数のラムダ式の中に現れる変数iはラムダに渡される変数であってiでなくても何でも良い。 ラムダと高階関数は後で出てくるのでそこで。 var data = Array(5, {i -> i*3 }) data.forEach{ println(it) } // 0 3 6 9 12 配列のアクセサは[...]演算子を使う。 範囲外アクセスは実行時例外。 var data = Array(5, {i -> i*3 }) println(data[0]) // 0 println(data[1]) // 3 println(data[10]) // Exception in thread \"main\" java.lang.ArrayIndexOutOfBoundsException: 10 コレクション arrayの他に、list,set,mapなどがある。 var list:List = listOf(\"hoge\",\"fuga\") println(list) // [hoge, fuga] var list2 = listOf(\"hoge\",\"fuga\") println(list2) // [hoge, fuga] var set:Set = setOf(\"A\",\"B\",\"C\") println(set) // [A, B, C] var set2 = setOf(\"A\",\"B\",\"C\") println(set2) // [A, B, C] var map = mapOf(\"first\" to 1, \"second\" to 2, \"third\" to 3) println(map) // {first=1, second=2, third=3} 定数 kotlinの定数はval予約語により定義する。 定数への再代入は不可。コンパイル前にIntteliJ IDEAでエラーがわかる。 val constant = \"THIS IS CONSTANT VALUE\" println(val) // THIS IS CONSTANT VALUE val = \"RE SET VALUE\" // Error:(18, 5) Kotlin: Unresolved reference: constanct 要はfinalなので、変数自体は変更できないが、変数の中身は変更できる。 例えば、配列を定数として定義したとき、配列自体を変更できないが、中身を変更できる。 val ary:array = arrayOf(1,2,3) ary = arrayOf(10,20,30) // Error:(19, 5) Kotlin: Val cannot be reassigned ary[0] = 100 println(ary[0]) // 10

default eye-catch image.

kotlin hello world anyway.

XCodeが10になって色々言われていてビビってあまり本質的でないところに引っかかりそうだったのと、 そもそもXCode10が全然落ちてこないので、先にkotlin@IntelliJ IDEA@Macを試した。 しばらくkotlinの言語仕様の勉強をしてみることにする(Androidアプリにしないで)。 kotlin overview JVM上で実行される静的型付けのオブジェクト志向言語。 本家の解説を1行ずつ噛み締めながら読んでいく。 引用文に意訳を付けていくセルフ輪講形式。 Kotlin is a great fit for developing Android applications, bringing all of the advantages of a modern language to the Android platform without introducing any new restrictions: kotlinはAndroidアプリケーション開発に適した言語であって、モダンな言語が持つ利点を導入によって新しい制約を持ち出すことなしにAndroidプラットフォームにもたらす。 Compatibility: Kotlin is fully compatible with JDK 6, ensuring that Kotlin applications can run on older Android devices with no issues. The Kotlin tooling is fully supported in Android Studio and compatible with the Android build system. 互換性。kotlinはJDK6と完全な互換性がある。kotlinアプリケーションが古いAndroidデバイスで動作することを保証する。 Performance: A Kotlin application runs as fast as an equivalent Java one, thanks to very similar bytecode structure. With Kotlin\'s support for inline functions, code using lambdas often runs even faster than the same code written in Java. パフォーマンス。kotlinアプリケーションはJavaアプリケーションと非常に似たバイトコード構造となることで、Javaアプリケーションと同等の速度で動作する。 Interoperability: Kotlin is 100% interoperable with Java, allowing to use all existing Android libraries in a Kotlin application. This includes annotation processing, so databinding and Dagger work too. 相互互換性。kotlinはJavaと100%相互互換性がある。従って、全ての既存のAndroidライブラリはkotlinアプリケーションで動作する。この振る舞いには追加処理が含まれ、データバインディングやデバッガもまた動作する。 Footprint: Kotlin has a very compact runtime library, which can be further reduced through the use of ProGuard. In a real application, the Kotlin runtime adds only a few hundred methods and less than 100K to the size of the .apk file. フットプリント。kotlinは非常に小さな実行時ライブラリである。ProGuard使用時によりも小さくなる。また、kotlinランタイムは、たかだか2,300の新しいメソッドが加わるだけであり、.apkのサイズは100KB未満となる。 Compilation Time: Kotlin supports efficient incremental compilation, so while there\'s some additional overhead for clean builds, incremental builds are usually as fast or faster than with Java. コンパイル時間。kotlinは効率的なインクリメンタルコンパイルをサポートしている。従って、(ベースとなる)Javaのクリーンビルドと比較してオーバーヘッドがあるにも関わらず、インクリメンタルコンパイラによるコンパイル時間はJavaのそれと比べて同じかより速い。 Learning Curve: For a Java developer, getting started with Kotlin is very easy. The automated Java to Kotlin converter included in the Kotlin plugin helps with the first steps. Kotlin Koans offer a guide through the key features of the language with a series of interactive exercises. 学習曲線。Java開発者にとって、kotlinを始めるのは非常に簡単である。まずは、kotlinプラグインに含まれるJava->kotlin自動コンバータの恩恵を受ける。 anyway 参考にしたのは以下。 正味1時間くらいで環境構築できた。 Kotlin入門 IntelliJ IDEAで開発環境の構築とHello World 他にはAndroid Studioがないといけないんだろう...。 kotlinの文法をチェックするだけなら、 kotlin playgroundで出来る。 とりあえずhello,world.出してみる。 Scalaみたく.classが出力される。 コンパイル速度は遅くはないけど瞬殺って訳でもない。 2013LateのMBPでhello worldが2秒くらい。 10月29日にkotlin1.3がリリースされてる。 .classを経由しないでいきなりNativeになるとか。 今のところ聞かなかったことに...。 kotlinの名前の由来はコトリン島。 サンクトペテルブルグに近いバルト海に浮かぶ島だそう。 JetBrains社のサンクトペテルブルグにある研究所が作った言語なんだそうで。 とりあえず、とりあえず、でわからないままAndroidアプリにしても、 何も意味がないので、kotlinの言語仕様から。 結構続く。

default eye-catch image.

標本平均の母平均の推定

[mathjax] zozoスーツではないけれども、標本が正規分布に従うというのは、 真実の母平均に対して正規分布に従う計測誤差を含む分布を観測しているのと同じ。 母平均(mu)が未知である現象を計測誤差がわかっている計測手段で計測する話。 (n)回計測を行って得られた標本(X_1,X_2,cdots,X_n)は、母平均を中心として誤差分振れているはず。 つまり、(X_1=mu+e_1,X_2=mu+e_2,cdots,X_n=mu+e_N)。 誤差(e_i)は平均0、分散(sigma^2)の正規分布(N(0,sigma^2))に従うと考えると、 標本(X_i)は(mu)だけオフセットした正規分布(N(mu,sigma^2))に従うと考えられる。 標本平均は(bar{X}=frac{1}{n}(X_1+X_2+cdots+X_n))だから、 (n)に関係なく(E(bar{X})=mu)であって、(V(bar{X})=frac{sigma^2}{n})から(lim_{nrightarrow infty}V(bar{X})=0)。 中心極限定理により大なる(n)のとき(bar{X})の分布は正規分布(N(mu,frac{sigma^2}{n}))で近似できる。 標本(X_i)の標準偏差が(sigma)である一方、標本平均の標準偏差は(frac{sigma}{sqrt{N}})だから、 標本の分布より、標本平均の分布の方が裾が狭い。 正規分布(N(mu,frac{sigma^2}{n}))を標準化しておくと、標準正規分布の累積度数表を使って 平均(mu)、標準偏差(sigma)を評価できるようになる。z得点は以下の変換。 begin{eqnarray} Z=frac{bar{X}-mu}{frac{sigma}{sqrt{n}}} end{eqnarray} 分布(Z)は平均0、標準偏差1の標準正規分布になる。 見方としては、残差が標準偏差何個分か?の分布。全部足して1になる。 (bar{X},mu,sigma,n)として具体的な値を入れると数値(Z)が決まる。 ちなみに確率密度関数と累積度数は以下の通り。 begin{eqnarray} f(x) &=& frac{1}{sqrt{2pi}} exp left( -frac{x^2}{2} right) \\ int_{-infty}^{infty} f(x) dx &=& 1 end{eqnarray} (x=0)から(x=z)の面積(int_0^{z} frac{1}{sqrt{2pi}} left( -frac{x^2}{2} right) )を(Phi(z))とおき、 (Phi(z)=a)となる点を上側(a)パーセント点という名前が付いている。 (Phi(z))の積分は解析的に計算できないけれど、有用だし決まった数値なので、 ここみたいに表ができているからルックアップすれば良い様子。 (Z)得点が1.96であったとすると、標準正規分布表から(Phi(z=1.96)=0.475)であることがわかる。 これは上側確率が0.475という意味なので、両側確率は2をかけて0.975ということになる。 逆に言うと、(mu)だけが不明で、既知の母分散と標本平均から(mu)を推測することに、 この話を使うことができる。つまり、(-1.96 le z le +1.96)という式を立てると、 (mu)の信頼区間を作ることができる。つまり、(n)個の標本を取る操作を100回繰り返すと97.5回は 信頼区間が母平均を含まない区間になっている。 例 確率変数(X)が平均2、分散10の正規分布(N(2,10))に従うとする。 95%信頼区間は(-1.96 lt z lt 1.96)から、 (-1.96 sqrt{10} + 2 lt X lt 1.96 sqrt{10} + 2)。 (-4.2 lt X lt 8.2)。 100回試すと97.5回は母平均がこの区間にある。 (X)が負になる確率は、(Z=frac{X-2}{sqrt{10}})から、(sqrt{10}Z+2lt 0)、(Z lt -frac{2}{sqrt{10}})、(Z lt - 0.633)。 (P(X lt 0)=P(Z lt -0.633)=1-P(z lt 0.633))。

default eye-catch image.

たたみこみと正規分布の再生性

[mathjax] 正規母集団からの推定をやる前に、正規分布の再生性の理解が必要だったのでまとめてみる。 独立な確率変数(X_1)、(X_2)がそれぞれ確率分布(g(x))、(h(x))に従うとする。 各確率変数の和(X_1+X_2)が従う確率分布を(k(z))とする。 確率(P(X_1+X_2=z))を考えると、(X_1+X_2=z)となるのは、 (X_1=x, X_2=z-x)としたとき、両者を足して(z)になる全ての組み合わせ。 (X_1)は(g(x))、(X_2)は(h(z-x))に従うので、両者が同時に起こる確率は(g(x)h(z-x))。 これをまとめて書くと、 begin{eqnarray} k(z) = sum_x g(x)h(z-x) end{eqnarray} この形が「たたみこみ(convolution)」。  (k = g * x)と書く。 確率変数(X_1)、(X_2)が独立で、それぞれ平均(mu_1)、(mu_2)、分散(sigma_1^2)、(sigma_2^2)の正規分布に従うなら、 以下が成り立つ。 begin{eqnarray} N(mu_1,sigma_1^2) * N(mu_2,sigma_2^2) = N (mu_1+mu_2, sigma_1^2 + sigma_2^2) end{eqnarray} これ、モーメント母関数を使って証明できる様子。 ある分布のモーメント母関数があったとして、モーメント母関数を(n)回微分して変数を(0)と置くと、 分布の期待値、分散、歪度、突度など統計量を求められるやつ。 [clink url=\"https://ikuty.com/2018/09/22/moment_generating_funuction/\"] 正規分布の確率密度関数とモーメント母関数は以下の通り。 begin{eqnarray} f(x) &=& frac{1}{sqrt{2pisigma}} expleft( - frac{(x-mu)^2}{2sigma^2} right) \\ M(t) &=& exp left( mu t + frac{sigma^2 t^2}{2} right) end{eqnarray} もちろん、(N(mu_1,sigma_1^2))、(N(mu_2,sigma_2^2))のモーメント母関数は, begin{eqnarray} M_1(t) &=& exp left( mu_1 t + frac{sigma_1^2 t^2}{2} right) \\ M_2(t) &=& exp left( mu_2 t + frac{sigma_2^2 t^2}{2} right) \\ end{eqnarray} かけると、以下の通り(N(mu_1+mu_2,sigma_1^2+sigma_2^2))のモーメント母関数となる。 begin{eqnarray} M_1(t) M_2(t) &=& expleft( mu_1 t +frac{sigma_1^2 t^2}{2} right) expleft( mu_2 t + frac{sigma_2^2 t^2}{2} right) \\ &=& expleft( (mu_1+mu_2) t +frac{(sigma_1^2 + sigma_2^2) t^2}{2} right) end{eqnarray} たたみこみの操作は、独立な確率変数(X_1,X_2)について(X_1+X_2)の確率分布を求める操作だから、 この結果は独立な確率変数(X_1,X_2)が(N(mu_1,sigma_1^2))、(N(mu_2,sigma_2^2))に従うとき、 (X_1+X_2)が(N(mu_1+mu_2,sigma_1^2+sigma_2^2))に従うことを意味する。 ある確率分布のたたみ込みの結果が同じ確率分布になることを再生性(reproductive)というらしい。 正規分布の再生性を使った演算 正規分布には再生性があるので、以下みたいな演算ができる。 (X_1,X_2,cdots,X_n)が独立で、それぞれ正規分布(N(mu_1,sigma_1^2),N(mu_2,sigma_2^2),cdots,N(mu_N,sigma_N^2) )に 従うとき、(X_1+X_2+cdots+X_n)は(N(mu_1+mu2+cdots,mu_N,sigma_1^2+sigma_2^2+cdots+sigma_N^2))に従う。 (X_1,X_2,cdots,X_n)が全て同じ(N(mu,sigma^2))に従うなら、 (X_1+X_2+cdots+X_n)は、(N(nmu, nsigma^2))に従う。 (bar{X}=frac{X_1+X_2+cdots+X_n}{n})は(N(mu,frac{sigma^2}{n}))に従う。

default eye-catch image.

標本分散(sample variance)と不偏分散(unbiased variance)

[mathjax] 不偏分散は(frac{1}{n} sum_{i=1}^n (X_i-bar{X})^2)ではなく、(frac{1}{n-1} sum_{i=1}^n (X_i-bar{X})^2)。 分母から1を引く必要がある。なんでか調べてみたので書いてみる。 標本平均は(n)の大小によらず母平均の近傍にあって、母平均に確率収束する。 標本平均は(n)の大小に関係なく、その期待値と母平均が等しい(不偏)。 begin{eqnarray} E(bar{X}) &=& frac{1}{n}nmu = mu \\ lim_{n rightarrow infty} V(bar{X}) &=& 0 end{eqnarray} 100個のデータがあって、その中から5個取ったときの平均と、50個取ったときの平均に 母平均の推測という意味で違いがない。 では、分散はどうか。 定義通り標本の分散を(S^2 = frac{1}{n}{ (X_1-bar{X})^2 + (X_2-bar{X})^2 + cdots + (X_n-bar{X})^2 } )とすると、 (S^2)は母分散と等しくならない。不偏にならない。つまり、(E(S^2) ne sigma^2)。 その値が不偏であるか否かは、実際に期待値を式変形してみるとわかる。 結論を知っていないと出来ない変形ばかりだけども...。 begin{eqnarray} E(S^2) &=& Eleft[frac{1}{n} sum_{i=1}^n (x_i-bar{X})^2 right] \\ &=& frac{1}{n} Eleft[ sum_{i=1}^n (x_i-bar{X})^2 right] \\ &=& frac{1}{n} Eleft[ sum_{i=1}^n left( (x_i-mu)-(bar{X}-mu) right)^2 right] \\ &=& frac{1}{n} Eleft[ sum_{i=1}^n (x_i-bar{X})^2 -2sum_{i=1}^n(x_i-mu)(bar{X}-mu) + sum_{i=1}^n (bar{X}-mu)^2 right] \\ &=& frac{1}{n} Eleft[ sum_{i=1}^n (x_i-bar{X})^2 -2n (bar{X}-mu) +n (bar{X}-mu)^2 right] \\ &=& frac{1}{n} Eleft[ sum_{i=1}^n (x_i-bar{X})^2 - n(bar{X}-mu)^2 right] \\ &=& frac{1}{n} sum_{i=1}^n Eleft[ (x_i-mu)^2 right] - Eleft[ (bar{X}-mu)^2 right] \\ &=& frac{1}{n} sum_{i=1}^n V(x_i) - V(bar{X}) \\ &=& sigma^2 - frac{1}{n} sigma^2 \\ &=& frac{n-1}{n} sigma^2 end{eqnarray} ということで、(E(S^2)ne sigma^2)。不偏でない。 では、どうすれば不偏な標本分散を得られるのか。 (E(S^2)=frac{n-1}{n} sigma^2)から、(frac{n}{n-1}E(S^2)=sigma^2)なので、(s^2=frac{n}{n-1}E(S^2))とすれば、 (s^2=sigma^2)ということになり、(s^2)は不偏となる。(s^2)を不偏分散という。 begin{eqnarray} s^2 = frac{n}{n-1} { (X_1-bar{X})^2 + (X_2-bar{X})^2 + cdots + (X_n-bar{X})^2 } end{eqnarray} 100個データがあって、10個データをとったときと、100個データをとったときの (E(S^2))の母分散とのズレは以下の通り。10個のとき(E(S^2))をそのまま計算してしまうと、 その値は母分散から10%もズレてしまう。100個にしても1%ずれる。 begin{eqnarray} E(S_{10}^2) &=& frac{9}{10}sigma^2 \\ E(S_{100}^2) &=& frac{99}{100}sigma^2 \\ end{eqnarray}

default eye-catch image.

母集団の種類に関係なくランダムウォークが正規分布に従う

[mathjax] 統計のど真ん中。大数の法則と中心極限定理。 確かに奇跡的な美しさを感じる...。 同じ確率分布に従う独立な確率変数(X_1,X_2,cdots,X_n)について、(n)が大きければ (bar{X}=frac{X_1+X_2+cdots+X_n}{n})と置いたときに、(E(bar{X})=E(X_n))として扱ってよかった。 ((bar{X})は(mu)に確率収束した。(n)が次第に大きくなるにつれて(V(bar{X)})が0に収束した。) begin{eqnarray} lim_{n rightarrow infty}P(|X_n-mu|ge epsilon) rightarrow 0 end{eqnarray} なんとなく、最頻値を峰として(n)の増加に伴って峰が険しくなっていきそうだけど、 実際、(X_i)がどんな確率分布に従っていたとしても、 (S_n=X_1+X_2+cdots+X_n)は平均(nmu)、標準偏差(sqrt{n}sigma)の正規分布に従う(と考えて良い)らしい。 (bar{X})は平均(mu)、標準偏差(frac{sigma}{sqrt{n}})の正規分布に従う。 これが中心極限定理(Central limit theorem)。 (n)が大きくなったとき、ランダムウォーク(S_n)は平均(nmu)、標準偏差(sqrt{n}sigma)の分布が正規分布になる、 というのは以下のようにかけるらしい。 右辺は標準正規分布の確率密度関数の定積分。 左辺は既に正規分布であることを前提にしているような... begin{eqnarray} Pleft(a le frac{S_n -nmu}{sqrt{n}sigma} le bright) rightarrow int_a^b frac{1}{sqrt{2pi}}e^{-frac{x^2}{2}}dx end{eqnarray} (frac{S_n-nmu}{sqrt{n}sigma}=frac{n(frac{1}{n}S_n-mu)}{sqrt{n}sigma}=frac{bar{X}-mu}{frac{sigma}{sqrt{n}}}) みたいに変形すると、 以下のようにすることもできる。 正規分布を標準正規分布に変換するために(つまり平均=0、標準偏差=1にするために)、 標準偏差で割る(つまり標準偏差何個分か?)変換。 begin{eqnarray} Pleft(a le frac{bar{X}-mu}{frac{sigma}{sqrt{n}}} le b right) rightarrow int_a^b frac{1}{sqrt{2pi}}e^{-frac{x^2}{2}}dx end{eqnarray} 統計学入門に奇跡的な証明が書いてあるけど完全に写経になるので終わり。

default eye-catch image.

統計的推測と大数の法則

[mathjax] 大数の法則は、標本数が大きくなったときに標本平均が母平均に確率収束することを数学的に表す。 begin{eqnarray} lim_{n rightarrow infty} P(|bar{X}-mu|ge epsilon) rightarrow 0 end{eqnarray} 母集団の確率分布がわかっていれば、母平均、母分散、標準偏差などの統計量は定義済みだから、 標本数が多いという仮定があれば、標本から母平均を見立てて計算し、 見立てた母平均が\"どの程度\"真の母平均に収束しているかを言うことで、 標本がどの程度母集団を言い当てているかを言うことができる。 1か0が起こるベルヌーイ試行を100回やって、1が80回、0が20回起こったとする。 この100回の標本は\"たまたま\"取得した結果だけれども、 これが無限集団の一部分だったとして、今回得られた標本から無限集団の平均を得られないか。 まず、確率変数(X_1,X_2,cdots,X_{100})は以下の確率分布に従う。 begin{eqnarray} P(X_i=1) &=& frac{80}{100} = 0.8 \\ P(X_i=0) &=& frac{20}{100} = 0.2 end{eqnarray} さらに確率変数(X_1,X_2,cdots,X_{100})は(p=0.8)のベルヌーイ分布に従う。 その平均は(p=0.8)、分散は(p(1-p)=0.16)。 これは\"たまたま\"取得した100個の標本を使って作った母平均と母分散。 では、この\"たまたま\"の母平均と母分散を使って、 (bar{X}=frac{X_1+X_2+cdots+X_n}{n})に関する(E(bar{X}))、(V(bar{X}))を計算する。 (E(bar{X})=mu)、(V(bar{X})=frac{sigma^2}{n})であるから、 (E(bar{X})=0.8)、(V(bar{X})=0.0016)、((sqrt{V(bar{X})}=0.04 )) 大数の法則(弱法則)により、\"たまたま\"の100回という試行において、 (bar{X})は0.8に確率収束し、その標準偏差は(0.04)であることがわかる。 試行回数を増やしていくと、標準偏差が0に収束する。 100回という試行回数において、1が起こる回数は平均80回で、上下に(pm 4% )ばらける。 無限大まで増やしていく途中、100回という区切りではこうだった、ということで、 さらに増やしていくと、上下の幅が(pm 4% )からさらに狭くなる。 全ての標本を得ないでも、手元の標本だけから、 上下の幅という曖昧さを残すことで推測できる。 大数の法則 (n)個の確率変数(X_1,X_2,cdots,X_n)が平均(mu)、分散(sigma^2)の分布に独立に従うとき、 和を(n)で割った平均の分布(bar{X}=frac{X_1+X_2+cdots+X_n}{n})について、平均、分散はそれぞれ、 begin{eqnarray} E(bar{X}) &=& frac{nmu}{n} = mu \\ V(bar{X}) &=& frac{nsigma^2}{n^2} = frac{sigma^2}{n} end{eqnarray} (n)の極限をとったとき、標本平均は母平均(mu)に収束し、分散はゼロに収束する。 begin{eqnarray} lim_{n rightarrow infty} E(bar{X}) &=& mu \\ lim_{n rightarrow infty} V(bar{X}) &=& 0 \\ end{eqnarray} (n)が少ない間は、(mu)の近傍に散らばって分布するが、 (n)が増えるに従って、(mu)の近傍の散らばりの範囲が狭くなっていく。 昔、写経した証明は以下。 だいぶ普通に読めるようになった。 [clink url=\"https://ikuty.com/2018/07/06/law_of_large_numbers/\"] 上で書いたように(V(bar{X})=frac{sigma^2}{n})だから、(sigma_n^2)は、(sigma_n^2=V(bar{X}_n)=frac{sigma^2}{n}) 以下のチェビシェフの不等式において、 begin{eqnarray} P(|X_n−mu|ge ksigma_n) &le& frac{1}{k^2} end{eqnarray} (ksigma_n=epsilon)とおく。(k=frac{epsilon}{sigma_n}=sqrt{n} frac{epsilon}{sigma}) だから、 begin{eqnarray} P(|X_n−mu|ge epsilon) &le& frac{sigma^2}{nepsilon^2} end{eqnarray} 極限を取ると、 begin{eqnarray} lim_{n rightarrow infty} P(|X_n−mu|≥epsilon) &=& lim_{n rightarrow infty }frac{1}{epsilon^2} cdot frac{sigma^2}{n} \\ &rightarrow& 0 end{eqnarray}

default eye-catch image.

エレベーターの定員

[mathjax] 620kgまで積載できるエレベータ。定員は何人でしょうか..? ネガティブ思考の大家としては、 太っている人が乗ることを考えて少なめな方が...と常日頃思っていたのだけど、 案外、悪いことばかり考えるのも客観的でないことがわかる。 数を打つと、大きいもとと小さいものが打ち消しあう。 良いことと悪いことが打ち消しあって平均に収まる。 確率変数が大量((n)個)にあったとき、それらが全て同じ分布に独立に従うとして、 その分布の平均が(mu)、分散が(sigma^2)だとする。それらの平均、分散は、 begin{eqnarray} E(X_1) = E(X_2) = cdots = E(X_n) = mu \\ V(X_1) = V(X_2) = cdots = V(X_n) = sigma^2 \\ end{eqnarray} 和の平均、分散は、標準偏差は、 begin{eqnarray} E(X_1+X_2+cdots + X_n) = nmu \\ V(X_1+V_2+cdots + X_n) = nsigma^2 \\ s = sqrt{n} sigma end{eqnarray} 人間1人の体重が平均65.0kg、標準偏差が6.0kgだとすると、 9人の平均は人数に比例して580kg。人数の増加にしたがって順当に見積もりが増える。 どれぐらい上にマージンが必要かというと..標準偏差は(sqrt{9}*6=18.0)kg。 最大積載重量が600kgであったとしても(+1sigma)の範囲に収まる! 620kgあれば、(+2sigma)も収まる。9人はいける。 振れ幅は(sqrt{n})に比例する。 (n)が大きくなっても大して振れ幅は大きくならない。 ちなみに、法令上(建築基準法施行令第129条)は、 1人あたり65kgとして頭数で割るだけが決まっているそうで、 体重の振れ幅については結局謎のまま。 床面積あたりにするとキツキツになる模様。 http://www.mitsubishielectric.co.jp/elevator/inquiry/faq/category01/qa3.html 最大積載重量が実際のハードリミットでそこでブザーがなるんだから、 定員、というソフトリミットは目安でしかないんだな。