ナスカブログ

未来の自分へのドキュメント

Ruby Goldへの道 day1

以下のサイトでruby gold取得に向けて毎日一回Goldチャレンジを行い間違えた問題を簡単にまとめる。 rex.libertyfish.co.jp

簡単な自己紹介

  • 2019年10月から某プログラミングスクールでプログラミング学習開始
  • 2020年4月から都内のRuby受託会社に入社
  • 実務経験半年(内2ヶ月社内研修)
  • Ruby silverは取得済み

1日目 得点 32点/100点中

50問中16問正解。逆に言うと34問間違い。。。
間違えた問題について復習する

  • selfはnewされたオブジェクトのクラス
class Fuga
  def initialize
    p self.class
  end
end

class Hoge < Fuga
end

Hoge.new #=> Hoge
  • 特異クラス内で宣言されたメソッドは特異メソッド
class Hoge
  class << Hoge
    def fuga
      '特異メソッドだよん'
    end
  end

  def fuga
    'インスタンスメソッドだよん'
  end
end

p Hoge.new.fuga #=> "インスタンスメソッドだよん"
p Hoge.fuga #=> "特異メソッドだよん"
  • -t, -fオプションはない

  • オープンクラスによる影響をローカルにとどめる為にRefinementがある
    RefinementはModule#refineで呼び出すことができModule#usingで定義したRefinementを有効化できる

class Hoge
  def fuga
    200
  end
end

module R
  refine Hoge do # Refinementを呼び出す
    def fuga
      100
    end
  end
end


puts Hoge.new.fuga #=> 200

using R # Refinementを有効か

puts Hoge.new.fuga #=> 100
  • Procはcallの際に引数の数を省略され不足の引数にはnilが代入される
local = 0

p1 = Proc.new { |arg1, arg2|
  arg1, arg2 = arg1.to_i, arg2.to_i
  local += [arg1, arg2].max
}

p1.call("1", "2") #=> 2
p1.call("7", "5") #=> 2 + 7 = 9
p1.call("9") #=> 9 + 9 = 18

p local # => 18
  • 定数はレキシカルに決定される
    レキシカル...静的??
module M1
  class C1
    CONST = "class_C1"
  end

  class C2 < C1
    CONST = "class_C2"

    module M2
      CONST = "module_M2"

      class Ca
        CONST = "class_Ca"
      end

      class Cb < Ca
        p CONST #=> "module_M2"
      end
    end
  end
end
  • ブロック引数は仮引数の中で最後に記述する
def hoge(&block, *args)
  block.call(*args)
end

hoge(1,2,3,4) do |*args|
  p args.length > 0 ? "hello" : args
end
  • initializeはpublicなどでアクセス修飾子をつけたとしても、privateから変わることはない。
class Hoge
  public
  def initialize
  end
end
  
p Hoge.new.private_methods.include? :initialize #=> true
  • loadはrequire同様に外部ライブラリを読み込む
    外部ライブラリではなくモジュールを読み込むとエラーになる
module M
  def foo
    super
    puts "M#foo"
  end
end

class C2
  def foo
    puts "C2#foo"
  end
end

class C < C2
  def foo
    super
    puts "C#foo"
  end
  load M
end

C.new.foo #=> no implicit conversion of Module into String (TypeError)
  • method_missingは継承チェーンをたどった末にメソッド が見つからなかったら呼び出される
module M
  def method_missing(id, *args)
    puts "M#method_missing"
  end
end
class A
  include M
  def method_missing(id, *args)
    puts "A#method_missing"
  end
end
class B < A
  def method_missing(id, *args)
    puts "B#method_missing"
  end
end

obj = B.new
obj.dummy_method #=> B#method_missing
  • クラス/モジュールに定義されているクラス変数 name の値を返す
class S
  @@val = 0
  def initialize
    @@val += 1
  end
end

class C < S
  class << C
    @@val += 1
  end

  def initialize
    @@val += 1
    super
  end
end

C.new
C.new
S.new
S.new

p C.class_variable_get(:@@val) #=> 7
  • キーワード引数は省略することができない
def foo(arg:)
  puts arg
end

foo 100 #=> wrong number of arguments (given 1, expected 0; required keyword: arg) (ArgumentError)
  • クラス変数が更新されるタイミング
    クラスメソッドが定義された
    C.newが呼び出された
    superからCのinitializeを呼び出された
    S.newが呼び出された
class S
  @@val = 0
  def initialize
    @@val += 1
  end
end

class C < S
  class << C
    @@val += 1
  end
end

C.new
C.new
S.new
S.new

p C.class_variable_get(:@@val) #=> 5
  • Dateクラス同士の減算はRationalになる
require 'date'
d = Date.today - Date.new(2015,10,1)
p d.class #=> Rational
  • Procオブジェクトをメソッドで実行するにはブロックに変換する必要がある
val = 100

def method(val)
  yield(15 + val)
end

_proc = Proc.new{|arg| val + arg }

p method(val, &_proc.to_proc) #=> 215

# &_proc.to_procと&_procはブロックに変換できる
  • ブロック変数に可変長引数はそのまま受け取る
def hoge(*args, &block)
  block.call(args)
end

hoge(1,2,3,4) do |*args|
  p args.length < 0 ? "hello" : args #=> [[1, 2, 3, 4]]
end
  • each_charとto_enum(:each_char)は同じ結果が返ってくる
enum = "apple".to_enum(:each_char)
# enum = "apple".each_char

