Gold模擬問題を解いた時に気になったポイントメモ
口癖
- Thread は ねう!ふぉーく!すたあと!
- YAML は ろーど!・・・・だんぷ!
- JSON は ろーど!ぱーす!だんぷ!そしてHash#to_json
- 可変長引数は *
- キーワード引数は **
- RDOC
- *太いボールド*
- _斜めイタリック_
- +等幅タイプライター+
- オプションに trはない・・lpはある・・
スコープ
- ローカル変数には越えられない壁
- class, module, def 定義の境目
- に越えられない壁は存在する。完全にスコープは隔絶される
- ブロック
- は外は見える、中は見えないという「例のアレ」
- ブロックの前にローカル変数が定義されていれば
- それを使う
- ブロック内で初めてローカル変数が定義された場合
- ブロックを抜けた瞬間にそのローカル変数は深淵へと飲み込まれる
- ブロック外でそのローカル変数にアクセスすると例外
- Proc, lambda
- クロージャを抱え込む為、超えられないはずの境界を超える
あまり使われないのに出題される組み込み定数
ARGF(ARGVじゃないぞ)
「スクリプトに指定した引数 (Object::ARGV を参照) をファイル名とみなして、 それらのファイルを連結した 1 つの仮想ファイルを表すオブジェクト」
- マジか(´・ω・`)
./moko1.txt
moko1 1
moko1 2
./moko2.txt
moko2 1
moko2 2
# irbではダメ
p ARGF.readlines
$ ruby temp.rb ./moko1.txt ./moko2.txt
#=> ["moko1 1\n", "moko1 2\n", "moko2 1\n", "moko2 2\n"]
DATA
__END__ 以降は実行対象にならず、Data(Fileオブジェクト)からアクセスされるデータと成り果てる
# irbではダメ
p DATA.readlines
__END__
a
b
c
# => ["a\n", "b\n", "c\n"]
コマンドラインオプション
手で打たないと覚えないよ
-I(アイ)
- $LOAD_PATH にパスが追加される
- $RUBY_LIB はもちろん変化なし
- require, load する時に検索されるようになる
$ ruby -e 'p $:'
$ ruby -I mokohage -e "p $:"
-c 文法チェック
$ ruby -c -e "moko("
syntax error
-e ワンライナー
$ ruby -e "p [1, 2, 3].sum"
6
-W0 -W1 -W2(W) 冗長出力モード 右ほど冗長 -w1がデフォルト
$ ruby -e "HAGE = HAGE = 1"
-e:1: warning: already initialized constant HAGE
-e:1: warning: previous definition of HAGE was here
$ ruby -W1 -e "HAGE = HAGE = 1"
-e:1: warning: already initialized constant HAGE
-e:1: warning: previous definition of HAGE was here
$ ruby -W0 -e "HAGE = HAGE = 1"
=> ワーニングが出ない
-r スクリプト実行前に指定されたファイルをrequireする
moko1.rb がrequireされた後に6が表示される
$ ruby -e "p [1, 2, 3].sum" -r ./moko1
-d デバックモード
スレッドの例外はデバッグモードで顕現するのを観測してみる
$ ruby -e "Thread.new { raise 'HAGE'}"
$ ruby -d -e "Thread.new { raise 'HAGE'}; sleep 100"
require, load
require | 1度しか読み込まない | 拡張子の補完を行う | バイナリエクステンションが読み込める |
load | 何度でも読み込む | 拡張子の補完を行わない | バイナリエクステンションは読み込めない |
可変長引数 多重代入
基本形
a, b = 1, 2, 3
a
=> 1
b
=> 2
配列でもおk
a, b = [1, 2, 3]
a
=> 1
b
=> 2
*を付けて渡すと「これ配列だけど、分解して送るわ!」みたいな感じ(つまり、何も考えずに使うと基本形と同じ動作ぽくなる)
a, b = *[1, 2, 3]
a
=> 1
b
=> 2
例えば2つ目の配列を分解して送りたい!時に使える
a, b = 1, *[2, 3]
a
=> 1
b
=> 2
*を付けて受け取ると「分解して送られてきてるけど、配列として残り全部配列で貰うわ!」みたいな感じ
a, *b = 1, 2, 3
a
# => 1
b
# => [2, 3]
*を付けて受け取って、さらに「配列として残りぜん・・あ、最後の引数は貰い先が決まってるぽいから残り全部配列で貰うわ!」みたいな感じ
a, *b, c = 1, 2, 3, 4
a
# => 1
b
# => [2, 3]
c
# => 4
a, *b, c = 1, 2, 3
a
# => 1
b
# => [2]
c
# => 3
注意
代入時とメソッド時で感覚的な挙動が違ったりする
*argv = [1, 2, 3]
p argv
# => [1, 2, 3]
def hage(*argv)
p argv
end
hage([1, 2, 3])
# => [[1, 2, 3]]
def hage(*argv)
p argv
end
hage(1, 2, 3)
# => [1, 2, 3]
キーワード引数
基本形
def moko(a:, b:)
p a, b
end
moko(a: 1, b: 2)
# => 1
# => 2
ハッシュでもおk
def moko(a:, b:)
p a, b
end
moko({a: 1, b: 2})
# => 1
# => 2
引数を勝手に省略する事は許されない
def moko(a:, b:, c:)
p a, b
end
moko(a: 1, b: 2)
# => ArgumentError: missing keyword: c
初期値を設定すれば省略できる
def moko(a:, b:, c: 3)
p a, b, c
end
moko(a: 1, b: 2)
# => 1
# => 2
# => 3
moko(a: 1, b: 2, c: 100)
# => 1
# => 2
# => 100
どんなキーワードが来るか知らんが、残りは全て受け取ってやる!という気概
def moko(a:, b:, **keywords)
p a, b, keywords
end
moko(a: 1, b: 2, c: 3, d: 4)
# => 1
# => 2
# => {:c=>3, :d=>4}
引数にハッシュが渡されている場合、何も考えないともちろんエラー
def moko(a:, b:)
p a, b
end
moko(a: 1, { b: 2 })
# => SyntaxError 渡せない、というよりは構文としておかしいと判断される
引数にハッシュが渡されている場合「ハッシュを展開して下さい!ほらキーワード引数なんですよ!」と構文解釈させる感じ
def moko(a:, b:)
p a, b
end
moko(a: 1, **{ b: 2 })
# => 1
# => 2
何も考えずに使うと基本形と同じ動作ぽくなる
def moko(a:, b:)
p a, b
end
moko(**{ a: 1, b: 2 })
# => 1
# => 2
Numerable連中の四則演算の結果
- Complex > Float > Rational > Integer
的に変換される Floatは自身に精度がない為にもう一辺の精度を犯すが、Complexだけは他では表現するのは無理なので特別・・みたいな感じ
例外
- NoMethodError < NameError < StandardError < Exception
- raise だけすると RuntimeError (RuntimeError < StandardError) が raise される
- rescue だけすると StandardError(を継承している全て) が rescue される
- rescue は拾う例外を限定、絞る為に使う
- raise Exception や rescue Exception も可能ではあるが普通はやらない(致命的なエラーを無視、回収してしまう可能性)
定義できないメソッド名
:: |
=(+=, =+, ..等の自己代入演算子) |
? : |
$で始まるグローバル変数名 |
他にはえーと・・ |
- 上記以外は全てメソッド定義可能
- 「再定義できない演算子」と混同してはイケない
- まともに定義できなければ define_method 等でむりやり定義可能
- まともに呼び出せないなら send 等で呼び出し可能
- つまり(普通はしない事だが)下記は可能という事である
def def
'ok'
end
p send(:def)
=> "ok"
def end
'ok'
end
p send(:end)
=> "ok"
def true
'ok'
end
send(:true)
=> "ok"
def and
'ok'
end
send(:and)
=> "ok"
def if
'ok'
end
send(:if)
=> "ok"
def return
'ok'
end
send(:return)
=> "ok"
def self
'ok'
end
send(:self)
=> "ok"
def send
'ok'
end
self.send # さすがにsendを潰したのでsendメソッドは使えない
=> "ok"
def ENV
'ok'
end
send(:ENV)
=> "ok"
def __ENCODING__
'ok'
end
send(:__ENCODING__)
=> "ok"
define_method(:"'") { 'ok' }
send(:"'")
=> "ok"
define_method(:"&&") { '&& ok' }
send(:"&&")
=> "&& ok"
define_method(:"||") { '|| ok' }
send(:"||")
=> "|| ok"
定数
- 定数の定義だとか
- メソッド内からの定数の定義・変更(定数がが指し示しているポインタを別のものに変える行為)は御法度である
- 定数が指しているポインタを変更しない自己破壊的メソッドは問題ない おk マジで
- SyntaxError < ScriptError < Exception が発生する
- が、この例外はrescueできなかった。諦めてしまった
- const_set を使用すれば 再変更時にwarning は出るが変更は可能となる
- 定数の参照
- 直接定数名を指定した場合
- class, module, def 定義的に外側(ネストの浅い方)へ探しにいく
- 見つからない場合
- class, module, def を定義の外側に向かって探索する
- Hoge::Hage のように相対で指定した場合も同じルートで相対的に探索する
- メソッド探索ルートと同じルートを検索していく
- 見つからない場合
- class, module, def 定義的に外側(ネストの浅い方)へ探しにいく
- object.const_get(:Hage) みたいな書き方で、レシーバから探索を始める
- object.const_get(‘Hoge::Hage’) もちろんその探索ルートにて相対的に探索してくれる
- クラスにて定数が定義されていない場合、
- 自クラスでinclude, 親クラスにてinclude, prepend している場合、探索ルートに入るのでもちろん探索されるが、
- 自クラスでprependしている場合、自クラスの定数が探索される(最初戸惑ったが、考えてみれば当然か)
- 直接定数名を指定した場合
module A
Value = 'A'
module B
p Value
module C
Value = 'C'
end
p Value
end
end
# => "A"
# => "A"
内側(ネストの深い方)を探しに行ってはくれない(ただし、定数名のみで指定した場合)
module A
module B
p Value
# => NameError: uninitialized constant A::B::Value
module C
Value = 'C'
end
p Value
# => NameError: uninitialized constant A::B::Value
end
end
# => "A"
# => "A"
::を使って定数を参照した場合は、外側(ネストの浅い方)へ(相対位置を)探しにいく ある意味、内側(ネストの深い方)も見てくれる、とも言えなくもない
module A1
module B1
Value = 'ok'
end
module B2
p B1::Value
end
end
# => "OK"
prepend していてもそのクラスから定数を見つける
module M
Value = 'M'
end
class Moko
Value = 'Moko'
prepend M
p Value
# => "Moko"
end
p Moko::Value
# => "Moko"
const_missing
- const_missing(method_name) はクラス化モジュールに特異クラスとして定義してね
- 当然の話だが、 Moko::Hage で const_missingが呼ばれた時には Hage が引数に渡ってくる
Proc.new, Kernel#proc, Kernel#lambdaを指に覚えさせておく
- ちなみに Proc.new と Kernel#proc は同じ
- ちなみに -> (a) {a} と lambda{ |a| a } は同じ
- lambda は引数の数に厳しくてreturnすれば呼び出し元に返るメソッドっぽい動きをするアレ
Proc.new { |a| p a }.call(6)
proc { |a| p a }.call(6)
lambda { |a| p a }.call(6)
-> (a) { p a }.call(6)
catch, throw の動作
catch のブロック内全てを実行したいが・・throw の時点で catch ブロック内の動作を中断する・・ようなイメージ
catch(:homo) do
p 1
p 2
throw(:homo)
p 3
end
1
2
=> nil
throw の第2引数が戻り値
catch(:homo) do
p 1
p 2
throw(:homo, 'HOMO')
p 3
end
1
2
=> "HOMO"
throwするのがメソッドでもよい。スタックを駆け上る
def homo
throw(:homo, 'HOMO')
end
catch(:homo) do
p 1
p 2
homo
p 3
end
1
2
=> "HOMO"
catch, throwのラベルは省略できない
- catch のラベルは省略できる
- throw のラベルは省略できない
- catch のラベルを省略すると throw はどこに戻れば良いか判らないので => 現実的に catch のラベルを省略する意味がない
dup, clone 出題される
clone | 汚染状態、インスタンス変数、ファイナライザ、凍結状態、特異メソッド | を複製する |
dup | 汚染状態、インスタンス変数、ファイナライザ | を複製する |
dupでは特異メソッドはコピーされない
moko = []
def moko.hage
'HAGE-!'
end
p moko.hage
# => "HAGE-!"
p moko.clone.hage
# => "HAGE-!"
p moko.dup.hage
# => undefined method 'hage'
dupでは凍結状態ははコピーされない(最近のRubyだと凍結状態のコピーが指定できたりする)
moko = []
moko.freeze
p moko.frozen?
# => true
p moko.clone.frozen?
# => true
p moko.dup.frozen?
# => false
ファイナライザはそのオブジェクトが開放される時に呼ばれる処理を定義できる・・らしい dupでもコピーされる
a = []
ObjectSpace.define_finalizer(a) {
puts 'I m dead'
}
b = a.dup
c = a.clone
GC.start
# => "I m dead"
# => "I m dead"
# => "I m dead"
Thread
thread を開始するのは 3つ!コレ出題されるんで注意
ねう!ふぉーく!すたあと!と唱える
Thread.new | Threadクラスを継承したサブクラスを開始する時にinitializeを呼ぶ |
Thread.fork | Threadクラスを継承したサブクラスを開始する時にinitializeを呼ばない |
Thread.start | Threadクラスを継承したサブクラスを開始する時にinitializeを呼ばない |
指で覚える
Thread.new { p 'thread' }
Thread.form { p 'thread' }
Thread.start { p 'thread' }
スレッドの中で例外が起こったらどうなるか
- スレッドは警告無しで終了する
-
Thread.new { raise } # => 何も言わないで終了
- つまり、親から見ると例外は観測できない
- 観測できない事は「起こっていない」
- 観測できない事は「起こっていない」 大事な事なのでもう一度
- ruby起動時に -d オプションを付けているとスレッド内で例外が発生した時点でインタプリタ自体が終了する
- 例外が起こったらしいスレッドを joinで待っている親スレッドがある場合は、その親スレッドで同じ例外がraiseされる
-
t = Thread.new { sleep 0.5; raise StandardError } begin t.join rescue => e p "#{e.class} ktkr" end # => "StandardError ktkr"
Module.append_features, Module.included
- 出題された事があるらしい・・マジかよ
- インクルード直前と直後に呼び出される
module M
class << self
def append_features(klass)
p "before include in #{klass}"
end
def included(klass)
p "after include in #{klass}"
end
end
end
include M
# => "before include in Object"
# => "after include in Object"
Enumerable#lazy (Enumerable::lazy) のお話
#force | 遅延評価を開始する(実は#to_aと同じ) |
#take(n) | アタマからn個が対象だよ、と言うが遅延評価状態のままlazyオブジェクトを返す |
#first(n) | アタマからn個が対象だよ、と言って遅延評価を開始する |
正規表現
最短マッチというものがある、ちょっと小耳に挟んでおく
正規表現は全力で最長マッチするように働く
url = 'http://example.com/moko/hage/'
/^http:\/\/example.com\/(.*)\// === url
$1
# => "moko/hage"
のを、「全力は出さなくていいよ、条件に合う中で一番短いマッチをしてくれればいいよ」なのが最短マッチである
# ? が1つ追加されてる
url = 'http://example.com/moko/hage/'
/^http:\/\/example.com\/(.*?)\// === url
$1
# => "moko"
もう一つ例
'_12_3456_' =~ /\d{3,}/
p $&
# => 3456
'_12_3456_' =~ /\d{3,}?/
p $&
# => 345
Fiber
- Fiber は作成した時点では全く動かない
- f.resume により、Fiber.yield まで動作する
覚えるのはコレだけ!
Fiber.new {} |
Fiber.yield |
y.resume |
f = Fiber.new do
p 'これが表示されたという事は、初回の resume が実行されたという事'
Fiber.yield('戻り値 1')
p 'これが表示されたという事は、再度 resume が実行されたという事 まぁ、実行されないんですけどね'
Fiber.yield('戻り値 2')
'I am end'
end
p 'START'
sleep 1
p '一息ついてコレが表示される'
p f.resume
#=> "START"
#=> "一息ついて Fiber.yield の戻り値が表示される。これが表示されたという事は、初回の resume が実行されたという事"
#=> "戻り値 1"
IOのクラスツリー
UDPServer は存在しない。そんなもの ウチには ないよ |
StringIO は IO のようなインターフェースを持つが、IO のサブクラスではない |
- IO
- BaseSocket
- IPSocket
- TCPSocket
- TCPServer
- SOCKSSocket
- UDPSocket
- TCPSocket
- UNIXSocket
- UNIXServer
- Socket
- IPSocket
- BaseSocket
Time, Date, DateTime
- require ‘date’ で Date と DateTime が使えるようになる
- Time は内部的に Float を保持している
- Date, DateTime は内部的に Rational を保持している
- Date, DateTime #to_f メソッドは持っていない(あ、そうなんだ・・程度)
- Date, DateTime #new メソッドでは現在時間でオブジェクトが作成されるわけではない(あ、そうなんだ・・程度)
- ↑Date#today, DateTime.now を使う
- Dateはロケールを持っていない?のでクソ
四則演算
- Time と (Date, DateTime) は演算できない
- Time と Time の差はミリ秒差を Float で取得できる
- (Date, DateTime) と (Date, DateTime) の差を Rarional で取得できる ※(一日を 0〜0.99 で表しているので注意)
require 'date'
datetime1 = DateTime.now
sleep 1
datetime2 = DateTime.now
p (datetime2 - datetime1).to_f * 86400
=> 1.002833
Date, DateTime で出題される気がするメソッド
- 1 | 前の日 |
+ 1 | 次の日 |
« 1 | 前の月 |
» 1 | 次の月 |
ちなみに
Time.now + 1 | 1秒後 |
Time.now « 1 | エラー |
何度でも 特殊変数 おさらい
$0, $1, $2… $_ は出題される気がする
$0 | 実行中のプログラムファイル名 |
$1 | マッチした1番目のキャプチャ |
$2.. | マッチした2番目のキャプチャ.. |
$+ | マッチした最後のキャプチャ ($~.to_a.last == $+) => true |
$~ | MatchDataオブジェクト 配列っぽくアクセスできる ($~[0] => マッチ箇所 $~[1] => キャプチャ1) |
$` | マッチした部分より前の文字列 |
$& | マッチした部分 ($~.to_a.first == $&) => true |
$’ | マッチした部分より後の文字列 |
$_ | 最後にgetsやreadlineで読み込んだ文字列 |
$: | $LOAD_PATH |
$* | ARGV |
$? | 最後に終了した子プロセス |
$! | 直近で補足した例外オブジェクト |
$@ | バックトレース |
ちなみにネストしたキャプチャは始まった順番に重複して取得可能
/(\d(\d(\d)(\d))\d)/ === '12345'
#=> true
p $1 # => "12345"
p $2 # => "234"
p $3 # => "3"
p $4 # => "4"
def Module#define_method
- def
- def 新しいメソッド名; 定義; end
- 直接メソッド名を指定する
- Module#alias_method
- クラスの中で呼び出す => 特異メソッド呼び出し => 特異クラスの祖先にはModuleが居る => Module#define_method が呼び出せる
- define_method メソッド名 { 定義 }
- シンボルか文字列で指定する
- 直接メソッド名は指定できない
class Moko
define_method :hage do
'hage-!'
end
end
p Moko.new.hage
=> "hage-!"
alias Module#alias_method
- alias
- alias 新しい名前 古い名前
- カンマは要らない
- シンボルか直接メソッド名を指定する
- 文字列は指定できない
- Module#alias_method
- クラスの中で呼び出す => 特異メソッド呼び出し => 特異クラスの祖先にはModuleが居る => Module#alias_method が呼び出せる
- alias_method 新しい名前, 古い名前
- メソッドの引数なのでカンマが要る
- シンボルか文字列で指定する
- 直接メソッド名は指定できない
class Moko
def moko
'moko'
end
alias :old_moko :moko
end
Moko.new.old_moko
# => "moko"
class Moko
def moko
'moko'
end
alias old_moko moko
def moko
'new_moko'
end
end
Moko.new.old_moko
# => "moko"
Moko.new.moko
# => "new_moko"
class Moko
def moko
'moko'
end
alias_method 'old_moko', 'moko'
def moko
'new_moko'
end
end
p Moko.new.moko
undef Module#undef_method
- undef されたメソッドはメソッド探索を中止する
- つまり、メソッドが(消されて)見つからないとしてもスーパークラスの同名メソッドを呼び出そうとはしない
- 探索を中止せずにメソッド探索を続けたいなら undef ではなく remode_method を使う
- undef されたメソッドをその後再定義すると普通に呼びさせる
- undef
- undef メソッド名
- 複数指定できる カンマは要らない
- シンボルか直接メソッド名を指定する
- 文字列は指定できない
- Module#undef_method
- クラスの中で呼び出す => 特異メソッド呼び出し => 特異クラスの祖先にはModuleが居る => Module#undef_method が呼び出せる
- undef_method メソッド名
- 複数指定できる カンマが要る
- シンボルか文字列で指定する
- 直接メソッド名は指定できない
class A
def a
'A'
end
end
class B < A
def a
'B'
end
undef_method :a
end
p B.new.a
# => undefined method 'a'
Module#remove_method
- クラスの中で呼び出す => 特異メソッド呼び出し => 特異クラスの祖先にはModuleが居る => Module#remove_method が呼び出せる
- 探索を続けずにmethod_missingにしたいなら remove ではなく undef を使う
- remove されたメソッドをその後再定義すると普通に呼びさせる
- remove_method メソッド名
- 複数指定できる カンマが要る
- シンボルか文字列で指定する
- 直接メソッド名は指定できない
- メソッドが(消されて)見つからないのでスーパークラスの同名メソッドを呼び出してくれる
class A
def a
'A'
end
end
class B < A
def a
'B'
end
remove_method :a
end
p B.new.a
# => "A"
クラス変数
あーやべえ ルールわかんねえ
- 同じクラス変数を参照する仲間たち
- インスタンス
- 特異クラス
- サブクラス
- サブクラスの特異クラス
- サブクラスのインスタンス
- 仲間じゃないのか・・残念・・な感じ
- インスタンスの特異クラス
- サブクラスのインスタンスの特異クラス
class Moko1
p @@kurasu = 1
class << self
p @@kurasu += 1
end
def insmethod
p @@kurasu += 1
end
end
class Moko2 < Moko1
p @@kurasu += 1
end
moko1 = Moko1.new
moko2 = Moko2.new
moko1.insmethod
moko2.insmethod
def moko1.tokui_method
p @@kurasu += 1
end
moko1.tokui_method
# =>NameError: uninitialized class variable @@kurasu in Object
sort が出題される
- <=> と (a - b) の違い(というか「同じ事をしている」という意識)
- ( ・`ω・´) <= sortさん
- 「左辺が大きかったら」
- a <=> b が 1以上
- (a - b) が 1以上
- 「左辺と右辺を入れ替えないとね!」
- 「左辺が大きかったら」
[1, 3, 2].sort { |a, b| a <=> b }
# => [1, 2, 3]
[1, 3, 2].sort { |a, b| a - b }
# => [1, 2, 3]
おとなしく sort_by 使ってくれればいいのに、いじわるでsortが出題される
[1, 3, 2].sort_by { |a| a }
# => [1, 2, 3]
YAML
やむるはろーど!だんぷ!
- YAML.load(yaml_string)
- YAML.dump(object, io = STDOUT)
require 'yaml'
yaml_string = <<YAML_STRING
moko:
- hoge1
- hoge2
- hoge3:
- hoge3-1
- hoge3-2
YAML_STRING
yaml_data = YAML.load(yaml_string)
p yaml_data
# => {"moko"=>["homo1", "homo2"]}
p YAML.dump(yaml_data)
# => "---\nmoko:\n- homo1\n- homo2\n"
基本的なルール
とりあえずハッシュと配列があればほぼイケる
ハッシュ
yaml_string = <<-YAML_STRING
moko: hoge
YAML_STRING
YAML.load(yaml_string)
# => {"moko"=>"hoge"}
配列
yaml_string = <<-YAML_STRING
- hoge1
- hoge2
- hoge3
YAML_STRING
YAML.load(yaml_string)
=> ["hoge1", "hoge2", "hoge3"]
組み合わせ
yaml_string = <<-YAML_STRING
moko:
- hoge1
- hoge2
- hoge3:
- hoge3-1
- hoge3-2
YAML_STRING
YAML.load(yaml_string)
=> {"moko"=>["hoge1", "hoge2", {"hoge3"=>["hoge3-1", "hoge3-2"]}]}
JSON
じぇいそんはろーど!ぱーす!だんぷ!
- JSON.load(json_string)
- JSON.parse(json_string)
- JSON.dump(json_object, io = nil)
- Hash#to_json
- Array#to_json
require 'json'
json_string = <<-JSON_STRING
{"homo": true, "homos": ["a", "b"]}
JSON_STRING
p json_object = JSON.load(json_string) # => {"homo"=>true, "homos"=>["a", "b"]}
p JSON.parse(json_string) # => {"homo"=>true, "homos"=>["a", "b"]}
p JSON.dump(json_object) # => "{\"homo\":true,\"homos\":[\"a\",\"b\"]}"
p ({moko: [1, 2, 3]}.to_json) # => "{\"moko\":[1,2,3]}"
p ([1,2,3, {moko: 1}].to_json) # => "[1,2,3,{\"moko\":1}]"
基本的なルール
とりあえずハッシュと配列があればほぼイケる
ハッシュ
json_string = <<-JSON_STRING
{"moko1": "hoge"}
JSON_STRING
JSON.load(json_string)
# => {"moko1"=>"hoge"}
配列
json_string = <<-JSON_STRING
[
"moko1",
"moko2",
"moko3"
]
JSON_STRING
JSON.load(json_string)
# => ["moko1", "moko2", "moko3"]
組み合わせ
json_string = <<-JSON_STRING
{
"moko1":[
"hoge1",
"hoge2",
{
"hoge3": [
"hoge3-1",
"hoge3-2"
]
}
]
}
JSON_STRING
JSON.load(json_string)
# => {"moko1"=>["hoge1", "hoge2", {"hoge3"=>["hoge3-1", "hoge3-2"]}]}
Object#respond_to? のおはなし
- デフォルトでは publicなメソッドのみtrueを返す(Ruby2.0からこの仕様)
- object.respond_to?(:public_hage) # => true
- object.respond_to?(:protected_hage) # => false
- object.respond_to?(:private_hage) # => false
- 第2引数にtrueを渡すとアクセス制御にかかわらず動作するようになる(Ruby1.9までの仕様)
- object.respond_to?(:public_hage, true) # => true
- object.respond_to?(:protected_hage, true) # => true
- object.respond_to?(:private_hage, true) # => true
ancestorsも含めて検索対象である (通常のメソッド呼び出しと同じ感覚)
class Oyaji
def hoge
end
end
class Moko < Oyaji
end
p Moko.new.respond_to?(:hoge)
# => true
undef, remove すると false (通常のメソッド呼び出しと同じ感覚)
class Moko
def hage1
end
def hage2
end
end
p Moko.new.respond_to?(:hage1)
# => true
class Moko
undef_method :hage1
remove_method :hage2
end
p Moko.new.respond_to?(:hage1)
# => false
p Moko.new.respond_to?(:hage2)
# => false
定数の探索経路をおさらい
通常、自分のクラスで定数が見つからなければ親クラスに探しに行く
class Oyaji
CONST = 'OYA_CONST'
end
class Moko < Oyaji
CONST = 'C_CONST'
def hage
CONST
end
end
p Moko.new.hage
# => "C_CONST"
class Oyaji
CONST = 'OYA_CONST'
end
class Moko < Oyaji
def hage
CONST
end
end
p Moko.new.hage
# => "OYA_CONST"
include はselfの直後に追加(後から処理した奴が優先されるという事)
- 注意!
- include M1, M2
- というように複数指定した時には先に記述した方が後から追加したように動作する (self => M1 => M2 の順番に配置される)
prepend はselfの末端に追加(後から処理した奴が優先されるという事)
- 注意!
- prepend M1, M2
- というように複数指定した時には先に記述した方が後から追加したように動作する (M1 => M2 => self の順番に配置される)
extend はselfの特異クラスの末端追加(後から処理した奴が優先されるという事)
- 注意!
- extent M1, M2
- というように複数指定した時には先に記述した方が後から追加したように動作する (#M1 => #M2 の順番に配置される)
include, prepend したModule内のメソッド内に定数が定義してあると取り込んだクラスの定数になる
extend は関係なし ancestorsルート関係ないしって事か
module II
I = true
end
module PP
P = true
end
module EE
E = true
end
class Moko
include II
prepend PP
extend EE
end
Moko::I
# => true
Moko::P
# => true
Moko::E
# => NameError: uninitialized constant Moko::E
- include, prepend, extend したModule内のメソッド内に定数へのアクセスが記述してあると
- 自らのモジュールからの相対位置へ定数を検索しに行き、見つからないと嘆く事に注意
- 親クラスに記述してある定数へのアクセスは、親クラスの定数を探しに行く。 const_get を使うとselfから探してくれる
- 大事な事なのでもう一度、モジュール内のメソッドから定数を参照すると、自モジュールの参照している行から検索を開始する
module IncleM
def incle1
CONST
end
end
module PrepenM
def prepen1
CONST
end
end
module ExtenM
def exten1
CONST
end
end
class Moko
CONST = 'C_CONST'
include IncleM
prepend PrepenM
extend ExtenM
end
moko = Moko.new
moko.prepen1
# => NameError: uninitialized constant PrepenM::CONST
moko.incle1
# => NameError: uninitialized constant IncleM::CONST
Moko.exten1
# => NameError: uninitialized constant ExtenM::CONST
superメソッドがスーパークラスのメソッドを見つける時
普通に継承したクラスでのsuperを使用
class Moko1
def hage
p 'Moko1#hage'
end
end
class Moko2 < Moko1
def hage
p 'Moko2#hage'
super
end
end
m2 = Moko2.new
m2.hage
# => "Moko2#hage"
# => "Moko1#hage"
サブクラスのメソッド名を変更してみると・・
- Moko2#hogeメソッドはきれいさっぱり消されたので親クラスのhogeメソッドを呼びに行く、ここまでは普通だが
- なぜ Moko2#hoge2メソッドが superを呼んだ結果、Moko#hogeが呼び出されているのか??
- と、まぁメソッド名はメソッドへのポインタに過ぎず、定義時に既にsuperはMoko1#hogeだと決まっていて、実行時にメソッド名によって動的にスーパーメソッドが呼ばれるわけではない、という事なんだなきっと
class Moko1
def hage
p 'Moko1#hage'
end
end
class Moko2 < Moko1
def hage
p 'Moko2#hage'
super
end
alias_method :hage2, :hage
remove_method :hage
end
m2 = Moko2.new
m2.hage
# => "Moko1#hage"
m2.hage2
# => "Moko2#hage"
# => "Moko1#hage"
- てことは、別のメソッド名から同じ名前へメソッド名が変更された、という状況でもスーパークラスは呼び出せないという事か?
- ・・そのようだ まぁ方法はあるんだろうけど
class Moko1
def hage
p 'Moko1#hage'
end
end
class Moko2 < Moko1
def hage2
p 'Moko2#hage'
super
end
alias_method :hage, :hage2
remove_method :hage2
end
m2 = Moko2.new
m2.hage
# => "Moko2#hage"
# => super: no superclass method 'hage2'
- 次に親クラスのメソッド名を買えた場合はどうなんだ?
- ・・呼び出せた、がこれは Moko2#hogeを定義する時には既に Moko1#hogeは名前が変わっていたので通常の動作とそんなに変わらない
- では、Moko2#hogeを定義してからMoko1#hogeのメソッド名を変更したらどうなるか・・試してみたがどうやってもsuperは追随してきた
- 親クラスでどれだけメソッドを書き換えても、サブクラスはそのsuperというポインタにて、古いスーパーメソッドを保持し続けているという事だと思う
class Moko1
def hage2
p 'Moko1#hage2'
end
alias_method :hage, :hage2
remove_method :hage2
end
class Moko2 < Moko1
def hage
p 'Moko2#hage'
super
end
end
m2 = Moko2.new
m2.hage
# => "Moko2#hage"
# => "Moko1#hage2"
exit とは
「そこでプログラムを終了する」ではなく SystemExit < Exception 例外を発生させる
begin
exit
rescue SystemExit => e
p e
p e.class
p e.class.superclass
end
# => #<SystemExit: exit>
# => SystemExit
# => Exception
ちなみに exit! は例外も発生させずにマジ終了する
begin
exit!
rescue Exception => e
p e
p e.class
p e.class.superclass
end
全ての例外を捉えるはずの Exception であっても rescue できずに終了する
トップレベルのメソッドを定義するということ
- class 定義などの外では、トップレベルに居る、という状態になる
- トップレベルは 「Objectクラスのインスタンスである、main に居る」状態である
- そのため、トップレベルでは self => main となる
- トップレベルでメソッドを定義する、
- という事は「Objectクラスのインスタンスである main が self である状態でメソッドを定義する」
- つまり「Objectクラスのインスタンスである main にメソッドを定義する」という事である
- 通常は privateメソッドとして定義されるが irb では publicメソッドになるので注意
- irbの場合
-
Object.public_instance_methods(false) # => [] p Object.private_instance_methods(false) # => [:DelegateClass, :default_src_encoding, :irb_binding] def moko true end Object.public_instance_methods(false) # => [:moko] p Object.private_instance_methods(false) # => [:DelegateClass, :default_src_encoding, :irb_binding]
- 通常スクリプトの場合
-
p Object.public_instance_methods(false) # => [] p Object.private_instance_methods(false) # => [:DelegateClass] def moko true end p Object.public_instance_methods(false) # => [] p Object.private_instance_methods(false) # => [:DelegateClass, :moko]
- あまり重要ではないかもしれないが、Object のインスタンスにメソッドを定義するという事は全てのオブジェクトにメソッドを追加したのと同じという事でもある
-
def hage 'hage-!' end hage => "hage-!" :aaa.hage => "hage-!"
RDOCの書式が出題される
書けば覚えるだろうけど・・
Marshal.dump で文字列化できないオブジェクト
- 無名のクラス、モジュール
- システムがオブジェクトの状態を保持するようなIOクラス、Dirクラス、Fileクラスなど
- MatchDataクラス、Procクラス、Threadクラスのようなもの
- 上記のオブジェクトを使っているオブジェクト
- たとえば特異クラスを持つクラスは無理(特異クラスは無名なので無理)
- ちなみにオブジェクトに戻すには Marshal.load を使うよ
クラスやモジュールをfreezeするとどうなる
まずは、できるのか? => できます
class A
end
module M
end
A.freeze
# => A
M.freeze
# => M
- インスタンスの freeze は
- インスタンス変数の変更
- 特異メソッドの定義
- 諸々ができなくなる
-
class A def moko @moko = 1 end end a = A.new a.freeze a.moko # => can't modify frozen A def a.hoge end # => can't modify frozen object class << a def hoge end end # => RuntimeError: can't modify frozen object
- 使い慣れたインスタンスも同様に
-
hash = {a: 1} hash.freeze hash[:a] = 1 # => can't modify frozen Hash array = [1, 2] array.freeze array[0] = 1 # => can't modify frozen Array
- クラスインスタンス、モジュールインスタンスの freeze は
- クラス変数の変更
- クラスインスタンス変数の変更
- インスタンスメソッドの定義
- 特異メソッドの定義、特異クラスを使ったメソッド定義
- 諸々ができなくなる
-
class A def moko1 @moko1 = 1 end def moko2 @@moko2 = 2 end class << self def moko3 @moko3 = 3 end def moko4 @@moko4 = 4 end end end A.freeze p A.new.moko1 # aオブジェクトは freeze されてないからおk # => 1 p A.new.moko2 # => can't modify frozen #<Class:A> p A.moko3 # => can't modify frozen #<Class:A> p A.moko4 # => can't modify frozen #<Class:A> def A.hoge end # => can't modify frozen Class class << A def hoge end end # => can't modify frozen Class
-
クラスインスタンス変数に触れるのはクラス定義内とクラスメソッドからのみ
- ・・はず