ナスカブログ

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

Ruby Goldへの道 day3

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

3日目 得点 54点/100点中

半分超えた〜少しずつメソッドを覚えてきた と言うより問題の内容を覚えてきた(良くない。。)

  • takeはEnumerable オブジェクトの先頭から n 要素を配列として返す
p (1..10).lazy.map{|num|
  num * 2
}.take(3).inject(0, &:+)
# [1*2, 2*2, 3*2]を返す
#=> 12
  • 値を取り出すには、Enumerator::Lazy#forceまたはEnumerator::Lazy#firstを呼び出す必要がある
p (1..100).each.lazy.chunk(&:even?).first(5)
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]

p (1..100).each.lazy.chunk(&:even?).take(5).force
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]
  • extendは引数に指定したモジュールのメソッドを特異メソッドとして追加する
module M
  def class_m
    "class_m"
  end
end

class C
  extend M
  # Mモジュールのclass_mメソッドを特異メソッドとして追加
end

p C.methods.include? :class_m #=> true
  • Refinementは有効化したスコープのみに影響を与えることが出来る
    オープンクラスについての理解が必要そう...
class C
  def m1
    400
  end
end

module M
  refine C do
    def m1
      100
    end
  end
end

class C
  using M
  # スコープ外のため無効
end

puts C.new.m1 #=> 400
  • initializeの可視性はprivateに設定されている
class C
  private
  def initialize
  end
end
  
p C.new.public_methods.include? :initialize #=> false
p C.new.private_methods.include? :initialize #=> true
  • usingはメソッドの中で呼び出すことは出来まず、呼び出した場合はRuntimeErrorが発生する
class C
end

module M
  refine C do
    def m1
      100
    end
  end
end

class C
  def m1
    400
  end

  def self.using_m
    using M
    # メソッドの中で呼び出すことができない
  end
end

C.using_m

puts C.new.m1 #=> Module#using is not permitted in methods (RuntimeError)
  • loadはrequire同様外部ライブラリを呼び出す
    モジュールは呼び出せない
    loadはrequire loadはrequire loadはreq...
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)
  • 定数の定義はメモリ上にあるテーブルに管理される
    別々に書いてもテーブル上の値を参照する
module M
  CONST = "Hello, world"
end

module M
  def self.say
    CONST
  end
end

p M::say #=> "Hello, world"
  • instance_evalの引数に文字列を指定するとネストの状態はモジュールMの特異クラスになる
module M
  CONST = "Hello, world"
end

M.instance_eval(<<-CODE)
  def say
    CONST
  end
CODE

p M::say #=> uninitialized constant #<Class:M>::CONST (NameError)
# CONSTはモジュールMにのみあるので、例外が発生する
  • module_evalの引数に文字列を指定するとネストの状態はモジュールMになる
module M
  CONST = "Hello, world"
end

M.module_eval(<<-CODE) # モジュールMになる
  def self.say
    CONST
  end
CODE

p M::say #=> "Hello, world"
  • Refinementで再定義したメソッドの探索はprependより優先して行われる
class C
  def m1
    200
  end
end

module R
  refine C do # 優先して探索が行われる
    def m1
      300
    end
  end
end

using R

class C
  def m1
    100
  end
end

puts C.new.m1 #=> 300
class Base # 参照
  CONST = "Hello, world"
end

class C < Base # 参照
end

module P
  CONST = "Good, night"
end

class Base
  prepend P
end

module M
  class C
    CONST = "Good, evening"
  end
end

module M
  class ::C # トップレベルの探索
    def greet
      CONST
    end
  end
end

p C.new.greet #=> "Hello, world"
  • クラス変数はレキシカルに決定される
class C
  @@val = 10
end

module B
  @@val = 30
end

module M
  include B
  @@val = 20

  class << C
    p @@val #=> 20
  end
end
  • includeはモジュールのメソッドをインスタンスメソッドとして追加する
    メソッド探索順はselfの後に追加される
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
  include M # モジュールMのfooメソッドを追加する self.fooの後に呼ばれる
end

C.new.foo
#=> C2#foo
#=> M#foo
#=> C#foo
  • const_defined?メソッドは第2引数に継承関係を探索するか指定出来る
    デフォルトではtrue
mod = Module.new

mod.module_eval do
  EVAL_CONST = 100
end

puts "EVAL_CONST is defined? #{mod.const_defined?(:EVAL_CONST)}"
#=> EVAL_CONST is defined? true

puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST)}"
#=> EVAL_CONST is defined? true

# 第2引数にfalseを指定すると継承関係まで探索しない
puts mod.const_defined? :EVAL_CONST, false # false
  • ブロック引数は他の引数より後ろに記述する
def bar(&block)
  block.yield
end

bar do
  puts "hello, world"
end

def bar(&block)
  block.call
end

bar do
  puts "hello, world"
end

def bar(n, &block)
  block.call
end

bar(5) {
  puts "hello, world"
}

# エラー
def bar(&block, n)
  block.call
end

bar(5) {
  puts "hello, world"
}

-__END__をファイルとして扱い、このファイルへのアクセスはDATAを用いる

while not DATA.eof?
  print DATA.read 1
end

__END__
1
2
3
4

#=> 1
#=> 2
#=> 3
#=> 4
  • superを実行した場合にもRefinementが影響する
class C
end

module M
  refine C do
    def m1(value)
      super value - 100 #=> ②
    end
  end
end

class C
  def m1(value)
    value - 100 #=> ③
  end
end

using M

class K < C
  def m1(value)
    super value - 100 #=> ①
  end
end

puts K.new.m1 400 #=> 100
  • レキシカルスコープにある定数を探索する
module K
  CONST = "Good, night"
  class P
  end
end

module K::P::M
  class C
    CONST = "Good, evening"
  end
end

module M
  class C
    CONST = "Hello, world"
  end
end

class K::P
  class M::C
    # K::P::M::CのCONSTを参照する
    p CONST #=> "Good, evening"
  end
end
  • チェーンを行う場合はEnumeratorオブジェクトを作成する必要がある
    作成に必要なメソッド enum_forto_enum
class Array
  def succ_each(step = 1)
    return to_enum(:succ_each) 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}

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

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

p array #=> ["A", "B", "C"]
  • Kernel#Array、Kernel#Hash、Kernel#StringはKernelのモジュール関数として定義されている