ナスカブログ

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

Ruby Goldへの道 day11

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

11日目 得点 64点/100点中

安定して70点を超えていたのに急に落ちた。 まだまだ理解が足りんな。

  • 定数の参照は静的に行われる。継承を辿っていかない
module M
  CONST = "Hello, world"
end

class M::C
  def awesome_method
    CONST
    # 定数の参照はレキシカルに行われる
    # 継承を辿っていかない
    # M::CONST #=> "Hello, world"
  end
end

p M::C.new.awesome_method
##=>  uninitialized constant M::C::CONST (NameError)
  • class_eval
    • ブロックを渡すとネストの状態はモジュールなのでモジュールの定数を参照
    • 文字列を渡すとネストの状態はクラスなのでクラスの定数を参照
class C
end

module M
  CONST = "Hello, world"

  C.class_eval do
    # ブロックを渡すのでモジュールMに
    def awesome_method
      # モジュールMのCONSTを参照する
      CONST
    end
  end
end

p C.new.awesome_method #=> "Hello, world"
class C
  CONST = "Hello, world"
end

module M
  # 文字列を渡すのでネストの状態はクラスC
  # CクラスのCONSTを参照する
  C.class_eval(<<-CODE)
    def awesome_method
      CONST
    end
  CODE
end

p C.new.awesome_method
  • Module#refine
  • 引数で指定したクラスまたはモジュールに対してブロック内の機能を提供する
class C
  def m1
    200
  end
end

module R
  refine C do
    # Cクラスにm1メソッドを定義する
    # この場合m1メソッドをオーバーライドする
    def m1
      100
    end
  end
end

using R
# モジュールRを有効化する

c = C.new
puts c.m1 #=> 100

Ruby Goldへの道 day10

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

10日目 得点 82点/100点中

  • Hashオブジェクト
  • **オブジェクトでオブジェクトの中身をキーワード引数に渡すことができる
def foo(arg1:100, arg2:200)
  puts arg1
  puts arg2
end

option = {arg2: 900}

foo arg1: 200, *option # **optionでハッシュオブジェクトとして渡せる
#=> syntax error, unexpected *
  • module_evalにブロックを渡した場合
  • ネストされた状態ではなくトップレベルにいる
  • トップレベルで定義した定数はObjectの定数になる
module A
  B = 42

  def f
    21
  end
end

A.module_eval do
  p Module.nesting #=> []
  # ネストされた状態ではない
  def self.f
    p B
  end
end

B = 15

A.f #=> 15
# トップレベルにある定数を指す
class Base
  # CONST = "Hello, world"
  # ②探索される
  # もしここにCONSTがなかったら18行目でPモジュールをprependしているのでPモジュールを探索する③
end

class C < Base
  # ①探索される
  # Baseクラスを継承しているのでBaseクラスにCONSTがあるか探索する②
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
      # トップレベルのCクラスにCONSTの定数があるか探索する①
    end
  end
end

p C.new.greet #=> "Hello, world"
p M::C::CONST #=> "Good, evening

Ruby Goldへの道 day9

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

9日目 得点 72点/100点中

後2問。 いつも前から問題をみて気になった問題を選んでいるが今日は気分転換に後ろから。

  • yaml
  • yamlモジュールにあるメソッドload
    • YAML.loadは引数を1つ取り、YAMLデータをRubyオブジェクトにする
    • read, new, openはない
require 'yaml'

yaml = <<YAML
  sum: 510,
  orders:
    - 260
    - 250
YAML
# 文字列のYAMLデータをハッシュオブジェクトにしている

object = YAML.load yaml
# YAMLデータをrubyオブジェクトにしている

p object #=> {"sum"=>510, "orders"=>[260, 250]}
  • singlten_class
  • Object.singleton_classで特異クラスを取得することができる
class C
  def self._singleton
    class << C
      self
      # レシーバのオブジェクトを返す
      # ここでは class C
    end
  end
end

p C._singleton #=> #<Class:C>

class C
end
p C.singleton_class #=> #<Class:C>

p C.new.singleton_class #=> #<Class:#<C:0x00007fcfc5823998>>
  • Enumerator
  • オブジェクト作成にはenum_forかto_enumを使う
  • また、newメソッドでオブジェクトを作成することもできる
class Array
  def succ_each(step = 1)
    return enum_for(:succ_each, step) unless block_given?
    # 引数が2つ

    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

#=> ["d", "e", "f"]
#=> "j"
#=> "k"
#=> "l"


