Ruby入門(1.基本)です。
たのしいRuby 第5版を参考にしています。
基本的な算術演算子には、
演算子 | 説明 |
---|---|
+ | 足し算 |
- | 引き算 |
* | 掛け算 |
/ | 割り算 |
% | 割り算の余り |
** | 累乗 |
などがある。
また、Math.sin()やMath.sqrt()などの数学関数を使うことができる。sqrtは平方根。
Rubyには4種類の変数がある。
変数 | 記法 |
---|---|
ローカル変数 | 先頭がアルファベットの小文字か「_」で始まる。 |
グローバル変数 | 先頭が「$」で始まる。 |
インスタンス変数 | 先頭が「@」で始まる。 |
クラス変数 | 先頭が「@@」で始まる。 |
ローカル変数は、その関数の内部でしか使えない。グローバル変数はどのスクリプトファイルからでも使えるが、バグが入りやすいため使い方に注意。
インスタンス変数は、そのクラスのオブジェクトの内部で保持される変数で、インスタンスごとに別々の値を使うことができる。似ているものにselfキーワードがあり、そのレシーバ自身のことを指す。
インスタンス変数は、メソッドとは異なりアクセサ(アクセスするための専用のメソッド)を定義しなければアクセスできない。そのため、@valueにアクセスするためにはそのためのアクセサを定義する必要がある。しかしながらバグが入りやすいため自動的にアクセサを定義する書き方がある。
アクセサ | 意味 |
---|---|
attr_reader :hoge | 参照のみ可能。リーダーあるいはゲッターと呼ぶ。 hogeメソッドの定義と同様。 |
attr_writer :hoge | 変更のみ可能。ライターあるいはセッターと呼ぶ。 hoge=メソッドの定義と同様。 |
attr_accessor :hoge | 参照・変更ともに可能。 |
selfキーワードを用いることで、「自分自身のメソッド」や「自分自身のアクセサ」を通じて自分に対する参照・変更・操作を行える。
クラス変数はどのクラスのオブジェクトでも保持される変数で、たとえばHogeクラスのoneオブジェクトとtwoオブジェクトがあったとして、oneオブジェクトでクラス変数@@valueを加算すると、その結果がtwoオブジェクトからも参照できる。
Rubyでは、文字列を表現するために、ダブルクォーテーションで"~"と囲む場合と、シングルクォーテーションで'~'で囲む場合がある。
"~"で囲んだ場合、内部にRubyの変数の値を埋め込むことができる。この場合、変数は#{value}のように#{変数名}で囲む。
たとえば、
name = "Assy" puts "My name is #{name}."
の場合、nameという変数の値が展開され、以下のように表示される。
My name is Assy.
'~'で囲んだ場合、内部にRubyの変数の値を埋め込むことはできない。#{}が記述されていても、出力した時はそのままの状態で表示される。
変数名やメソッド名にはvalue_of_rubyのように小文字の単語を_で区切るが、クラス名やモジュール名はClassOfRubyのように単語と単語の区切りを大文字にするという慣例がある。
また、値を変更しない定数は変数を大文字で始める。大文字で始められた定数の値を変えようとすると警告される。
Rubyでは、Perlと同様に#を一行コメントとして利用できる。行の中の#に続く文字列はコメントとして扱われ、無視される。
また、コメントの開始行に=begin、終了行に=endを書くことで、複数行のブロックコメント(埋め込みドキュメント)を記述できる。
2023.02.03
配列とは連続したデータ構造をひとつの名前と添え字で利用する方法。
配列の例:
cats = ["タマ", "シロ", "クロ", "ミケ"] print cats[0] #=> "タマ"
配列はたとえば多重代入(いくつかの代入を同時に行う)を用いた際に、Perlのように以下のように使うことができる。ただし()の構造と[]の構造が一致する必要がある。
ary = [1, 2, [3, 4, 5]] a, b, (ca, cb, cc) = ary
またeachメソッドを用いてブロックを記述することで、全ての配列の要素に同じ処理を行うことができる。これを「イテレータ」と呼ぶ。
ary = ["Hoge", "Fuga", "Foo", "Bar"] ary.each {|item| puts item }
each_with_indexメソッドを使えば、要素が何番目かの情報(インデックス)も同時に得ることができる。
ary = ["Hoge", "Fuga", "Foo", "Bar"] ary.each_with_index do |item, i| puts "#{i}番目の要素は#{item}です。" end
配列とリストとハッシュも参照のこと。
Rubyでは、pushとpopあるいはunshiftとshiftを用いて、スタックとキューとして配列を操作できる。
配列の末尾に新しい要素を加える(プッシュ)にはpush、取り出して削除する(ポップ)にはpopを使う。
配列の先頭に新しい要素を加える(要素はひとつひとつずらされる)にはunshift、取り出して削除するにはshiftを使う。
また、配列に格納されている先頭要素を参照するにはfirst、末尾要素を参照するにはlastを使う。
スタックとキューも参照のこと。
Rubyでは、splitを使うことで文字列から配列を作れる。
array = "hoge Hoge fuga Fuga foo Foo bar Bar".split()
a.collectやa.collect!、a.mapやa.map!で、配列aの各要素にブロックを適用し、その結果を集めて新しい配列を作る。
a = [1, 2, 3, 4, 5] a.collect! {|item| item * 3} p a #=> [3, 6, 9, 12, 15]
!のついているメソッドは破壊的なメソッド。破壊的なメソッドとは、レシーバの値を変更するメソッドのこと。
このほか、fillメソッドで配列のすべての要素を同じ値に埋めたり、flattenメソッドで平坦化(ネストのない配列にする)したり、reverseメソッドで逆順の並べ替えをしたり、sortメソッドでソートができる。破壊的メソッドはflatten!、reverse!、sort!となる。
ハッシュを作るには、{}を使う。PerlやPHPと同様=>を使ってキーに対する値を対応付けたデータ構造を利用できる。
ハッシュはシンボル(先頭に:をつけたキーワード)を使ってPerlのようなハッシュを作ることができます:
my_names = {:human => "Assy", :robot => "Schwarz", :god => "Frey"}
キーがシンボルの時は{キー:値}という書き方をすることもできる。上記はキーがシンボルなので、以下のようにも書ける:
my_names = {human: "Assy", robot: "Schwarz", god: "Frey"}
ハッシュはHash.newでも作成できる。ハッシュはハッシュ名["キー"]でキーを参照・変更できる。
hs = Hash.new hs["name"] = "斉藤" p hs["name"]
保管のためにhs.store("name", "斉藤")、取り出すためにhs.fetch("name")という構文も使える。
ハッシュの繰り返しにはeachメソッドを使う。取り出されるのはキーと値の両方。
hs = {"human" => "Assy", "robot" => "Schwarz", "god" => "Frey"} hs.each do |key, value| puts "I'm #{key}, my name is #{value}." end
キーだけを取得するにはeach_keyメソッド、値だけを取得するにはeach_valueメソッドを使う。
条件判断:
def cmp0(x) if x > 0 puts "#{x}は正の数。" elsif x < 0 puts "#{x}は負の数。" else puts "#{x}は0。" end end cmp0(1) cmp0(-1) cmp0(0)
このほかunless文(ifと反対)やcase文(C言語でおなじみの値による複数分岐)がある。
また!を使うことで条件判断を逆にできる。複数の条件判断を使う時は論理演算子の&&や||を使う。
構造化プログラミングも参照のこと。
繰り返し:
my_names = ["assy", "schwarz", "zaidou", "inoue"] for n in my_names puts "わたしの名前は#{n}。" end
この時、for文には繰り返し可能なオブジェクトをinの後に続けて記述する。
またfor i in from..toとすれば、fromからtoまでの値で反復させることもできる。from..toはfromからtoまでの連続した数値を作る。
whileを使って繰り返しを書くこともできる。while文を使う時は、繰り返しが行われ続ける条件式を記述する。until文はwhile文の逆で、条件が満たされない間繰り返しを行う。
また、繰り返しの中でbreak文を実行すると繰り返しを抜ける。next文で次の繰り返しに移行し、redo文で再度同じ処理を実行する。
繰り返しだけをするloop文などもある。この場合はbreak文を適切に設定して繰り返しをどこかで抜けるようにする。
また、配列の要素に対して.each do記法を使うことで、for文と同様に全ての配列要素に対する処理をブロックを使って記述できる。
forはeachのシンタックスシュガー(同じ機能を用いる別の構文)であり、for文は簡単にeach文に変換できる。
ちなみに、回数を指定して繰り返すだけなら、.timesメソッドとブロックを使うと便利。
100.times {|i| puts i}
メソッドの定義:
def mew(name) puts "にゃーん、僕の名前は#{name}だよ。" end mew("タマ")
Rubyのメソッドにはレシーバ(メソッドが帰属するオブジェクト)を持つインスタンスメソッド、クラスメソッド、あるいはレシーバを持たない関数的メソッドがある。
インスタンスメソッドでは、
tama.mew("タマ")
のように.と()を使ってレシーバ名からメソッドを実行する。クラスメソッドである場合は、クラス名から.あるいは::でメソッドを実行する(.と::は同じ意味)。関数的メソッドでは単に
mew()
とする。
関数を参照のこと。
たくさんの不特定多数の引数を取る場合は*argsとする(argsには配列の形でたくさんの引数が入る)。また、:を使うことでキーワード引数を作ることができる。
メソッドには演算子のようなものも指定できる。たとえば[]や[]=を指定したり、def +(arg)やdef -(arg)のような形で演算子も再定義できる。
また、メソッドはreturnで値を返すことができるが、returnは省略できる。returnを省略した場合、最後の値がメソッドの呼び出し元に返される。そのため、
def ave2(x, y) (x + y) / 2 end puts ave2(200, 35)
のように、まるでLispなどの関数型言語のような記述ができる。
引数では、ブロックは&blockのように引数に記述する。これにより、
do |i| ... end
のようなブロックを実行できる。
ブロックをメソッドに与えた場合、それを受け取ったメソッドの側で、次のいずれかを実行することで与えられたブロックを実行できる。
実行処理 | 意味 |
---|---|
yield | 与えられたブロックを実行する。 |
Procクラス | ブロックをオブジェクトとして持ち運び、操作するためのクラス。 |
yieldはRubyではブロックを実行するのに使う。Pythonのようなジェネレータではないため注意が必要。
ブロックの記述にはdo ~ end以外にも{}の形式も使える。普通は「複数行の記述にはdo endを、一行の記述には{}を使う」というやり方がある。
ブロック単体を記述して持ち運ぶにはProc.newを使う。
ave2 = Proc.new do |x, y| (x + y) / 2 end p ave2.call(10, 4)
Proc.newと同様、procメソッドを使っても同じことを記述できる。
また、ほかの書き方としてlambdaがある。lambdaを使うことで、Procオブジェクトを返すメソッドを書くこともできる(別の言語で言えば、関数を返す関数。)
def ave_gen(count) lambda do |*args| value = 0 print "Items: " args.each do |item| value += item print "#{item} " end puts "-- Sum: #{value}, Count: #{count}, Average: #{(value / count)}" end end ave3 = ave_gen(3) ave3.call(4, 8, 10) ave3.call(1, 4, 8) ave5 = ave_gen(5) ave5.call(7, 3, 14, 7, 2) ave5.call(11, 5, 16, 6, 4)
実行結果:
Items: 4 8 10 -- Sum: 22, Count: 3, Average: 7 Items: 1 4 8 -- Sum: 13, Count: 3, Average: 4 Items: 7 3 14 7 2 -- Sum: 33, Count: 5, Average: 6 Items: 11 5 16 6 4 -- Sum: 42, Count: 5, Average: 8
クロージャ・無名関数・関数オブジェクトも参照のこと。
クラスを作る:
class Cat def initialize(myname = "タマ") @name = myname end def mew puts "にゃーん、僕の名前は#{@name}だよ。" end end shiro = Cat.new("シロ") kuro = Cat.new("クロ") tama = Cat.new shiro.mew
オブジェクトのメンバ変数には変数名に@をつける。
インスタンス変数@valueには、外部からはアクセスできない。アクセスするためにはアクセサを定義する。また、インスタンスメソッドをprivateやprotectedやpublicで指定してアクセス可能範囲を設定できる。たとえばprivateにするには
private :priv_cat
とする。privateに引数をつけなかった場合、それ以降に定義された全てのメソッドがprivateになる。
.newはオブジェクトを作成するクラスメソッドであり、newの引数はインスタンスの初期化を行うためにnewした時に実行される、コンストラクタであるinitializeメソッドに送られる。
Rubyではクラスメソッドnewによってオブジェクトを作成する。また、new以外にもリテラルを用いてオブジェクトが作成されることもある。これは文字列リテラルである"text"などが挙げられる。
オブジェクト指向も参照のこと。
Rubyでは継承を行うことで、既にあるクラスに機能を付け加えたり、新しい内容で再定義できる。演算子のように見えるもの(たとえば配列の[])などもメソッドであることがあり、多くの場合再定義が許されている。
継承を行うことで、元のプログラムコードを維持したまま、機能を付け加えたりメソッドを上書きすることができる。superを使うことで親クラスのメソッドを実行できる。
class ExCat < Cat def initialize(myname = "タマ", yourname = "シロ") @name = myname @you = yourname end def mew super puts "やあ、#{@you}くん、お友達になりましょう。" end end kuro_mike = ExCat.new("クロ", "ミケ") kuro_mike.mew
継承の関係は「is-a関係」と呼ばれるが、これは英語のisとaに由来する。
また、Rubyには特異クラスと呼ばれる機能があり、通常のクラスではなく
hoge_str = "hoge" class << hoge_str def print100() 100.times {|i| puts "#{i} : #{self}" } end end hoge_str.print100()
のように、ある特定のオブジェクトだけのインスタンスメソッドを後で付け加えることができる。
Rubyではクラスによるオブジェクト指向の他に、モジュールという機能がある。モジュール内で独自の名前空間を使うことができる(特定のクラス名や変数名と重なっても大丈夫)ほか、クラス定義の中でメソッドや定数をクラスの中にincludeで取り込む(Mix-in)することができる。
module HogeCommonModule # ここに共通に使われるメソッドを書く end class HogeClass1 include HogeCommonModule # HogeClass1のメソッドとしてHogeCommonModuleモジュールを読み込む end class HogeClass2 include HogeCommonModule # HogeClass2のメソッドとしてHogeCommonModuleモジュールを読み込む end
また、includeではなくextendを用いることで、クラスの内部だけではなく、インスタンスとして作られたオブジェクトにも、モジュール内のメソッドを追加できる。たとえば文字列型のインスタンス変数を作って、このインスタンス変数に新しいメソッドを追加するようなことができる。
ある意味、JavaScriptのプロトタイプチェーンや委譲に近いかもしれない。
たとえば、クラスについて「継承関係を持たせたくはないが、同じ処理を複数のクラスで共有したい」といった場合、Mix-inを使うことが適切である。もちろん基本クラスに共通する処理を書いて、派生クラスには個別の処理を書いてもいい。
Rubyでは、「アヒルのように歩きアヒルのように鳴くものはアヒルである」というダックタイピングのやり方がある。たとえば、同じ名前のメソッド.eachを持っているメソッドは、同じように扱うことができる。
「意図していないメソッドが実行されるリスク」はあるが、たとえばゲームのキャラクターなどで、同じ継承関係に属していなくても、攻撃した時は同じように攻撃し、死んだ時は同じように死んでほしい時などに使える。
例外はRubyのエラー処理機構で、以下のように記述する。例外が投げられた時点で処理は中断し、rescueブロックに強制的に移動する。
begin hoge() fuga() foo() bar() rescue => ex # ここでエラー処理を行う end
例外はraiseを使えば自分で投げることもできる。
また、ファイル処理などを行う場合、「openした後で必ずcloseしたい」といった場合がある。こうした場合はブロックを用いることができる。
File.open("hoge.txt") do |file| file.each_line do |line| # lineに対する処理をここに書く end end
もしファイルの読み込みに失敗したら、このブロックは途中で中断する。また、読み込みの後でファイルをcloseする必要があれば、最後に必ずファイルをcloseする。