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.

ブロック操作系まとめ

全然網羅できてないけどブロック操作系まとめ。 [arst_toc tag=\"h3\"] スコープを作ってコレクションを操作できるのはそうだとして、 ブロック内の評価値をまとめたものがブロックの評価結果となるところがポイント。 コードがぐっと短くなって気分が良い。 基本的なeach 最も基本的な配列、ハッシュのeach。配列、ハッシュをレシーバとしてeachメソッドを呼び出す。 評価結果はレシーバ自身。 arrays = [100, 200, 300] rets = arrays.each do |value| p value end # 100 # 200 # 300 # [100, 200, 300] p rets # [100, 200, 300] hashes = {a1:\"a1\", a2:\"a2\", a3:\"a3\"} rets = hashed.each do |value| p value end # [a1:\"a1\", a2:\"a2\", a3:\"a3\"] # {a1: \"a2\"} # {a2: \"a2\"} # {a3: \"a3\"} # [a1:\"a1\", a2:\"a2\", a3:\"a3\"} p rets # [a1:\"a1\", a2:\"a2\", a3:\"a3\"} each_with_index 配列の順序数を付けることができる。 arrays = [100, 200, 300] arrays.each_with_index do |value,index| p \"#{index}_#{value}\" end # \"0_100\" # \"1_200\" # \"2_300\" # [100, 200, 300] each_key, each_value ハッシュのキーのみ、値のみをブロックで使う場合は以下。 キー、値の両方を使える制限無しの状態でどちらかを使う、というのではなく、 初めからキー、値のどちらを使うかを宣言して、それだけを使うという拘りビリティ。 hashes = {a1:\"a1\", a2:\"a2\", a3:\"a3\"} rets = hashes.each_key do |key| p key end # :a1 # :a2 # :a3 # {:a1=>\"a1\", :a2=>\"a2\", :a3=>\"a3\"} rets2 = hashes.each_value do |value| p value end # \"a1\" # \"a2\" # \"a3\" # {:a1=>\"a1\", :a2=>\"a2\", :a3=>\"a3\"} upto, downto ループを回して、制御変数をインクリメント、デクリメントするケースには専用の構文を使う。 Rubyにはインクリメント演算子は無いし、腐ってもforの外で用意した変数に対して+1を自己代入したりしない。 何より、英語の構文と同じところが気持ちが良い。 100.upto(103) do |i| p i end # 100,101,102,103 103.downto(100) do |j| p j end # 103,102,101,100 times n回の構文,times。 3.times do |i| p i end # 0,1,2 map mapは、レシーバの各要素分繰り返す。ブロックの評価値は各要素に対応する値となり、新たな配列が返る。 eachだと、評価結果がレシーバ自身であるためレシーバを加工して返す用途に使えないが、 mapを使うと、ブロックの応答として加工済みの配列を返せるので、制御構文がグッと短くなって気持ちが良い。 あくまでもレシーバの個数繰り返すので、ブロックでnilを返すと、評価結果の該当要素がnilになる。 arrays = [1, 2, 3] rets = arrays.map do |i| \"values_#{i}\" end [\"values_1\", \"values_2\", \"values_3\"] rets2 = arrays.map do |i| \"values_#{i}\" if i <3 end ["values_1", "values_2", nil] ただ、順序数付きのmap_with_indexはこのような動きをしない。 arrays = [1, 2, 3] rets = arrays.map_with_index do |index,value| \"#{index}_#{value}\" end p rets # {評価結果なし} select 前述の通り、mapはレシーバの個数分繰り返すため、ブロック内の最終評価値がnilとなった場合、 評価値の該当要素がnilになる。 そうではなく、nilでない評価結果をまとめて返したい場合はselectを使う。 rets3 = arrays.select do |i| \"values_#{i}\" if i <3 end ["values_1", "values_2"] まだまだ全然網羅できてないけど、とりあえず終了。

default eye-catch image.

procとlambda

[arst_toc tag=\"h3\"] Procとcallメソッド 以下では、get_closureメソッドにより、クロージャオブジェクトを生成している。 その際初期値を設定している。 クロージャオブジェクトのcallメソッドによりクロージャを実行する。 初期値を設定した変数initial_valueはクロージャのスコープなので、 クロージャの外からは一切存在を管理する必要がない。 def get_closure initial_value Proc.new{|up| initial_value += up} end closure = get_closure(100) closure.call(10) # 110 closure.call(20) # 130 proc内でのreturn proc内でreturnすると、procの呼び出し元のスコープを抜ける。 procの呼び出し位置で処理が止まるため、そのあとの処理は実行されない。 以下callerというメソッド内でprocを実行するが、 procがreturnすることで、proc.callの実行後callerのスコープを抜ける。 なので2は評価されない。 この動きは後のlambdaと異なる。 def caller proc = Proc.new{ return 1 } proc.call 2 end Procオブジェクトをブロックに変換 作っておいたクロージャオブジェクトをメソッド実行時にブロックとして使用することができる。 メソッドの引数として、&を付与してProcオブジェクトを渡す。 def func hoge hoge + yield end closure = Proc.new {5} func(10, &closure) # 15 ブロックをProcオブジェクトに変換 ブロックをProcオブジェクトとして受けることができる。 def func2 hoge, &proc hoge + proc.call end func2(100) do 200 end # 300 lambda lambdaもprocを返すが、Proc.newとは異なる動きをする。 procの実行時にreturnするとprocの呼び出し元のスコープを抜けていた。 lambdaは実行時にreturnしてもlambdaの呼び出し元のスコープを抜けない。 下の例でlambdaが返したprocが途中で終わっても callerはそこで抜けず、proc.callの後が実行される。 def caller proc = lambda{ return 1 } proc.call 2 end lambdaの別の書き方 以下のように格好良く書ける。他の言語の同様の構文に似せた感じ。 lmd2 = ->(hoge,fuga) { hoge + fuga } lmd2.call(100,200) # 300

default eye-catch image.

ブロック(クロージャ)とスコープ

[arst_toc tag=\"h3\"] Ruby固有のスコープの扱いについて追記。 ブロックとスコープ メソッド呼び出しの直後にブロックを指定することができる。 例えば、以下においてメソッドfuncを呼び出す際に引数1と同時にdoから始まりendで終わるブロックを指定している。 ブロックの中ではx1という変数に2を代入していて、それがブロックの評価値となる。 ブロック付きで呼び出されたメソッドの中では、ブロックの評価値をyeildという特殊変数で受け取ることができる。 つまり、メソッドfuncの中では、引数y1=1と、ブロックの評価値2の和の評価が行われる。 def func y1 y1 + yield end # 3 func (1) do x1 = 2 end なお、ブロックはdo..endだけでなく、{..}でも書ける。 Rubyの掟的には、複数行のブロックはdo..end、1行のブロックは{..}とのこと。 func (1) { x1 = 2 } ちなみに、ブロックの中と外はスコープが異なる。 ブロックの中で確保した変数x1は、スコープの外からは見えない。 ブロックの中で確保した変数はブロックの評価が完了した時点で破棄される。 func (1) { x1 = 2 } p x1 # Exception 破棄されるのはブロックで初めて確保した変数であって、 ブロックの外部の変数をブロック内で使用した場合は、 ブロック内での操作がブロック外の変数に影響する。 例えば、以下のように予めブロックの外で変数x1を確保しておき、 ブロックにより加算するという状況で、ブロック内のx1は紛れもなくブロック外のx1。 x1はブロックにより束縛される(キリっ)。 ブロックの評価後に評価すると、確かにブロック内の加算が行われた後。 x1 = 10 func (1) { x1 += 2 } # 13 p x1 # 12 Rubyの特殊なスコープ事情 変数の束縛について追記。他の言語では動きそうな以下のコードはRubyでは動かない。 Rubyではオブジェクトやメソッドの壁を超えて変数を参照することはできない。 (Railsではインスタンス変数を使って超えちゃってるけど..) fuga = 20 def myfunc p fuga end ブロックを使うことで初めて実行時の環境(コンテキスト,文脈)を共有できる。 ここで一般化して良いのかわからないけども、 処理の生成時の変数を束縛するものをクロージャというらしい。 実行時の環境を動的に使えて便利だな、ぐらいの認識だったけど、 変数の束縛がポイント。 ブロックの引数 ブロック、つまりクロージャの引数の定義方法と使いかたは以下の通り。 クロージャ内の先頭で引数を|で囲む。かなり特殊に見える。慣れだろうか。 def func2 x2, y2 x2 + yield(y2,30) end p func2(10,20){|x,y| x+y} # 60 メソッド側でブロックが指定されたか調べる どんなケースでそんな使いかたをするのか.. メソッド側で呼び出し時にブロックが指定されてか否かを調べることができる。 def func3 x3,y3 return x2 + yield(y2,30) if block_given? 0 end p func3(10,20){|x,y| x+y} # 60 p func3(10,20) # 0

default eye-catch image.

範囲リテラル、case式、while式

[arst_toc tag=\"h3\"] 範囲を表すリテラルが言語仕様として準備されている。 範囲リテラル 範囲クラスはRangeクラスのインスタンス。 \"..\"により開始、終了の両方を含む範囲を表現する。\"...\"だおt終了は含まれない。 def func for var in 1..10 do p var end end func # 1 2 3 4 5 6 7 8 9 10 def func2 for var in 1...10 do p var end end func2 # 1 2 3 4 5 6 7 8 9 包含と同値 include?により範囲に値が含まれるかどうかを調べられる。 (1..5).include?(3) # true (1..5).include?(6) # false なお、===演算子も包含関係を表す。 (1..5) === 3 # true (1..5) === 10 # false ==演算子は同値関係を表す。 (1..5) == (1..5) # true (1..5) == 3 # true 添字演算子 配列の添字演算子に範囲を指定することができる。何番目から何番目という表現はかなり直感的。 a = [ 1, 2, 3 ,4 ,5] p a[1..2] # [2..3] p a[1,3] # 文字列の添字演算子にも範囲を指定することができる。部分文字列を範囲で表現できる。直感的。 a = \"abcdef\" p a[1..2] # bc case式 phpなど他の言語のswitch文。case when.. と書く様子。 でもなぜswitchではいけなかったのだろうか。 case式自体の評価値はcase式内で最後に評価された値となる。 def hoge a case a when 10 then p \"first\" when 20 then p \"second\" else p \"third\" end end hoge 10 # \"first\" hoge 20 # \"second\" hoge 30 # \"third\" whenで何が行なわれているかというと、===演算子によって同値の判断がされている。 なので、whenにRangeを指定すると、===演算子によって包含関係が評価される。 while式 phpのwhileと同等。条件が真である間繰り返す。 i = 0 while (0..5) === i do p i i += 1 end # 1 2 3 4 5 phpなどにもあるように、ループの末尾にwhileを書くことができる。 最初の1回は必ず実行される例のやつ。 後に置くので\"後置while\"という名前がついている。 i=0 begin p i i+=1 end while (1..4) === 1 # 0 1 2 3 4 後置whileとは別に、後に置くが\"修飾詞\"として使う書き方がある。 最初の1回はwhileが評価されるところが後置ifと異なる。 i=0 p i += 1 while (0..4) === i # 1 2 3 4 5 until式 条件が真になるまで繰り返す(真になったら抜ける)、専用の条件式。 Rubyは条件式を書く時に否定文を書く必要がない。(たぶん) i=0 until i===5 do p i i+=1 end # 0 1 2 3 4

default eye-catch image.

ハッシュリテラル

要素の順序性がある場合に使う配列と、順序性がない場合に使うハッシュ。 Rubyのハッシュの言語仕様について。 [arst_toc tag=\"h3\"] 基本的な書き方 PHPと同じようにキーとして文字列を使う場合は以下の通り。 a = {\"hoge1\" => 100, \"hgoe2\" => 200, \"hoge3\" => 300} a[\"hoge1\"] # 100 a[\"hoge2\"] # 200 a[\"hoge4\"] # nil キーとしてシンボルを使う場合は以下の通り。Rubyっぽくなる。 Ruby1.9以降はこちらを使う。 b = {:hoge1 => 100, :hoge2 => 200, :hoge3 => 300} b[:hoge1] # 100 b[:hoge2] # 200 b[:hoge4] # nil Hash.new()による生成 Hash.new()により配列の初期値として新たに確保した変数を設定できる。 Array.new()と同様に、全ての要素として同じオブジェクトを設定する。 a = Hash.new(100) a[:hoge1] # 100 a[:hoge1].object_id # 12345 a[:hoge2] # 100 a[:hoge2].object_id # 12345 []による生成 面倒くさいことに、キーと値が交互に現れる配列からハッシュを生成できる。 その書き方は以下の通り。 a = Hash[:hoge1, 100, :hoge2, 200, :hoge3, 300] # [:hoge1=>100,:hoge2=>200,:hoge3=>300] b = [[:hoge1,100],[:hoge2,200],[:hoge3,300]].to_h # [:hoge1=>100,:hoge2=>200,:hoge3=>300] 関数の引数、波カッコの省略 関数にハッシュを引数として与える場合、ハッシュを構成する波カッコを省略できる。 というか、引数の数が合わなかったときに末尾の処理の一つである様子。 ハッシュ引数の波カッコの種略を引数の先頭にもってくると(末尾の引数でないと)、例外が発生する。 def func a, b, c c end func 100,200,:hoge1=>300,:hoge2=>400 # {:hoge1=>300,:hoge2=>400} func :hoge1=>100,:hoge2=>200,300,400 func :hoge1=>100,:hoge2=>200,300,400 # 例外 キーワード引数 Rubyっぽい書き方。キーワード引数。 キーワードにシンボルで名前をつけることができる。関数を呼び出す時の可読性が向上する。 def func hoge1:, hoge2:, hoge3 hoge1 + hoge2 + hoge3 end func 100,200,300 # 600

default eye-catch image.

配列、配列演算、繰り返し

本日の素振り。 [arst_toc tag=\"h3\"] 配列 配列はArrayクラスのインスタンス。 a = Array.new(3) # [nil,nil,nil] b = Array.new(2,\"hoge\") # [\"hoge\",\"hoge\"] 配列は[]で表す。 a = [10,20,30] # [10,20,30] a[1] # 20 b = [20,true,\"hoge\"] # [10,true,\"hoge\"] b[2] # \"hoge\" b[3] # nil %記法で配列生成 配列の要素が文字列である場合に限って、%W、%wによって文字列を配列化できる。 デリミタは半角スペース、%Wでダブルクォート、%wでシングルクォート(なし)。 自動的にダブルクォート、シングルクォートで括るため、 文字列の配列を作るためにダブルクォート、シングルクォートで汚すことを防げる。 p = %W(hoge fuga) # [\"hoge\",\"fuga\"] q = %w(hoge fuga) # [\'hoge\',\'fuga\'] joinで配列結合 配列と配列を結合して新しい配列を作るのではなく、要素を文字列として結合する。 p = [100,200,300] # [100,200,300] p.join # \"100200300\" p.join(\"_\") # \"100_200_300\" 配列の生成 Array.new()を使うと同じオブジェクトを持つ配列を作れる。 要素分の変数が確保されるのではなく、指定した値を持つ変数が1つ確保されて並ぶ。 a = Array.new(2,\"hoge\") # [\"hoge\", \"hoge\"] a.object_id # 4032 b.object_id # 4032 a[0].replace(\'fuga\') # [\"fuga\", \"fuga\"] a.object_id # 4032 b.object_id # 4032 Array.new(){}だと要素数分の変数が確保されて並ぶ。 a = Array.new(2){\'hoge\'} # [\"hoge\", \"hoge\"] a[0].object_id # 4033 a[1].object_id # 4034 範囲外アクセス 配列の大きさを超えてアクセスしたとき、例外とならず、アクセスしたところまで配列が広がる。 最初の位置から作った最後の位置までnilで埋められる。 a = Array.new(3){\"hoge\"} # [\"hoge\",\"hoge\",\"hoge\"] a[5] = \"fuga\" # [\"hoge\",\"hoge\",\"hoge\",nil,nil,\"fuga\"] 負の添え字アクセス 何が何でもコード行を削減したい意思! 配列に負の添え字でアクセスすると\"末尾から数えた位置\"という意味になる。 a = [\"hoge1\", \"hoge2\", \"hoge3\"] # [\"hoge1\", \"hoge2\", \"hoge3\"] a[-1] # \"hoge3\" 配列のスライス phpのarray_slice()に相当するやつ。指定したインデックスから何個分というやつ。 a = [0, 1, 2, 3, 4, 5, 6] # [0, 1, 2, 3, 4, 5, 6] a[2,3] # [2, 3, 4] 以下のように指定したインデックスから何個分を置き換えられる。 a = [0, 1, 2, 3, 4, 5, 6] # [0, 1, 2, 3, 4, 5, 6] a[1,2] = \"hoge\" # [0, \"hoge\", 3, 4, 5, 6] b = [0, 1, 2, 3, 4, 5, 6] # [0, 1, 2, 3, 4, 5, 6] b[1,2] = \"hoge\", \"fuga\" # [0, \"hoge\",\"fuga\", 3, 4, 5, 6] c = [0, 1, 2, 3, 4, 5, 6] # [0, 1, 2, 3, 4, 5, 6] c[1,2] = \"hoge1\", \"hoge2\", \"hoge3\" # [0, \"hoge1\",\"hoge2\",\"hoge3\", 3, 4, 5, 6] 複数の戻り返却と多重代入 複数の値を返す関数を定義する。複数の値を返すように見えるが配列が返っている。 def test return \"hoge1\", \"hoge2\", \"hoge3\" end a, b, c = test # [\"hoge1\", \"hoge2\", \"hoge3\"] a # \"hoge1\" b # \"hoge2\" c $ \"hoge3\" 戻りとしてネストした配列を返すことができて、同じ構造の変数に代入できる。(多重代入) def test2 return [[\"hoge1\", \"hoge2\"], \"hoge3\"] end (x,y),z = test2 # [[\"hoge1\",\"hgoe2\"], \"hoge3\"] +、-演算子 左オペランドと右オペランドの要素の加算、左オペランドから右オペランドの減算 a = [\"hoge1\",\"hoge2\"] # [\"hoge1\",\"hoge2\"] b = [\"hoge2\",\"hoge3\"] # [\"hoge2\",\"hoge3\"] a + b # [\"hoge1\",\"hoge2\",\"hoge2\",\"hoge3\"] a - b # [\"hoge1\"] b - a # [\"hoge3\"] 減算の場合、左オペランドに重複があるときは全て削除される x = [\"hoge1\",\"hoge1\",\"hoge2\"] # [\"hoge1\",\"hoge2\",\"hoge3\"] y = [\"hoge1\"] x - y # [\"hoge2\",\"hoge3\"] *演算子 右オペランドが数値の場合は左オペランドをその回数繰り返す。 右オペランドが文字列の場合はjoinと同じ。 a = [1,2,3] a * 2 # [1,2,3,1,2,3] for式,each式 PHPのforeachに近いforとeach。forは期待と異なりループの中と外はスコープが同じ。 for内の変数をfor外でアクセスできる。 for value in [100,200,300] inner_loop = 500 end inner_loop + 100 # 600 対してeachはループ内とループ外でスコープが異なりeach内の変数はeach外で未初期化。 [100,200,300].each do inner_look2 = 500 end inner_loop # undefined variable

default eye-catch image.

symbol、参照、破壊的メソッド

[arst_toc tag=\"h3\"] シンボル 文字列そのものに意味はなく、単純にラベルとして文字列を扱いたいというときシンボルを使う。 シンボルのポイントは、文字の並びが同じであれば(同値であれば)同一であること。 hoge1 = :hoge1 p hoge1 # 524328 hoge2 = :hoge1 p hoge2 # 524328 hoge3 = :hoge3 p hoge3 # 324648 文字列からシンボルに変換できる。 hogeA = \"hoge1\".to_sym p hogeA.object_id # 524328 値の同値性と同一性 演算子==は、例えば2つの文字列の値が等しいかどうか(同値であるか)を比較する。 一方で、2つの変数のobject_idが等しいかどうか(同一であるか)を比較するために、 equal?メソッドを利用する。 hoge_x = \"hoge1\" hoge_y = \"hoge1\" p hoge_x == hoge_y # true p hoge_x.equal? hoge_y # false オブジェクトと参照 変数を宣言すると(つまりリテラルを指定すると)オブジェクトへの参照が与えられる。 以下の場合、\"hoge\"という一つのオブジェクトをhoge1、hoge2が参照する形になる。 hoge1 = \"hoge\" hoge2 = hoge1 hoge1.equal? hoge2 # true 片方の変数について値を変更した場合、その変更に対応するオブジェクトが新たに作られ、 新しいオブジェクトへの参照が作られる。 hoge1 = \"hoge\" hoge2 = hoge1 hoge2 = hoge2 + \"fuga\" p hoge1 # hoge p hoge2 # hogefuga hoge1.equal? hoge2 # false 実引数と仮引数の参照 関数に実引数として渡した変数と関数内で利用できる仮引数の参照は同じ。 ただし関数内で仮引数に対して処理すると、別の値が確保され、仮引数はその値への参照となる。 x = 100 x.object_id # 435123 def func x p x.object_id # 435123 x = 200 p x.object_id # 249821 end 破壊的メソッドと参照 変数の値を変更したときに参照を変えずに値を変更するメソッドを破壊的メソッドという。 慣例的に破壊的メソッドの末尾には「!」をつける。 破壊的メソッドと、代入時に別変数を確保して参照を変更するメソッドはしばしば同名で、 「!」により区別することがある。 残念ながら徹底されておらず、破壊的メソッドなのに「!」がなかったり逆もある。 w = \"hogehoge\" w.chop # \"hogehog\" w # \"hogehoge\" w.chop! # \"hogehog\" w # \"hogehog\"

default eye-catch image.

文字列、%記法

[arst_toc tag=\"h3\"] 文字列の式展開 PHPと同様にダブルクォート、シングルクォートにより文字列を表現する。 前者は変数展開あり、後者は変数展開なし。 実際には文字列内の\"式展開\"で、式のto_sメソッドの評価結果が文字列に展開される。 to_sメソッドを備えていれば独自クラスでも展開できる。 hoge = 10 p \"hoge is #{hoge}\" # hoge is 10 p \'hoge is #{hoge}\' # hoge is #{a} ただ文字列の中に変数を書いただけではダメで、変数展開用の識別子がいるんだな。 パーセント記法による式展開 シングルクォート、ダブルクォート以外を使って文字列を作ることもできる。 文字列の中でエスケープ無しでシングルクォート、ダブルクォートを使えて便利。 hoge = %*THIS IS TEST STRING \"HOGEHOGE\".*% もともとシングルクォート、ダブルクォートの区別により式展開するしないが決まっていたが、 当然パーセント記法を使うと式展開をそのままでは選べない。 パーセントの後にq、またはQを付与することで式展開をするしないを選ぶことができる。 なお、デフォルトは式展開をする。つまり%Qと同じ。 fuga = 100 %q*#{fuga + 100}* # \"#{fuga + 100}\" %Q*#{fuga + 100}* # \"200\" Rubyが複雑に見えるのはコイツのせいじゃないだろうか。 文字列の型変換 文字列から整数、浮動小数点、複素数、有理数などへ変換できる。 それぞれ、to_i、to_f、to_c、to_rというメソッドが用意されている。 厄介そうなことに、型変換ができない場合、変換できるところまで変換する。 そもそも先頭の文字が変換できないなら0。 \"100\".to_i # 100 \"10ab\".to_i # 10 \"1.1.1\".to_f # 1 \"hoge\".to_i # 0 デバッグ出力と関数 文字列をデバッグ用途に出力するときはpを使う。変数内の式は展開されない。 他に、print、putsで出力できる。こちらは変数内の式が展開される。 hoge = \"100 n\" p hoge # \"100 n\" puts 100 # 100 ヒアドキュメント 変数展開なしのヒアドキュメントは以下の通り。ヒアドキュメントの開始をシングルクォートにする。 ヒアドキュメントの終了は識別子も前にスペース禁止。 hoge = 100 str = <<'DOC' #{hoge} HOGE DOC 変数展開ありのヒアドキュメントは以下の通り。開始をダブルクォートにする。 hoge = 100 str = <<"DOC" #{hoge} HOGE DOC 文字列演算 文字列は+、*に対応している。 hoge = \"HOGE\" + \"FUGA\" # \"HOGEFUGA\" fuga = \"HOGE\" * 3 # \"HOGEHOGEHOGE\" <<により文字列連結。 hoge = \"HOGE\" hoge << "FUGA" # "HOGEFUGA" 比較演算 文字列の辞書順で比較する。 \"X\" \"Y\" # false \"XXX\" < "XXY" # true "XYZ" == "XYZ" # true 文字列長 .lengthメソッドにより文字列長を返す。バイト数ではなく文字数。 \"HOGEHOGE\".length # 8 sprintfと% %演算子にはsprintf相当の機能も備わっていて混乱する。 知らないと訳がわからない。 sprintf(\"THIS IS TEST INTEGER %02d\",1) # \"THIS IS TEST INTEGER 01\" p \"THIS IS TEST INTEGER %02d\" % 1 # THIS IS TEST INTEGER 01\"