class Array
  def succ_each(step = 1)
    return to_enum(:succ_each) unless block_given?
    # 引数が2つ

    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

#=> ["c", "d", "e"]
#=> "j"
#=> "k"
#=> "l"


class Array
  def succ_each(step = 1)
    return to_enum(:succ_each, step) unless block_given?
    # 引数が2つ

    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

#=> ["d", "e", "f"]
#=> "j"
#=> "k"
#=> "l"

Ruby Goldへの道 day8

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

8日目 得点 78点/100点中

今日も間違えた中から数問深掘りする

  • ネストされたクラス内の定数の参照
class C
  p Module.nesting #=> [C]
  CONST = "Hello, class C"
end

module M
  class C
    p Module.nesting #=> [M::C, M]
    CONST = "Hello, module M class C"
  end
end

p C::CONST #=> "Hello, class C"
p M::C::CONST #=> "Hello, module M class C"
  • Module#refineは無名のモジュールをを作成する
  • ブロック内のselfは無名モジュールになる
  • クラスメソッドを再定義するにはsingleton_classを利用する
    • self.メソッド としない
class C
  def self.m1
    200
  end
end

module R
  refine C.singleton_class do
    def m1
      # Cクラスのm1メソッドを再定義する
      # singleton_classがなければそのまま200が出力される
      100
    end
  end
end

using R

puts C.m1 #=> 100
  • 演算と戻り値のクラス
  • 戻り値はRationalとFloatとComplex
  • Rational < Float < Complexで覚えるといい
  • Date同士の演算はRation
  • Time同士の演算はFloat
  • Datetime同士の演算はRational
val = 1 + 1/2r
puts val.class #=> Rational

puts 1.class #=> Integer
# puts 1/2r.class #=> 

float = 1/3.to_f
puts float.class #=> Float
complex = 3.to_c
puts complex.class #=> Complex

val2 = float + complex
puts val2.class #=> Complex

Ruby Goldへの道 day7

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

7日目 得点 76点/100点中

初の合格点。この調子で理解を深めていこう。 今日も間違えた中から数問深掘りする

  • aliasとalias_method
    • alias 新メソッド 旧メソッド  の形で記述する。" , " は不要。
    • alias_method :新メソッド, 旧メソッド の形で記述する。 " , " は必要。シンボル 型で記述できる
class String
  # aliasの場合 新メソッド 旧メソッド の形で記述する , はいらない
  alias hoge reverse

  # alias_methodの場合 :新メソッド, :旧メソッド の形で記述する
  alias_method :hoge, :reverse  
end

p "12345".hoge
  • prepend
    • モジュールのメソッドを特異メソッドとして追加する
    • メソッドの探索はselfより前に行われる
    • 複数個記述した場合は左から探索される
module M1
end

module M2
end

class C
  prepend M1, M2
  # selfより先に探索される。左から順に探索される。
end

p C.ancestors #=> [M1, M2, C, Object, Kernel, BasicObject]
  • includeはmoduleのインスタンスメソッドをMix-inする
  • C.methodsはCの特異メソッドを表示する
module M
  def self.class_m
    "M.class_m"
  end
end

class C
  include M
  # MモジュールのインスタンスメソッドをMix-in
end

p C.methods.include? :class_m #=> false
# Cにはclass_mは追加されない

今日はこの辺で、、 メタプログラミングRuby読みながら問題で気になった箇所を深掘り、なかなかハマってる気がする。

Ruby Goldへの道 day6

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

6日目 得点 66点/100点中

停滞期に入ったな〜 70点の壁が厚い

今日から間違えた問題の深堀に方向転換

  • class_eval
    • ブロックを渡した場合は、ブロック内のネストはモジュールMになる
    • 文字列を渡した場合のネストの状態はクラスCです。
# ブロックを渡した例
# ブロックはモジュールになるからMモジュールないのCONSTを探索
class C
end

module M
  CONST = "Hello, world"

  C.class_eval do
    def awesome_method
      CONST
    end
  end
end

p C.new.awesome_method #=> "Hello, world"


# 文字列を渡した例
# ネストの中はクラスなのでCクラスのCONSTを探索
class C
  CONST = "Hello, world"
end

module M
  C.class_eval(<<-CODE)
    def awesome_method
      CONST
    end
  CODE
end

p C.new.awesome_method #=> "Hello, world"
  • 特異クラス
    • class << Hoge
    • class << self
    • def クラス.メソッド

の三パターンある 呼び出しはクラスメソッドと同じ クラス.メソッドで呼び出せる

