ちょっと便利なことだったり,今さら知った恥ずかしいことなど
小文字または`_'で始まる識別子はローカル変数また はメソッド呼び出しです。ローカル変数スコープ(クラス、モジュー ル、メソッド定義の本体)における小文字で始まる識別子への最初 の代入はそのスコープに属するローカル変数の宣言になります。宣 言されていない識別子の参照は引数の無いメソッド呼び出しとみな されます。 ローカル変数のスコープは、((宣言した位置から))その変数が宣 言されたブロック、メソッド定義、またはクラス/モジュール定義 の終りまでです。寿命もそのブロックの終りまで(トップレベルの ローカル変数はプログラムの終了まで)ですが、例外としてブロッ クが手続きオブジェクト化された場合は、そのオブジェクトが消滅 するまで存在します。同じスコープを参照する手続きオブジェクト 間ではローカル変数は共有されます。
-
「ローカル変数のスコープは,宣言した位置からその変数が宣言された ブロック,メソッド定義,またはクラス/モジュール定義の終わりまで」なので,if ~ elseとかbegin ~ rescueはローカル変数のスコープを作らない
-
なのでいちいち,beginやifの中で使う変数をその処理の前で初期化する必要はない
# はじめにfを定義しておく必要はない
#f = nil
begin
f = File.open("/path/to/file")
rescue => e
puts "ファイルを開けません"
raise
end
puts f.read
ユーザ一覧からそれぞれのメールアドレスを取るとき
users.map {|user| user.email }
とせずに,
users.map(&:email)
とかけますよというお話.
http://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fcall.html#block
ブロックの部分だけを先に定義して変数に保存しておき、後からブロック付きメソッドに渡すことも出来ます。 それを実現するのが手続きオブジェクト(Proc)です。 それをブロックとして渡すにはブロック付きメソッドの最後の引数として `&' で修飾した手続きオブジェクトを渡 します。Proc の代わりにメソッドオブジェクト(Method)を渡す ことも出来ます。この場合、そのメソッドを呼ぶ手続きオブジェクトが生成さ れ渡されます。
というわけで,mapなどでProcを渡すときはProcを渡すメソッドは以下のような形で渡せる
proc = Proc.new {|user| user.email }
emails = users.map(&proc)
さらには同URLにて
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。
とあるのでto_procメソッドを持っているオブジェクトを&で渡せば,暗黙的に&で渡したインスタンスをto_procしてproc化される.実はSymbolオブジェクトにはto_procが定義されており,シンボル名と同名のメソッドを呼び出すProcオブジェクトを生成する. つまり下記1,2,3,4は結果としては同じものとなる(実装の詳細は違うかもしれないが)
# 1. 一番省略した形
users.map(&:email)
# 2 to_procをあえて明示的に書くとこんな感じ
users.map (&(:email.to_proc))
# 3 さらにprocを明示的に作ったらこんな感じ(emailメソッドは引数0なのでargsは実際は関係なし)
# proc = Proc.new {|x, *args| x.email(*args) }
proc = Proc.new {|x| x.email }
users.map (&proc)
# 4 Procをnewせずにブロックで直接指定した場合
users.map {|x| x.email }
基本的にrubyでの繰り返し操作はmapやeachなどのイテレータを用いるが,純粋に各要素を順番に取り出すとともに1,2,3,4などと数字を取り出したいときもある. そんな場合each_with_indexを使う
%w(a b c d e).each_with_index do |ch, index|
puts "#{index + 1}, #{ch}"
end
#出力
#1, a
#2, b
#3, c
#4, d
#5. e
でも0オリジンになってしまっているので,eachとは別にindexの底上げに気を使わなければいけない.そんなときはeach.with_indexがいい感じ
%w(a b c d e).each.with_index(1) do |ch, index|
puts "#{index}, #{ch}"
end
#出力
#1, a
#2, b
#3, c
#4, d
#5. e
まず,eachでEnumeratorオブジェクトが得られる.Enumeratorオブジェクトはwith_indexメソッドを持っており,指定した数値を起点とするインデックスをもとのEnumeratorに追加する.詳細はリファレンスを参照
メソッド全体を例外で補足する場合begin ~ rescue ~ endのbeginを省略できる 次の2つは同じ
class Hoge
def fuga
begin
# なんか処理
rescue => e
$stderr.puts e
raise
end
end
end
class Hoge
def fuga
# なんか処理
rescue => e
$stderr.puts e
raise
end
end
..は終端を含む...は終端を含まない
(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1...10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
Enumerableなオブジェクトに対しcycleを使うとよいcycleに引数を渡さないと無限列になるので注意
(1..5).cycle(3) # => #<Enumerator: ...>
(1..5).cycle(3).to_a
=> [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
- include? は内部的には===による比較, cover? は <=> による比較
http://docs.ruby-lang.org/ja/2.2.0/method/Range/i/cover=3f.html
# Arrayで継承+(include)されているクラス一覧
Array.ancestors #=> [Array, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
# Arrayにincludeされているクラス一覧
Array.included_modules #=> [Enumerable, PP::ObjectMixin, Kernel]
class Point
WIDTH_MAX = 640
HEIGHT_MAX = 320
@@class_variable1 = "とくに良い物がなかったので適当"
@@class_variable2 = 111111
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
def abs
raise "out of range" if out_of_range?
Math.sqrt(@x ** 2 + @y ** 2)
end
def to_s
raise "out of range" if out_of_range?
"x = #{@x}, y = #{@y}"
end
def self.max_abs
Math.sqrt(WIDTH_MAX ** 2 + HEIGHT_MAX ** 2)
end
def +(other)
Point.new(@x + other.x, @y + other.y)
end
private
def out_of_range?
@x > WIDTH_MAX || @y > HEIGHT_MAX
end
end
p = Point.new(100, 200)
# オブジェクトの変数
p.instance_variables #=> [:@x, :@y]
#p.instance_values #=> {"x"=>100, "y"=>200} Powerd by 'active_support'
# オブジェクトのmethod
p.public_methods(false) #=> [:x, :x=, :y, :y=, :abs, :to_s, :+]
p.protected_methods(false) #=> []
p.private_methods(false) #=> [:initialize, :out_of_range?]
# クラスの取得
p.class #=> #<Point:0x007fde4d13d890 @x=100, @y=200>
Point.class_variables #=> [:@@class_variable1, :@@class_variable2]
Point.public_methods(false) #=> [:max_abs, :allocate, :new, :superclass]
Point.ancestors #=> [Point, Object, PP::ObjectMixin, Kernel, BasicObject]
# methodいろいろ
p.method(:abs) #=> #<Method: Point#abs>
method = p.method(:+) #=> #<Method: Point#+>
method.receiver #=> #<Point:0x007fde4d13d890 @x=100, @y=200> (もとのオブジェクト)
method.parameters #=> [[:req, :other]] (see also: http://docs.ruby-lang.org/ja/2.2.0/method/Method/i/parameters.html)
method.source_location # ["path/to/source_file", line_number] が得られる
# pryだとcd, lsとかでcliライクにmethodやクラスを探せる
pry(main)> cd Point
pry(Point):1> ls
constants: HEIGHT_MAX WIDTH_MAX
Point.methods: max_abs
Point#methods: + abs to_s x x= y y=
class variables: @@class_variable1 @@class_variable2
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
pry(Point):1> cd # クラスツリーのルートに戻る
# ソース見れる
pry(Point):1> show-method abs
From: [ファイルのパスが入る] @ line [行数が入る]:
Owner: Point
Visibility: public
Number of lines: 4
def abs
raise "out of range" if out_of_range?
Math.sqrt(@x ** 2 + @y ** 2)
end