たのしいRuby(第6版)を読んでのメモです。
p and print method
printメソッドは実行結果やメッセージなどを普通に表示したいとき、
pメソッドは実行中のプログラムの様子を確認したいとき、
と使い分ける。
magic comment
# encoding: 文字コード
でソースコードの文字コードを指定できる。
pメソッドで日本語の文字列を出力すると、
文字化けしたような出力になる場合、
-E 文字コード
の形式でコマンドラインオプションを指定する
opal
OpalはRubyのコードをJavaScriptに変換するコンパイラで、
WebブラウザでRubyを実行する環境を作ることもできる
require method
引数に指定されたライブラリを探して、そのファイルに書かれた内容を読み込む。
読み込みが終わると、requireメソッドの次の行から処理を再開する。
require_relativeメソッドは実行するプログラムが置かれたディレクトリを基準としてライブラリを探す
pseudo variables
nil,true,false,selfなどの特定の値を指し示すために予約された名前で
代入することによって値を変更することはできない
multiple substitution
複数の変数への代入を1つの式で行うことができる。
受け取る側の変数に1つだけ「*」を付けておくと、
その変数には余った値の配列が代入される。
一時変数を使わない値の入れ替えに使うこともできる。
a, b = 0, 1
a, b = b, a
p [a, b] #=> [1, 0]
配列を代入するときに左辺に複数の変数があると、
自動的に配列の要素を取り出して多重代入が行われる。
配列の先頭の要素だけを取り出したい場合には下記のようにも書ける
ary = [1, 2]
a, = ary
p a #=> 1
comparison operator
比較演算子の結果はtrueかfalseとなる
regular expression matching
マッチした場合には文字列中でマッチした部分の位置を、
マッチしなかった場合はnilを返す
boolean value of Ruby
真 | falseとnilを除くオブジェクト全て |
偽 | falseとnil |
trueやfalseを返さないメソッドで、
意味のある値を返せない場合でもnilを返すメソッドであれば、条件判断に利用できる
unless statement
条件が偽(false or nil)のときに文を実行する
case statement
比較したいオブジェクトが1つで、
そのオブジェクトの値によって場合分けしたい場合、
case文を使ったほうがわかりやすく見えることがある。
正規表現を用いた場合分けもできる。
case文はwhenで指定した値に一致するかどうかを「===」演算子を使って判定する。
「===」は左辺が数値や文字列の場合は「==」と同じ意味を持つが、
正規表現の場合は「=~」と同じようにマッチしたかどうかを判定したり、
クラスの場合は右辺がそのクラスのインスタンスかどうかを判定するなど,
両辺の値を比較するよりも、もう少し緩い意味で同じかどうかを判断するために使う
object_id
すべてのオブジェクトはアイデンティティと値を持っている。
オブジェクトのIDはobject_id(or id)メソッドで得ることができる。
2つのオブジェクトが同じかどうか(IDが同じかどうか)はequal?で判定できる。
オブジェクトではなく、値が等しいかどうかを調べるには「==」を使う。
値が同じかどうかを判定するメソッドとしてeql?もあり、
たいていは同じように振る舞うが、数値クラスで再定義されているので、
異なる振る舞いをする。
多少厳密に比較を行う必要がある場合に使う。
例えば、ハッシュのキーとして、0と0.0は別のものとして扱われるが、
ハッシュオブジェクトの内部ではeql?メソッドを使ってキーの比較が行われている
until statement
while文は条件が成立している間は繰り返すのに対して、
until文は条件が成立するまで繰り返す
for statement
for文はRubyの内部処理としてはeachメソッドが実行される特殊な構文になっている。
したがって、eachメソッドを呼び出すことができるオブジェクトであれば、
for文のinのあとに指定することができる
blocked method call
オブジェクト.メソッド名(引数, ・・・) do |変数1, 変数2, ・・・|
ブロックの内容
end
or
オブジェクト.メソッド名(引数, ・・・) {|変数1, 変数2, ・・・|
ブロックの内容
}
ブロックの最初の「| 〜 |」で囲まれた部分に指定された変数は
ブロック変数またはブロックパラメータという。
この変数にはブロックを実行するたびに、メソッドからパラメータが渡される。
パラメータの数や値はメソッドごとに異なる
no receiver method
Rubyではレシーバを記述せずに使えるメソッドを関数的メソッドと呼ぶこともある。
実際にレシーバに該当するオブジェクトがないわけではなく、
それが省略されている。
関数的メソッドはレシーバの状態によって結果が変わることがないように作られている。
(例えば、printやsleepメソッド)
variable length argument
引数の数が決められないメソッドは、
「*変数名」の形式で定義し、与えられた引数をまとめて配列として得られる。
「*変数名」の形式の引数はメソッド定義の引数リストに1つだけ含めることができる
keyword argument
キーワード引数を使うと引数名と値をペアで引数として渡せるようになる。
「引数名: 値」の形式でデフォルト値も指定できる。
デフォルト値を指定したくない場合は、「引数名: 」と引数名だけ書く。
デフォルト値が省略された引数は呼び出し時に省略できない。
定義に存在しないキーワード引数をエラーにせずに受け取りたい場合は
「**変数名」の形式で受け取る
def meth(x: 0, y: 0, z: 0, **args)
[x, y, z, args]
end
p meth(z: 4, y: 3, x: 2) #=> [2, 3, 4, {}]
p meth(x: 2, z: 3, v: 4, w: 5) #=> [2, 0, 3, {:v=>4, :w=>5}}
引数argsには、引数リストに存在しないキーワードをキーとして
ハッシュオブジェクトが設定される。
キーワード引数は通常の引数と組み合わせて用いることができる。
def func(a, b: 1, c: 2)
︙
end
aは必須、b,cはキーワード引数となり、
呼び出し時は
func(1, b: 2, c: 3)
のように最初の引数に続けて、キーワード引数を指定する。
ハッシュをキーワード引数として渡すことができる。
キーはシンボルである必要がある
instance_of?, is_a?
instance_of?はクラスのインスタンスであることを調べることができ、
is_a?は継承関係をさかのぼってクラスに属するかどうかを調べることができる。
スーパークラスを指定せずに定義したクラスは
Objectクラスの直接のサブクラスとなる
self variables
インスタンスメソッドの中で、
メソッドのレシーバ自身を参照するにはselfという特殊な変数を使う
private
レシーバを指定して呼び出せないメソッドにする。
レシーバを省略した形式でしか呼べないため、インスタンスの外側から利用できなくなる。
何も指定せずに定義されたメソッドはpublicになるが、
initializeメソッドだけは常にprivateとして定義される
module
クラスとモジュールは下記の点が異なる。
・モジュールはインスタンスを持つことができない。
・モジュールは継承できない。
モジュールの提供するメソッドは「モジュール名.メソッド名」という形式で参照する。
このような形式で使用するメソッドをモジュール関数という。
モジュール内で定義されたメソッドや定義と同名のものが定義されていない場合、
モジュール名の指定を省略できる。
includeを使えば、モジュールが持っているメソッドや定数名を
現在の名前空間に取り込むことができる。
また、クラスにモジュールを追加して複数のクラスでモジュールの機能を共有できる
メソッドをモジュール関数として外部に公開するにはmodule_functionを使う必要がある
Enumarable
Ruby標準の組み込みの機能で、
Mix-inにより機能を提供することができるモジュール
prepend method
Mix-inされたクラスでのメソッドよりも
モジュールのメソッドを優先することができる
extend method
extendメソッド(Object#extend)はモジュールで定義されたすべてのメソッドを
特異メソッドとしてオブジェクトに追加することができる。
モジュールを特異クラスにインクルードして、
オブジェクトにモジュールの機能を追加する
assignment operator
「+」と「=」を組み合わせて、「+=」とするように
二項演算子と代入を組み合わせた演算子。
「=」は右側
logical operator
・左側の式から順に評価される
・論理式の真偽が決定すると、残りの式は評価されない
・最後に評価された式の値が論理式全体の値となる
safe navigation operator(&. method)
nilチェック付きのメソッド呼び出すを行うことができる。
レシーバのオブジェクトがnilの場合はnilを返す。
RailsのActiveSupportにtryというメソッドがあるが、
&.の場合は呼び出そうとしているメソッドが定義されていない場合はエラーになる。
これはtryメソッドがrespond_to?でチェックしているため。
ちなみに、Rails6あたりから、
tryメソッドの内部でtry!メソッドを呼び出すのはやめるのかも
(https://github.com/rails/rails/commit/ba7d1265e3f2755f55243f32c0264c5c20e01610#diff-ed128222705b72beebd1daae7a6be237)
exception handling
例外処理にはbegin〜rescue〜end文を使用する。
rescueに続けて変数名を指定することで例外オブジェクトを得ることができる。
例外オブジェクトはclass,message,backtraceといったメソッドを持つ。
begin
例外を発生させる可能性のある処理
rescue => 例外オブジェクトが代入される変数
例外が起こった場合の処理
end
また、rescue節で補足する例外を指定することもできる。
メソッドの処理全体をbegin〜endでくくる場合はbeginとendを省略して、
rescue節やensure節を書くことができる。
ちなみに、rescueは修飾子として使うこともできる
例外を発生させる可能性のある処理 rescue 例外が起こった場合の処理
exception class
すべての例外はExceptionクラスのサブクラス。
rescue節で例外クラスを指定しなかった場合は
ExceptionのサブクラスであるStandardErrorとそのサブクラスが補足される。
自分で例外クラスを定義する場合は
StandardErrorクラスを継承したクラスを作り、さらにそれを継承するのが一般的とのこと
raise an exception
自分で例外を発生させるにはraiseメソッドを使う。
-
raise メッセージ
(StandardErrorクラスのサブクラスである)RuntimeErrorを発生させる。
新しく生成された例外オブジェクにメッセージとして文字列をセットする -
raise 例外クラス
指定した例外を発生させる -
raise 例外クラス、メッセージ
指定した例外を発生させ、
新しく生成された例外オブジェクにメッセージとして文字列をセットする -
raise
rescue節の外ではRuntimeErrorを発生させ、
rescue節の中では最後に発生した例外($!)をもう一度発生させる
x.divmod(y)
xをyで割ったときの商と余りを配列にして返す
securerandom library
安全な乱数を生成させるモジュールを提供
%Q, %q
「"」と「'」を含めた文字列を作りたいときは
「"」「'」などの特殊文字を使うよりも、%Qや%qを使うと簡単
desc = %Q{Rubyの文字列には「''」も「""」も使われます。}
str = %q|Ruby said, 'Hello world!!'|
Here documents
改行を含む長い文字列を作りたい場合は便利。
<<の後ろには終了の記号として" "か' 'で囲った文字列を書く。
インデントを揃えたいときは「<<」の代わりに「<<-」を使う。
5.times do |i|
print(<<-"EOB")
i: #{i}
EOB
end
上記の結果
i: 0
i: 1
i: 2
i: 3
i: 4
「<<~」を使うと行頭の空白が切り詰められる
5.times do |i|
print(<<~"EOB")
i: #{i}
EOB
end
上記の結果
i: 0
i: 1
i: 2
i: 3
i: 4
running OS command
`ls -al`
each_line code example
each_lineメソッドを使って繰り返し新しい行を読み込む場合には、
chomp!メソッドなどで破壊的に改行文字を落とすという方法がある
f.each_line do |line|
line.chomp!
# lineを処理
end
string code conversion
encodeメソッドを使う
str = '文字列'
str.encode('utf-8')
p str.encoding
Hash#fetch
Hashの値の取り出しに使える。
第2引数を指定すれば、キーが登録されていないときに返す値として使用できる
h = Hash.new
h.store('R', 'Ruby')
p h.fetch('R', '(undef)') #=> "Ruby"
p h.fetch('N', '(undef)') #=> "(undef)"
creating a Hash with default values by block
キーによって異なる値を返したい場合や、
すべてのキーに対する値が同じオブジェクトになることを避けたい場合には、
Hash.newにブロックを指定する
h = Hash.new do |hash, key|
hash[key] = key.upcase
end
h['a'] = 'b'
p h['a'] #=> "b"
p h['x'] #=> "X"
p h['y'] #=> "Y"
merging hashes
p ({'a': 'x'}.merge({'b': 'y'})) #=> {:a=>"x", :b=>"y"}
%r (regular expression)
%rを使って正規表現のオブジェクトを作ることができる。
正規表現中に「/」の文字を使いたいときに便利
%r(pattern)
%r<pattern>
%r|pattern|
%r{pattern}
matching of head and end
行頭が「^」、行末が「$」でマッチングする。
例えば、「^ABC」というパターンは「"012\nABC"」という文字列にもマッチする。
文字列の先頭は「\A」、文字列の末尾「\z」
matching with any character
「.」は任意の1文字とマッチ
repeating pattern
・「*」は0回以上の繰り返し
・「+」は1回以上の繰り返し
・「?」は0回または1回の繰り返し
・「{n}」はn回の繰り返し
・「{n, m}」はn〜m回の繰り返し
最短マッチでは
・「*?」は0回以上の繰り返しのうち最短部分
・「+?」は1回以上の繰り返しのうち最短部分
regular expression options
・「i」はアルファベットの大文字・小文字の違いを無視
・「x」は正規表現内の空白と「#」の後ろの文字を無視
・「m」は「.」が改行文字にもマッチ
Regexp.newメソッドでは第2引数にオプション定数を指定できる(複数可)
Regexp.new('Rubyスクリプト', Regexp::EXTENDED)
Regexp.new('Rubyスクリプト', Regexp::IGNORECASE | Regexp::MULTILINE)
p Regexp::EXTENDED #=> 2
p Regexp::IGNORECASE #=> 1
p Regexp::MULTILINE #=> 4
capturing groups in regular expression
$数字の形の変数でマッチした部分の一部を取り出せる。
「(?: )」でキャプチャする必要がないパターンをまとめることもできる。
$数字以外にも
「$`」でマッチした部分よりも前の文字列、
「$&」でマッチした部分そのものの文字列、
「$'」でマッチした部分より後ろの文字列などを取りだすことができる
sub method and gsub method
subメソッドは最初にマッチ部分だけ、
gsubメソッドはマッチする部分すべてを書き換える
scan method
パターンにマッチした部分を取り出す(置き換えない)。
マッチした部分になんらかの処理を行うときに使う
'abracatabra'.scan(/.a/) do |matched|
p matched
end
上記を実行すると
"ra"
"ca"
"ta"
"ra"
また、正規表現の中で「()」が使われていると、
そこにマッチした部分を配列にして返し、
ブロックの変数を「()」の数だけ並べると、
配列ではなくそれぞれの要素を取り出すことができる。
ブロックがない場合はマッチした文字列の配列を返す
standard input
標準入力はデータを受け取るためのIOオブジェクト。
組み込み定数STDINに割り当てられているほか、
グローバル変数$stdinからも参照されている。
レシーバを指定しないgetsなどのメソッドは$stdinからデータを受け取る。
標準入力の最初はコンソールに関連付けられていて、キーボード入力を受け取る
standard output
標準出力はデータを出力するためのIOオブジェクト。
組み込み定数STDOUTに割り当てられているほか、
グローバル変数$stdoutからも参照されている。
レシーバを指定しないputs,print,printfなどのメソッドは$stdoutへ出力する。
標準出力の最初はコンソールに関連付けられている
standard error
標準エラー出力は警告やエラーを出力するためのIOオブジェクト。
組み込み定数STDERRに割り当てられているほか、
グローバル変数$stderrからも参照されている。
警告メッセージを表示するためにwarnメソッドが$stderrへ出力する。
標準エラー出力も最初はコンソールに関連付けられている
input operation
io.gets(rs)
io.each(rs)
io.each_line(rs)
is.readlines(rs)
IOオブジェクトioからデータを1行読み込む。
行の区切りは引数rsで指定した文字列になるが、
引数が省略された場合は組み込み変数$(default "\n")が行の区切りとなる。
これらのメソッドは行の末尾の改行を含む文字列を返す。
文字列の末尾の改行文字を削除するにはchomp!メソッドが便利
getsメソッドやeach_lineメソッドを使って、行単位で読み込みを行うと、
それまでに何行読み込んだかが自動的に記録される。
その行数はlinenoメソッドで取得できる
binary mode and text mode
テキストモードは改行文字の変換が有効で、
バイナリモードは変換を行わない
zip file reading
zipに圧縮されたデータを読むときはzcatコマンドに展開してもらったデータを受け取ると便利で、
そのコマンドをIO.popenに記述すると
コマンド結果として標準出力に出力したデータをIOオブジェクトから受けることができる
stringio library
StringIOオブジェクトへの出力は実際にはどこへも出力されず、
オブジェクトの中に蓄えられ、あとからreadメソッドなどで読み出せる。
StringIOオブジェクトはすでに文字列として持っているデータを
IOオブジェクトのように見せかけることもできる。
巨大なデータはいったんファイルに保存し、そうでないデータはそのまま別の処理へ渡すという場合、
StringIOオブジェクトを使うと、IOオブジェクトか文字列かによって処理を分けなくて済む。
(例えば、open-uriライブラリでURIを開いたときに返されるオブジェクトは、
IOオブジェクトかStringIOオブジェクトなので、それぞれのオブジェクト毎の記述をしなくて済む)
fileutils library
fileutilsライブラリを読み込むことにより、
FileUtils.cp(ファイルのコピー)やFileUtils.mv(ファイルの移動)など、
ファイルを操作するメソッドを使用することができる。
FileUtils.mvを使うと、File.renameメソッドではできないファイルシステムやドライブをまたがったファイルの移動ができる
Dir.open example
Dir.openやFile.openはブロックを与えることによって、
closeメソッドの呼び出しを省略する。
Dir.open('/usr/local/lib/ruby/2.6.1') do |dir|
dir.each do |name|
p name
end
end
dir.read method
Dir#readメソッドを実行すると、最初に開いたディレクトリに含まれるものの名前を1つ順に返す。
ここで読み出せるものは基本的に次の4種類のいずれか
・カレントディレクトリを表す「.」
・親ディレクトリを表す「..」
・その他のディレクトリ名
・ファイル名
Dir.glob method
Dir.globメソッドを使うと、シェルのように「」と「?」などのパターンを使ってファイル名を取得できる。
Dir.globメソッドはパターンにマッチしたパス名(ファイル名およびディレクトリ名)を配列にして返す。
カレントディレクトリにあるすべての隠しファイル名はDir.glob('.')で取得できる
File.basename(path[, suffix])
パス名pathのうち、一番後ろの'/'以降の部分を返す。
拡張子suffixが指定された場合は戻り値から拡張子の部分が取り除かれる
File.split(path)
パス名pathをディレクトリ名の部分とファイル名の部分に分解し、
2つの要素からなる配列を返す。多重代入を使って受け取ると便利
dir, base = File.split('/usr/local/bin/ruby')
p dir #=> "/usr/local/bin"
p base #=> "ruby"
File.expand_path(path[, default_dir])
相対パス名pathをディレクトリ名default_dirに基づいて絶対パス名に変換する
find library
findライブラリに含まれるFindモジュールは
指定したディレクトリ以下に存在するディレクトリやファイルを再帰的に処理することができる
require 'find'
IGNORES = [ /^\./, /^\.svn$/, /^\.git$/ ]
def listdir(top)
Find.find(top) do |path|
if File.directory?(path)
dir, base = File.split(path)
IGNORES.each do |re|
if re =~ base
Find.prune
end
end
puts path
end
end
end
listdir(ARGV[0])
FileUtils.mkdir_p(path)
階層の深いディレクトリも一度に作成できる。
pathを配列にして複数のディレクトリを作成することも可能