class Hoge
  class << Hoge
    def hoge
      p '<< Hoge'
    end
  end
  
  class << self
    def hoge
      p '<< self'
    end
  end

  def Hoge.hoge
    p 'C.hoge'
  end
end
# 全てクラス.メソッドで呼び出せる
Hoge.hoge #=> "C.hoge"
# def Hoge.hoge..をコメントアウトすると
Hoge.hoge #=> "<< self"
# さらに<< self..をコメントアウトすると
Hoge.hoge #=> "<< Hoge"
class C
  class << C # またはself
    def fuga #=> 特異メソッド
      'Hi'
    end
  end

  def hoge #=> インスタンスメソッド
    'Goodbye'
  end
end

# クラス内に定義したメソッドはインスタンスメソッド
obj = C.new 
p obj #=> #<C:0x00007fb9128df8d8>
p obj.hoge #=> 'Goodbye'

p C.instance_methods(false) #=> [:hoge]
p C #=> C
# 特異メソッドの呼び出し
# クラスメソッドの呼び出しと同じ
p C.fuga #=> 'Hi'

Ruby Goldへの道 day5

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

5日目 得点 62点/100点中

昨日よりは落ちたけど半分は確実に超えるようになってきた。

  • 特異クラスの呼び出しはクラス.メソッド
class C
  class << C
    def hoge
      'Hi'
    end
  end

  def hoge
    'Goodbye'
  end
end

p C.new.hoge #=> "Goodbye"
p C.hoge #=> "Hi"
  • class << C ~ endの処理はクラスを定義した時点で実行される
class S
  @@val = 0
  def initialize
    @@val += 1
  end
end

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

  def initialize
  end
end

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

p C.class_variable_get(:@@val) #=> 3
  • メソッド探索順は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
  prepend M
end

C.new.foo
#=> C2#foo
#=> C#foo
#=> M#foo
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
  prepend M
end

C.new.foo
#=> C2#foo
#=> C#foo
#=> M#foo
module M1
  class C1
    CONST = "001"
  end

  class C2 < C1
    CONST = "010"

    module M2
      CONST = "011"

      class Ca
        CONST = "100"
      end

      class Cb < Ca
        p CONST "011"
        # Cbに一番近いCONSTを探索する
      end
    end
  end
end
  • usingは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
  • const_getは、selfに定義された定数を探索する
class Human
  NAME = "Unknown"

  def self.name
    p self #=> Fukuzawa
    const_get(:NAME)
    # selfに定義されたクラスを探索する
  end
end

class Fukuzawa < Human
  NAME = "Yukichi"
end

puts Fukuzawa.name #=> Yukichi
  • キーワード引数は省略できない
def foo(arg:)
  puts arg
end

foo 100
#=> wrong number of arguments (given 1, expected 0; required keyword: arg) (ArgumentError)
  • Module.nestingはネストの状態を表示する
module SuperMod
end

module SuperMod::BaseMod
  p Module.nesting #=> [SuperMod::BaseMod]
end

module SuperMod
  module BaseMod
    p Module.nesting # [SuperMod::BaseMod, SuperMod]
  end
end
class String
  # aliasの定義方法
  alias new_method old_method
  alias :new_method :old_method
  alias $new_global_val $old_global_val
end

p "12345".hoge
  • timesは0からself -1までの数値を順番に返す
10.times{|d| print d < 2...d > 5 ? "O" : "X" }
#=> OOOOOOOXXX
# 1. d < 2 が真を返すまでfalseを返す
# 2. d < 2 が真を返すとtrueを返す
# 3. 次は d > 5 を評価する
# 4. d > 5 が真を返すまでtrueを返す
# 5. d > 5 が真を返すとtrueを返して1に戻る
  • prependで複数指定した場合右側から探索される
module M1
end

module M2
end

class C
  prepend M1, M2
end

p C.ancestors #=> [SuperMod::BaseMod]
  • 1iは複素数(Complex)のオブジェクト
val = 1i * 1i
puts val.class #=> Complex
  • initialize(*)とすることで、サブクラスで引数を意識する必要が無くなる
class S
  def initialize(*)
    puts "S#initialize"
  end
end

class C < S
  def initialize(*args)
    super
    puts "C#initialize"
  end
end

C.new(1,2,3,4,5)
#=> S#initialize
#=> C#initialize

会社からメタプログラミングrubyを借りてきたので明日はそれを読み進めよう 特異クラスとか気になっていた単語について調べよう