Last active
July 13, 2019 14:42
-
-
Save TomohikoSato/8c69f0650e5e84bb2a60be9a807ec9fc to your computer and use it in GitHub Desktop.
EffectiveRuby写経
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目13 "<=>"とComparableモジュールで比較を実装しよう | |
class Version | |
include(Comparable) # <=>が定義されていれば、 <, <=, =>, > といったComparableで定義されたメソッドが利用可能になる | |
attr_reader :major, :minor, :patch | |
alias_method(:eql?, :==) # Hashのキーの同一性にeql?とhashを使う | |
def initialize(version) | |
@major, @minor, @patch = version.split('.').map(&:to_i) | |
end | |
def <=> (other) | |
return nil unless other.is_a?(Version) | |
[ major <=> other.major, | |
minor <=> other.minor, | |
patch <=> other.patch].detect {|n| !n.zero?} || 0 | |
end | |
def hash | |
[major, minor, patch].hash | |
end | |
end | |
# オブジェクトの順序は、"<=>"演算子を定義し、Comparableモジュールをインクルードして実装しよう。 | |
hoge = %w(1.2.3 3.4.5 0.1.1).map {|v| Version.new(v) } | |
p hoge.sort # => [#<Version:0x00007ff49386fad0 @major=0, @minor=1, @patch=1>, #<Version:0x00007ff49386ffa8 @major=1, @minor=2, @patch=3>, #<Version:0x00007ff49386fd00 @major=3, @minor=4, @patch=5>] | |
p Version.new('1.2.3') < Version.new('2.3.4') # => true | |
# クラスのために"<=>"を実装した場合、特にインスタンスをハッシュキーとして使うつもりなら、eql?を"=="の別名にすることを検討しよう。別名にする場合には、hashメソッドもオーバーライドしなければならない。 | |
v1 = Version.new '1.2.3' | |
v2 = Version.new '1.2.3' | |
h = {} | |
h[v1] = 'good' | |
h[v2] = 'nice' | |
p h # => {#<Version:0x00007ff7e18733f0 @major=1, @minor=2, @patch=3>=>"nice"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目14 protectedメソッドを使ってプライベートな状態を共有しよう | |
class Widget | |
def initialize(screen_x, screen_y) | |
@screen_x = screen_x | |
@screen_y = screen_y | |
end | |
# 他のobjectと座標が重なっているかチェックする場合など | |
def overlapping?(other) | |
x1, y1 = @screen_x, @screen_y | |
# レシーバであるotherが、selfと同じクラスまたは共通のスーパークラスからprotectedメソッドを継承している場合に、protectedだと呼べる | |
# privateだと呼べない。privateはレシーバーを指定すして呼べない。 | |
x2, y2 = other.screen_coordinates | |
p "x1 #{x1}: y1 #{y1}" # => "x1 2: y1 3" | |
p "x2 #{x2}: y2 #{y2}" # => "x2 10: y2 20" | |
end | |
protected | |
def screen_coordinates | |
[@screen_x, @screen_y] | |
end | |
end | |
w1 = Widget.new(2, 3) | |
w2 = Widget.new(10, 20) | |
w1.overlapping? w2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目15 クラス変数よりもクラスインスタンス変数を使うようにしよう | |
# クラス変数を利用 | |
## グローバル変数みたいなもん | |
class Singleton | |
private_class_method(:new, :dup, :clone) | |
def self.instance | |
@@single ||= new | |
end | |
end | |
class Configuration < Singleton; end | |
class Database < Singleton; end | |
p Configuration.instance # => #<Configuration:0x00007f9e610e9230> | |
p Database.instance # => #<Configuration:0x00007f9e610e9230> NG!! | |
# クラスインスタンス変数を利用 | |
## クラス定義ごとにユニークになるので、上述の継承の問題を解決できる | |
class Singleton | |
private_class_method(:new, :dup, :clone) | |
def self.instance | |
@single ||= new | |
end | |
end | |
class Configuration < Singleton; end | |
class Database < Singleton; end | |
p Configuration.instance # => #<Configuration:0x00007fe66681d400> | |
p Database.instance # => #<Database:0x00007fe66681d040> | |
# シングルトンパターンの実現にはSingletonモジュールを利用すれば十分 | |
require "singleton" | |
class SingletonClass | |
include Singleton | |
end | |
class Configuration < SingletonClass; end | |
class Database < SingletonClass; end | |
p Configuration.instance # => #<Configuration:0x00007fb72584cba8> | |
p Database.instance # => #<Database:0x00007fb72584c9a0> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目17 nil、スカラーオブジェクトを配列に変換するには、Arrayメソッドを使おう | |
class NGPizza | |
def initialize toppings # *で可変長引数 | |
toppings.each do |topping| | |
p topping | |
end | |
end | |
end | |
class Pizza | |
def initialize toppings # *で可変長引数 | |
Array(toppings).each do |topping| | |
p topping | |
end | |
end | |
end | |
ng_p1 = NGPizza.new [4, 8, 10, 13] | |
ng_p2 = NGPizza.new nil # undefined method `each' for nil:NilClass (NoMethodError) | |
p1 = Pizza.new [4, 8, 10, 13] | |
p2 = Pizza.new nil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目18 要素が含まれているかどうかの処理を効率よく行うために集合を使うことを検討しよう | |
# Setは標準ライブラリ requireする必要がある | |
# Array, Hash, Range はコアライブラリ requireする必要はない。リテラルからオブジェクトを作るための専用構文もある | |
# [a, b, c] | |
# {p: q} keyがシンボルの場合JSONのように記述できる {:p => q} | |
# .. 終端含める ... 含めない | |
# 権限の例 | |
# include? は Enumerableモジュールのメソッド どこで使用されているかはRubyMineでFindUsagesすれば一覧できる | |
# Hashの要素へのアクセスはO(logn) と書いてあるが、一般にハッシュテーブルはO(1)では? | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目19 reduceを使ってコレクションを畳み込む方法を身に付けよう | |
# map, select, reduceと言った重要なメソッドはEnumerableに属する | |
# reduceのレシーバが空の場合、アキュムレータの初期値がnilになる | |
## accumulate .. 蓄積 accumulaterにブロックの計算結果を繰り返しaccumulateしていくイメージ(?) | |
def sum enum | |
enum.reduce(0) do |acc, element| | |
acc + element | |
end | |
end | |
def ngsum enum | |
enum.reduce do |acc, element| | |
acc + element | |
end | |
end | |
p sum [1,2,3,4,5] # 15 | |
p sum [] # 0 | |
p ngsum [1,2,3,4,5] # 15 | |
p ngsum [] # nil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目6 Rubyが継承階層をどのように組み立てるかを頭に入れよう | |
module ThingsWithNames | |
def name | |
p 'things' | |
end | |
end | |
class Person | |
include(ThingsWithNames) | |
def name | |
p 'person' | |
end | |
end | |
Person.new.name #person |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目7 superのふるまいがひと通りではないことに注意しよう | |
# superはメソッドではなくキーワード | |
## 引数も括弧もつけずにsuperキーワードを呼ぶと、呼び出し元のメソッドと同じ引数をスーパークラスに渡してオーバーライドするメソッドを呼ぶ | |
class Base | |
def m1(x, y) | |
p 'base' | |
end | |
end | |
class Derived < Base | |
def m1 (x) | |
p 'derived' | |
super(x, 'b') # 'base' | |
super x, 'b' # 'base' | |
super # wrong number of arguments (given 1, expected 2) (ArgumentError) | |
super() # wrong number of arguments (given 0, expected 2) (ArgumentError) | |
end | |
end | |
Derived.new().m1('a') | |
Derived.new().m1('a', 'b') # wrong number of arguments (given 2, expected 1) (ArgumentError) | |
# moduleもsuperに入る | |
module CoolFeatures | |
def feature_a | |
p 'a' | |
end | |
end | |
class Vanila | |
include(CoolFeatures) | |
def feature_a | |
super # a | |
end | |
end | |
Vanila.new.feature_a |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 項目9 Rubyの最悪に紛らわしい構文に注意しよう | |
class SetMe | |
def initialize | |
@value = 0 | |
end | |
def value | |
@value | |
end | |
def value= (x) | |
@value = x | |
end | |
end | |
s = SetMe.new | |
p s.value # 0 | |
s.value = 3 | |
p s.value # 3 | |
class Counter | |
attr_accessor(:counter) | |
def initialize | |
counter = 0 #ローカル変数counterを作成し0に代入。インスタンス変数には代入されない。 | |
end | |
end | |
c = Counter.new | |
p c.counter # nil | |
class Counter2 | |
attr_accessor(:counter) | |
def initialize | |
self.counter = 0 | |
end | |
end | |
c2 = Counter2.new | |
p c2.counter # 0 | |
class Name | |
attr_accessor(:first, :last) | |
def initialize(first, last) | |
self.first = first | |
self.last = last | |
end | |
# Getterにselfは不要 | |
def full | |
self.first + " " + self.last | |
end | |
def full2 | |
first + " " + last | |
end | |
end | |
name = Name.new('Tomohiko', 'Sato') | |
p name.full # "Tomohiko Sato" | |
p name.full2 # "Tomohiko Sato" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment