ナスカブログ

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

Ruby Goldへの道 day2

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

2日目 得点 46点/100点中

class C
  @val = 3 # クラスインスタンス変数
  attr_accessor :val # 特異クラスのクラスインスタンス変数
  class << self
    @val = 10
  end
  def initialize
    @val *= 2 if val
  end
end

c = C.new
c.val += 10

p c.val #=> undefined method `+' for nil:NilClass (NoMethodError)
  • -tと-fはオプションがない 昨日も同じとこ間違えた〜 forceとtailはないで覚えておこう。

  • 複数回includeされた場合は、後に宣言された方からメソッド探索される

module M1
end

module M2
end

class C
  include M1
  include M2
end

p C.ancestors #=> [C, M2, M1, Object, Kernel, BasicObject]
  • クラスCの特異クラスにあるCONSTもコンテキストが異なる
class Object
  CONST = "100"
end

class C
  CONST = "010"
  class << self
    CONST = "001"
  end
end

p C::CONST #=> "010"
  • FixnumとRationalの演算はRationalになる
  • 1/2rはRationalのインスタンスが作成される
    rが未定義でエラーかと思った。
val = 1 + 1/2r
puts val.class #=> Rational
  • class << self; endで定義されたメソッドは、特異クラスのインスタンスメソッドになる
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
  class << self # 特異クラス
    def method_missing(id, *args)
      puts "B.method_missing"
    end
  end
end

B.new.dummy_method #=> A#method_missing
  • const_getは、selfに定義された定数を探索する
class Human
  NAME = "Unknown"

  def self.name
    const_get(:NAME) # Fukuzawaクラスのインスタンス
  end
end

class Fukuzawa < Human
  NAME = "Yukichi"
end

puts Fukuzawa.name #=> Yukichi
  • キーワード引数へHashオブジェクトを渡すことができる
def foo(arg1:100, arg2:200)
  puts arg1
  puts arg2
end

option = {arg2: 900}

foo arg1: 200, **option
#=> 200
#=> 900
#foo(arg1: 200, arg2: 900)になる
  • キーワード引数は省略できない これも昨日やったような、、、
def foo(arg:)
  puts arg
end

foo 100 #=> wrong number of arguments 

# 下記のように書き換える
def foo(arg:)
  puts arg
end

foo arg: 100 # <= arg: は省略できない
  • Enumerator::Yielderを評価するには、<<を呼び出す
enum_char = Enumerator.new do |yielder|
  "apple".each_char do |chr|
    yielder << chr # Enumerator::Yielderの評価
  end
end

array = enum_char.map do |chr|
  chr.ord
end

p array #=> [97, 112, 112, 108, 101]
  • succ ≒ next selfの次の文字列を返す
class Object
  CONST = "1"
  def const_succ
    CONST.succ! # 1,2,...
  end
end

class Child1
  const_succ
  class << self
    const_succ
  end
end

class Child2
  const_succ
  def initialize
    const_succ
  end
end

Child1.new
Child2.new

p Object::CONST #=> "5"
  • Module.nestingはネストの状態を表示する
    モジュールがネストされた場合全ての状態を表示する
module SuperMod
end

module SuperMod::BaseMod
  p Module.nesting #=> [SuperMod::BaseMod]
end
class Ca
  CONST = "001"
end

class Cb
  CONST = "010"
end

class Cc
  CONST = "011"
end

class Cd
  CONST = "100"
end

module M1
  class C0 < Ca
    class C1 < Cc
      class C2 < Cd
        p CONST #=> "100"
        # C2のスーパークラスCdを探索

        class C2 < Cb
        end
      end
    end
  end
end
  • ブロック変数に*argsを渡すことができる
def hoge(*args, &block)
  block.call(args)
end

hoge(1,2,3,4) do |*args| # [[1, 2, 3, 4]]が渡される
  p args.length < 0 ? "hello" : args #=> [[1, 2, 3, 4]]
end
  • sort!は破壊的メソッド
    大きい順に並び替える
class Company
  attr_reader :id
  attr_accessor :name
  def initialize id, name
    @id = id
    @name = name
  end
  def to_s
    "#{id}:#{name}"
  end
  def <=> other
    other.id <=> self.id
  end
end

companies = []
companies << Company.new(2, 'Liberyfish')
companies << Company.new(3, 'Freefish')
companies << Company.new(1, 'Freedomfish')

companies.sort! # 大きい順に並び替える

companies.each do |e|
  puts e
end
# 3:Freefish
# 2:Liberyfish
# 1:Freedomfish
  • freezeはオブジェクトの破壊的な変更を禁止する
array = ["a", "b", "c"].freeze
array = array.map!{|content| content.succ}

p array #=> can't modify frozen Array: ["a", "b", "c"] (FrozenError)
  • method_missingは、継承チェーンを辿った末にメソッドが見つからなかった場合に呼び出される
class Class
  def method_missing(id, *args)
    puts "Class#method_missing"
  end
end
class A
  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

p B.class # Class ???
B.dummy_method #=> Class#method_missing
# Classクラスのインスタンスメソッドが呼ばれる
  • raiseの例外クラスを省略した場合は、RuntimeErrorを発生させる
    RuntimeErrorはstanderdErrorのサブクラス
begin
  raise "Err!"
rescue => e
  puts e.class #=> RuntimeError
end
  • ブロックにあるローカル変数valはトップレベルにあるものと同じ
val = 100

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

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

p method(val, &_proc) #=> 215
  • const_defined? 指定された名前の定数が定義されていたら真を返す
mod = Module.new

mod.module_eval do
  EVAL_CONST = 100
end

puts "EVAL_CONST is defined? #{mod.const_defined?(:EVAL_CONST, false)}"
#=> EVAL_CONST is defined? false
puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST, false)}"
#=> EVAL_CONST is defined? true
  • Fiberは軽量スレッドを提供する
f = Fiber.new do
  Fiber.yield 15
  5
end

f.resume
f.resume
# f.resume 三回以上呼び出すとエラーになる
  • requireはライブラリのロード、loadは設定ファイルの読み込みに用いる。
# lib.rb
module Lib
  $num += 1
end
##

# 実行ファイル
$num = 0
1..10.times do |n|
  require './lib.rb'
end
puts $num #=> 1
  • JSON.loadまたは、JSON.parseは引数にJSON形式の文字列を指定するとHashオブジェクトに変換する
require 'json'

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

using_parse = JSON.parse json
p using_parse #=> {"price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8}

using_load = JSON.load json
p using_parse #=> {"price"=>100, "order_code"=>200, "order_date"=>"2018/09/20", "tax"=>0.8}
  • 破壊的な変更できない また出た
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"]