kotlin基本構文-ジェネリッック型,ジェネリック関数,共変と反変,out予約語
ジェネリック プライマリコンストラクタで受け取った文字列をプロパティとして持つオブジェクトを作成し、 その文字列を取得するコードは以下の通り。普通。 class MyObject(var value: String) { fun getProp(): String { return this.value } } val m = MyObject(\"hogehoge\") println(m.getProp()) この書き方だと、型が文字列に限定される。 型を一般化して、文法で型と紐づける書式がジェネリック。 上記のコードをジェネリックで一般化してみる。 一般化した型引数をTとしている。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(100) println(g2.getProp()) // 100 型推論による型指定の省略 プライマリコンストラクタに渡すパラメタの型からジェネリック型が唯一決まる場合は、 インスタンス化時の型指定を省略できる。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(\"fugafuga\") println(g2.getProp()) // fugafuga 複数のジェネリック型指定 複数のジェネリック型を指定できる。下記のような書き方になる。 class MyGenerics2(var value1: T, var value2: R) { fun getProp1(): T { return this.value1 } fun getProp2(): R { return this.value2 } } val g3 = MyGenerics2(100,\"hoge\") println(g3.getProp1()) println(g3.getProp2()) 型引数の制約 型引数TをT:typeとすると、type型かtype型の派生型という意味になり、型の制約を作れる。 ジェネリック型をHogeクラスの派生に制限する定義として、インスタンス化時にInt型を指定する例。 コンパイル時(前)にちゃんとエラーが出る。 class Hoge(var def: String) { fun getVal(): String{ return \"hoge\" } } class MyGenerics3(var value: T) { fun getProp(): T { return this.value } } // Error:(44, 32) Kotlin: The integer literal does not conform to the expected type Hoge var g4 = MyGenerics3(100) println(g4.getProp()) val hoge = Hoge(\"hoge\") var g5 = MyGenerics3(hoge) println(g5.getProp().getVal()) ジェネリック関数 関数の引数と戻り値を関数の呼び出し時に決めて紐づけられる仕組み。 ジェネリック型の定義はclassだが、ジェネリック関数はclassではなく関数。 以下は、リストの末尾(tail)を応答するジェネリック関数。 引数として配列を取るが、配列の型を呼び出し時に決められる。 fun tail(list: Array): T = list[list.size-1] var data = arrayOf(1,2,3,4,5) println(tail(data)) 共変(covariant)と不変(invariant) Javaの話。Javaでは配列の要素のクラスに継承関係があるとき、 その配列は親クラスの配列に代入できる。 aaryの定義はNumberの配列だが中身はIntegerの配列になっている。 定義上Numberの配列だから、Numberの派生クラスのインスタンスを代入する式を書いてもコンパイルが通る。 例えば、Longのインスタンスを入れられる。 実態はIntegerの配列だから、Longのインスタンスを入れた瞬間に怒られる。 Integer[] iary = new Integer[10]; Number[] aary = iary; aary[0] = new Long(0); // Exception occured. kotolinの配列は共変ではなく不変。 配列の要素に継承関係があったとしても代入できない。 これを不変(invariant)と言ってkotolinは不変が基本。 var intArray: Array = arrayOf(1,2,3) var anyArray: Array = intArray // Exception occured. ただし、意図的に共変にすることもできる。 以下の通り、kotlinの配列は不変であるから ArrayをArrayに代入できない。 コンパイル時にひっかかる。 open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) //Error:(17, 34) Kotlin: Type mismatch: inferred type is Array but Array was expected var fugaArray: Array = hogeArray out予約語をつけることで、その配列を共変配列にできる! open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) var fugaArray: Array = hogeArray 実際、コレクションのListはoutが指定してある。 なので、それを知らずともJava的に共変配列の操作のようなことができる。 interface List: Collection var list: List = listOf(\"aaa\",\"bbb\",\"ccc\") var list2: List = list
kotlin基本構文-データクラス,シングルトンオブジェクト,オブジェクト式,SAM変換,コンパニオンオブジェクト,Enumクラス
データクラスの同値性 構造体のような用途で使うデータクラス。 プライマリコンストラクタだけで目的を達成できるからclassブロックがない。 data class Article(val url: String, val title: String) var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/2\",\"記事2\") インスタンスの同値性は「==」演算子で評価される。 データクラスには「equals」が定義済みで「==」評価時にequalsが呼ばれる。 データクラスのインスタンス同士を「==」演算子で比較すると、 プライマリコンストラクタで指定した値が同じ場合に限りtrueが返る。 data class Article(val url: String, val title: String) var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/1\",\"記事1\") var article3 = Article(\"http://ikuty.com/2\",\"記事2\") println(article1 == article2) // true println(article1 == article3) // false データクラスの同値性はプライマリコンストラクタで定義された値だけが対象となる。 データクラスに含まれる他のメンバ変数が異なっていても、プライマリコンストラクタで 定義された値が同じであればtrueを返す。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = Article(\"http://ikuty.com/1\",\"記事1\") article1.value = 100 article2.value = 100 println(article1 == article2) article1.value = 100 article2.value = 200 println(article1 == article2) データクラスのtoString インスタンスの文字列化は「toString」関数が評価される。 例えば以下のような感じ。 プライマリコンストラクタで渡さなかったメンバ変数は出てこない。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") println(article1) // Article(url=http://ikuty.com/1, title=記事1) データクラスのプロパティの分割 例えば、IntのPairを2つのIntに分割する書式は以下だった。 val pair :Pair = Pair(10,20) val (p1, p2) = pair println(p1) // 10 println(p2) // 20 この書式において、Pairクラスのcomponent2メソッドが評価されている。 一般にcomponentN関数により左オペランドのN個の変数に代入する。 Pairの場合はcomponent2が評価され、左オペランドの2個の引数に変数が代入されている。 データクラスにおいても予めcomponentNが定義されており、 左オペランドにプライマリコンストラクタで指定した値が代入される。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") val (v1, v2) = article1 println(v1) // http://ikuty.com/1 println(v2) // 記事1 データクラスの複製 copyメソッドによりインスタンスを複製できる。 その際、名前付き引数で指定した値を変更してから複製することもできる。 data class Article(val url: String, val title: String) { var value: Int = 0 } var article1 = Article(\"http://ikuty.com/1\",\"記事1\") var article2 = article1.copy() var article3 = article1.copy(title=\"記事2\") println(article1) // Article(url=http://ikuty.com/1, title=記事1) println(article2) // Article(url=http://ikuty.com/1, title=記事1) println(article3) // Article(url=http://ikuty.com/1, title=記事2) シングルトンオブジェクト クラスではなくオブジェクトそのものを定義し、そのオブジェクトを使い回す書式。 以下のように書く。 object MySingletonSettings { var name: String = \"hoge\" } fun main(args: Array) { println(MySingletonSettings.name) } オブジェクト式 クラス定義を書かないで1回限りのオブジェクトを定義するやり方。 Androidアプリを書くときに、ボタンのイベントリスナーにオブジェクトを渡す例のアレ。 以下のように書く。 btnのsetOnClickListenerにobjectという名称のオブジェクトを渡している。 objectはView.OnClickListenerインターフェースを実装していて、 onClickメソッドをオーバーライドしている。 Androidアプリを書くとコレでいっぱいになると思われる。 btn.setOnClickListener(object: View.OnClickListener{ override fun onClick(view: View) { Log.v(\"Hello Kotlin\",\"clicked!\") } }) SAM変換 オブジェクト式で実装するインターフェースがもつ抽象メソッドが1つ (Single Abstract Method)の場合、 overrideが必要なメソッドは1つしかないということなので、 ラムダ式でそのメソッドを書くことができる。 以下のように書く。 btn.setOnClickListener({ view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")}) ラムダ式のお作法として、そのラムダ式が唯一の引数である場合はブラケットの外に出せる。 そして、ブラケットを省略できる。 さらに、ラムダ式に渡る引数の型(view:View)が明らかな場合に省略できるから以下のようになる btn.setOnClickListener() { view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")} btn.setOnClickListener { view: View -> Log.v(\"Hello Kotlin\",\"clicked!\")} btn.setOnClickListener { Log.v(\"Hello Kotlin\",\"clicked!\")} コンパニオンオブジェクト kotlinにはstaticメンバーは存在しない、のだけれども、 インスタンス化されていないクラス内部のコンパニオンオブジェクトのメソッドを呼び出す、 という仕組みによって、staticメンバー相当の呼び出しができる。 下記、Personクラスのプライマリコンストラクタはprivateであって外部からインスタンス化できない。 PersonクラスはFactoryオブジェクトを内包しており、Person.getInstance()というアクセスにより、 Factory.getInstance()が呼ばれ、Personインスタンスを返す。 class Person private constructor(var name: String) { companion object Factory { fun getInstance(): Person { return Person(\"パーソン\") } } override fun toString(): String { return \"My name is ${this.name}\" } } fun main(args: Array) { val p1 = Person.getInstance() println(p1) } Enumクラス 列挙型。class予約語の前にenumで置いてclassを修飾する。 列挙子は識別子で型はない。 列挙子の型チェックはis演算子で行う。 enum class Season { SPRING,SUMMER,AUTUMN,WINTER } fun main(args: Array) { val a = Season.SPRING println(a) // SPRING println(Season.SPRING == Season.SUMMER) // false println(Season.SPRING is Season) // true println(Season.SPRING is Enum) // true } 列挙型にプロパティを持たせられる。書き方が特徴的だけれども以下の通り。 列挙子の()はSeasonのプライマリコンストラクタで、 順序数(ordinal)の他に、新たに定義したvalueをもたせている。 列挙型のプライマリコンストラクタはprivateであって、外から明示的にインスタンス化できず、 列挙する時にのみ実行される。 enum class Season(val value: Int) { SPRING(1),SUMMER(2),AUTUMN(4),WINTER(8) } fun main(args: Array) { val a = Season.SUMMER println(a) // SUMMER println(a.ordinal) // 1 println(a.value) // 2 } さらにEnumクラスにメソッドを定義できる。 列挙子の列挙と、Enumクラスのメソッド定義の間がセミコロン「;」になっている。 enum class Season { SPRING { override fun getDisplayName(): String = \"春\" }, SUMMER { override fun getDisplayName(): String = \"夏\" }, AUTUMN { override fun getDisplayName(): String = \"秋\" }, WINTER { override fun getDisplayName(): String = \"冬\" }; abstract fun getDisplayName(): String } fun main(args: Array) { for (season in Season.values()) { println(season.getDisplayName()) } }
kotlin基本構文-パッケージ,継承,final修飾子,abstract,多態性,ダウンキャストとSmart cast,インターフェース
参考にしている書籍が薄すぎて悲しくなるが、サクサクっと読み進めてみる。 やはりJavaだからかクラス関係の記述が多いな。 パッケージのインポート kotlinのインポート構文はJava相当の書き方ができる。 パッケージfoo.barに含まれるgooクラスをインポートする書き方と、 foo.barに含まれる全てのクラスをインポートする(static import)書き方は以下。 import foo.bar.goo import for.bar.* kotlinはパッケージのトップレベルにクラスだけでなく関数を置けるので、 import構文も関数を指定することができる。 foo.barパッケージのトップレベルにあるgetValue()をインポートする書き方は以下。 import foo.bar.getValue 別パッケージの同一クラスをimportする際には、 Javaでは完全修飾して書かないといけないが、kotlinではas予約語を使って別名でインポートすれば良い。 import foo.bar.getValue as barGetValue import foo.hoge.getValue as hogeGetValue fun hoge() { println(barGetValue(10)) println(hogeGetValue(20)) } 継承 kotlinのクラスはデフォルトでfinal。デフォルトで継承できない。 open修飾子をつけることでfinalが外れ、継承できるようになる。 オブジェクト指向モデリングを少しかじると、やたら継承関係を作りたくなる参考書が多くて、 継承関係でないのにやたら継承関係にする人が多いと聞いたな...。 継承はextendsではなく「:」。 派生クラスのプライマリコンストラクタで基底クラスのプライマリコンストラクタを呼んでいる。 関数(kotlinはクラスのメンバ関数をメソッドと呼ばないで関数と呼ぶらしい)のデフォルトもfinal。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 派生クラスがプライマリコンストラクタを持たない場合、 基底クラスのコンストラクタを呼ぶ書き方が別にある。 super()で基底クラスのコンストラクタを呼び出す。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson : Person { var name: String var happiness: Int constructor (name: String, happiness: Int) :super() { this.name = name this.happiness = happiness } override public fun sayHello() { println(\"My name is ${this.name}, my happiness is ${happiness}\") } } var person = LuckyPerson(\"Tom\",200) person.sayHello() メンバ関数のデフォルトがfinalということは、 openなメンバ関数をoverrideした後、finalに戻す責任があるということ。 派生は悪、という宣言。。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 抽象クラス、抽象メソッド 抽象クラス、抽象メソッドはabstract予約語を使う。 指定位置は予想の通りで特別な印象はない。 abstract class Person(private var name: String = \"Tom\") { abstract public fun sayHello() } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 多態性と異種リスト 多態性と異種リストのサンプル。何も難しいことはない。 abstract class Animal() { abstract fun sayHello() } class Dog() : Animal() { final override fun sayHello() { println(\"bow\") } } class Cat() : Animal() { final override fun sayHello() { println(\"mew\") } } var animals:Array = arrayOf(Dog(),Cat()) animals.forEach { animal -> animal.sayHello() } // bow mew ダウンキャストとSmart Cast 派生クラスの固有メンバ関数を呼び出すとき、インスタンスのis a関係をチェックしてから 派生クラスにダウンキャストして呼ぶのが普通だと思っていたけども、 kotlinは、is a関係のチェックとダウンキャストを自動的に行ってくれる。 以下の通り、animalインスタンスのis aチェックを行ったあと、 animal.how()および、animal.groom()のコンテキストでは、 animalがそれぞれDog,Catのダウンキャストとなっている。 open class Animal() { } class Dog() : Animal() { fun how() { println(\"howling\") } } class Cat() : Animal() { fun groom() { println(\"grooming\") } } var animals:Array = arrayOf(Dog(),Cat()) animals.forEach { animal -> if (animal is Dog) { animal.how() } // Smart cast to Dog else if (animal is Cat) { animal.groom() } // Smart cast to Cat } インターフェース kotlinは多重継承をサポートしていない。 振る舞いを複数に分割しておいて必要な振る舞いを実装するモデル。 振る舞いはインターフェース(純粋仮想クラス)として書くようになっている。 インターフェースはabstract関数(ex. breath())だけでなくデフォルト実装(ex.sleep())を持てる。 当然、クラスは複数のinterfaceを実装できる。 interface Biological { fun breath() fun sleep() { println(\"goo\") } } interface Mammalian { fun nursing() } fun main(args: Array) { open class Animal() : Biological, Mammalian { final override fun breath() { println(\"foo.\") } final override fun nursing() { println(\"nursing.\") } } var animal = Animal() animal.breath() animal.sleep() animal.nursing() } インターフェースの名前解決 kotlinではインターフェースがデフォルト実装を持てるので、 実装クラス側から定義済みのデフォルト実装を呼ぶケースがある。 もし、実装する複数のインターフェースが同じ名称を持つメンバ関数を持っていた場合、 競合する複数のメンバ関数から どれを呼ぶのか解決する必要がある。 実装クラスからインターフェースを参照する場合には、super予約語を使う。 interface Biological { fun breath() { println(\"foo\") } } interface Mammalian { fun breath() { println(\"boo\") } } fun main(args: Array) { open class Animal() : Biological, Mammalian { override final fun breath() { super.breath() super.breath() } } var animal = Animal() animal.breath() }
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()
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
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
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の言語仕様から。 結構続く。
標本平均の母平均の推定
[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))。
たたみこみと正規分布の再生性
[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}))に従う。
標本分散(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}