p enum.next #=> "a"
p enum.next #=> "p"
p enum.next #=> "p"
p enum.next #=> "l"
p enum.next #=> "e"
  • 特異クラス定義の中でクラス変数を定義してもレキシカルに決定される
class C
  @@val = 10
end

module B
  @@val = 30
end

module M
  include B
  @@val = 20

  class << C
    p @@val #=> 20
  end
end
  • :fishはSymbolクラスのオブジェクト
begin
  print "liberty" + :fish.to_s
rescue TypeError
  print "TypeError."
rescue
  print "Error."
else
  print "Else."
ensure
  print "Ensure."
end

#=> libertyfishElse.Ensure.
  • andは左辺が真であれば、右辺の結果を返します。左辺が偽であれば、左辺の結果を返す
    優先順位が低いのでputs v2が評価される
p v1 = 1 / 2 == 0 #=> true
p v2 = !!v1 or raise RuntimeError #=> true
puts v2 and false #=> true
  • __method__はメソッドの中で呼び出すとそのメソッドに名になる
def awesome_method
  __method__
end

p awesome_method #=> :awesome_method
  • 引数名に&を付与することでブロック引数になる
def bar(&block)
  block.yield
end

bar do
  puts "hello, world" #=> hello, world
end
  • Module.nestingはネストの状態を表示する
module SuperMod
  module BaseMod
    p Module.nesting #=> [SuperMod::BaseMod, SuperMod]
  end
end
  • superを実行した場合にもRefinementが影響する
class C
end

module M
  refine C do
    def m1(value)
      super value - 100 # 300 - 100
    end
  end
end

class C
  def m1(value)
    value - 100 # 200 - 100
  end
end

using M

class K < C
  def m1(value)
    super value - 100 # 400 - 100
    # Refinementが有効なのでsuperはモジュールMにあるm1を参照する
  end
end

puts K.new.m1 400 #=> 100
  • String#scanはマッチした部分文字列を配列で返す
p "Matz is my tEacher".scan(/[is|my]/).length #=> 4
  • attr_accessorはattr_readerとattr_writerを同時に定義したもの
class C
  attr_accessor :v # ここ

  # 同じ
  def v=(other)
    @v = other
  end
  def v
    @v
  end

  # 同じ
  attr_reader :v
  attr_writer :v
end

c = C.new
c.v = 100
p c.v

class C
  
end
class Human
  NAME = "Unknown"

  def name
    NAME
  end
end

class Noguchi < Human
  NAME = "Hideyo"
end

puts Noguchi.new.name #=> Unknown
  • between?で値を比較するためには、Comparableをincludeする必要がある
# falsetrueになるように
class Company
  include Comparable # XXXX
  attr_reader :id
  attr_accessor :name
  def initialize id, name
    @id = id
    @name = name
  end
  def to_s
    "#{id}:#{name}"
  end
  def <=> other # YYYY
    self.id <=> other.id
  end
end

c1 = Company.new(3, 'Liberyfish')
c2 = Company.new(2, 'Freefish')
c3 = Company.new(1, 'Freedomfish')

print c1.between?(c2, c3)
print c2.between?(c3, c1)
  • チェーンを行う場合はEnumeratorオブジェクトを作成する必要がある
    作成に必要なメソッドはenum_forとto_enum
class Array
  def succ_each(step = 1)
    return enum_for(:succ_each, step) unless block_given?

    each do |int|
      yield int + step
    end
  end
end

p [98, 99, 100].succ_each(2).map {|succ_chr| succ_chr.chr} # ["d", "e", "f"]

[101, 102, 103].succ_each(5) do |succ_chr|
  p succ_chr.chr
  # "j"
  # "k"
  # "l"
end
  • freezeはオブジェクトの破壊的な変更を禁止する
    配列の破壊的な変更を禁止するが、配列の要素の破壊的な変更は禁止しない
array = ["a", "b", "c"].freeze

array.each do |chr|
  chr.upcase!
end

p array #=> ["A", "B", "C"]
  • JSON.loadまたは、JSON.parseは引数にJSON形式の文字列を指定するとHashオブジェクトに変換する
require 'json'

json = <<JSON
{
  "price":100,
  "order_code":200,
  "order_date":"2018/09/20",
  "tax":0.8
}
JSON

hash = JSON.load json

p hash #=> { "price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8 }
p Class.method_defined? :new #=> true
p String.method_defined? :new #=> false
p Class.singleton_class.method_defined? :new #=> true
p String.singleton_class.method_defined? :new #=> true
  • 定数の特徴
  • 代入を行うと警告が発生するが、値は変更される
  • 中身を直接変更した場合は値が変わる。ただし、警告は発生しない
CONST_LIST_A = ['001', '002', '003']
begin
  CONST_LIST_A.map{|id| id << 'hoge'}
rescue
end

CONST_LIST_B = ['001', '002', '003'].freeze
begin
  CONST_LIST_B.map{|id| id << 'hoge'}
rescue
end

CONST_LIST_C = ['001', '002', '003'].freeze
begin
  CONST_LIST_C.map!{|id| id << 'hoge'}
rescue
end

CONST_LIST_D = ['001', '002', '003'].freeze
begin
  CONST_LIST_D.push('add')
rescue
end

p CONST_LIST_A #=> ["001hoge", "002hoge", "003hoge"] 
p CONST_LIST_B #=> ["001hoge", "002hoge", "003hoge"] 
p CONST_LIST_C #=> ["001", "002", "003"]
p CONST_LIST_D #=> ["001", "002", "003"]