Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ワクワク!Rubyクイズ!!

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for nakaryo nakaryo
June 30, 2021

 ワクワク!Rubyクイズ!!

Ruby の言語仕様がちょっとだけわかるクイズです

Avatar for nakaryo

nakaryo

June 30, 2021
Tweet

More Decks by nakaryo

Other Decks in Technology

Transcript

  1. Q. 変数代入 a = 1, 2 p a => [1,

    2] b, c = 1 p b => 1 p c => nil d, *e = 1, 2, 3 p d => 1 p e => [2, 3] ❖ 右辺がカンマ区切りで複数ある場合には配列に変 換される ❖ 左辺の要素が余った場合、 nil で初期化される ❖ アスタリスクを利用するとまとめて代入できる
  2. Q. 定数 CONST = "constant" p CONST CONST = "overwrite!"

    p CONST CONST.reverse! p CONST 何が出力されるでしょう?
  3. Q. 定数 CONST = "constant" p CONST => "constant" CONST

    = "overwrite!" p CONST => "overwrite!" CONST.reverse! p CONST => "!etirwrevo" ❖ アルファベット大文字で始まる識別子は定数 ❖ 定数と言いつつ再代入が可能 ❖ 定数と言いつつ破壊的変更が可能
  4. Q. 定数(freeze) CONST = "constant".freeze p CONST CONST = "overwrite!".freeze

    p CONST CONST.reverse!.freeze p CONST 何が出力されるでしょう?
  5. CONST = "constant".freeze p CONST => "constant" CONST = "overwrite!".freeze

    p CONST => "overwrite!" CONST.reverse!.freeze => can't modify frozen String (FrozenError) Q. 定数(freeze) ❖ freeze で凍結しても再代入は可能 ➢ あくまでもオブジェクトの参照を入れ替えてい て、元々のオブジェクトを変更するわけではない から ❖ 破壊的な変更は RuntimeError になる ❖ もし厳格に定数を定義したい場合は Class や Module ごと凍結する module MyConst CONST = 'constant'.freeze freeze end p MyConst::CONST => "constant" MyConst::CONST = "overwrite!" => can't modify frozen Module
  6. Q. 定数(array) list = ["apple", "banana", "orange"].freeze list.map {|x| x

    << " juice" } p list list << "berry juice" p list 何が出力されるでしょう?
  7. Q. 定数(array) list = ["apple", "banana", "orange"].freeze list.map {|x| x

    << " juice" } p list => ["apple juice", "banana juice", "orange juice"] list << "berry juice" => can't modify frozen Array (FrozenError) ❖ array を freeze で凍結しても中身の要素に破壊的 変更を加えることは可能 ❖ array 自体への変更は RuntimeError になる ❖ もし array や hash の中身の要素まで凍結したい 場合は map 関数で一つ一つ freeze する list = ['apple', 'banana', 'orange'].map(&:freeze).freeze list.map {|x| x << ' juice' } => can't modify frozen String: "apple" (FrozenError)
  8. p nil.to_i => 0 p nil.to_s => "" p nil.empty?

    => undefined method `empty?' for nil:NilClass (NoMethodError) Q. nil ❖ nil もオブジェクトなのでレシーバとして機能する ➢ Ruby では全てがオブジェクト ➢ nil は NilClass のインスタンスオブジェクト ❖ メソッドが見つからない時は NoMethodError ❖ Object class と Kernel Module に実装されている メソッドなら呼び出し可能 p nil.class.ancestors => [NilClass, Object, Kernel, BasicObject]
  9. ❖ ちなみに Ruby のクラスはオープン ➢ 既存クラスを拡張できる ❖ 右の例ではデフォルトでは存在しない empty? メ

    ソッドを追加したり、既存の to_s メソッドの挙動を変 更したりしている ➢ でもモンキーパッチダメゼッタイ ... ❖ method_missing は呼び出されたメソッドが見つか らない時に実行されるメソッド ➢ つまりこれを使うと nil レシーバ呼び出しによ る RuntimeError を100%回避できる ➢ でもモンキーパッチダメゼッタイ ... class NilClass def empty?; true; end def to_s; raise "にるぽだよーん"; end def nil.method_missing(*_); nil; end end p nil.empty? => true p nil.hello => nil p nil.to_s => `to_s': にるぽだよーん (RuntimeError) Q. nil
  10. def hello_or_nil ["hello", nil].sample end p hello_or_nil&.upcase Q. safe navigation

    ❖ safe navigation operator 「&」を使うと、nil レシー バに対する呼び出しで例外が起きなくなる ➢ Ruby2.3.0 〜 ➢ ぼっち演算子とも呼ばれる
  11. Q. safe navigation require "active_support/all" def hello_or_nil ["hello", nil].sample end

    p hello_or_nil&.upcase p hello_or_nil.try(:upcase) ぼっち演算子と active support の try との違いはなんでしょう?
  12. ❖ ぼっち演算子 ➢ レシーバが nil なら nil を返す ➢ 右の例では

    1 に対する呼び出しはエラーに なる ❖ try ➢ NoMethodError を握りつぶす ➢ レシーバが何かを意識しない ➢ 右の例では常にエラーが発生しない Q. safe navigation require "active_support/all" def hello_or_nil_or_1 ["hello", nil, 1].sample end p hello_or_nil_or_1&.upcase p hello_or_nil_or_1.try(:upcase)
  13. ❖ ぼっち演算子を使うと、引数はメソッドが呼び出され た時のみ評価される ➢ try の場合は常に評価される ❖ 処理効率もぼっち演算子の方が高速かつ、 rails に

    ロックインしないため、理由がなければぼっち演算 子を使うと良い Q. safe navigation def sugoku_omoi_fn sleep 1000 end # こうではなく res = sugoku_omoi_fn obj&.foo(res) # こう obj&.foo(sugoku_omoi_fn)
  14. arr1 = [ "d", "a", "e", "c", "b" ] p

    arr1.sort arr2 = [9, 7, 10, 11, 8] p arr2.sort arr3 = ["9", "7", "10", "11", "8"] p arr3.sort Q. 配列のソート 何が出力されるでしょう?
  15. arr1 = [ "d", "a", "e", "c", "b" ] p

    arr1.sort => ["a", "b", "c", "d", "e"] arr2 = [9, 7, 10, 11, 8] p arr2.sort => [7, 8, 9, 10, 11] arr3 = ["9", "7", "10", "11", "8"] p arr3.sort => ["10", "11", "7", "8", "9"] Q. 配列のソート ❖ 内部的には <=> 演算子で各要素を比較している ❖ 数値の場合は見たまんまの並び替えが起こる ❖ 文字列の場合はバイト列比較なので見た目に反し た挙動になる ➢ バイト列に変換し、その数値を先頭から比 較していく p "10".bytes => [49, 48] p "9".bytes => [57] p "hoge" if "10" < "9" => "hoge" # 49 < 57 なので
  16. p "blank" if "" p "zero" if 0 p "true"

    if true p "false" if false p "nil" if nil Q. booleanと真偽 何が出力されるでしょう?
  17. ❖ Ruby では false と nil が「偽」で、それ以外は全て 「真」 p "blank"

    if "" => "blank" p "zero" if 0 => "zero" p "true" if true => "true" p "false" if false => p "nil" if nil => Q. booleanと真偽
  18. ❖ ちなみに Boolean 型というのは存在しない ➢ TrueClass と FalseClass があるのみ ❖

    true は TrueClassの、false は FalseClass の唯 一のインスタンスオブジェクト ➢ 擬似変数としてグローバルに定義され凍結さ れているかつ new が呼べないので、プログ ラム上で常に不変の object id を持つ ➢ (シングルトンにしてるのかなと思ったら new を undef してるっぽい) Q. booleanと真偽 p true.class.ancestors => [TrueClass, Object, Kernel, BasicObject] p false.class.ancestors => [FalseClass, Object, Kernel, BasicObject] b1, b2 = true, true p b1 == b2 => true p b1.equal?(b2) => true p TrueClass.new => undefined method `new'
  19. Q. スコープ if true var = "if_true" end p var

    (1..1).each do var2 = "do_each" end p var2 何が出力されるでしょう?
  20. ❖ Ruby はレキシカルスコープ ❖ if や for はスコープを作らない ❖ each

    はスコープを作る ➢ 正確にはブロックがスコープを作る ➢ ブロックはコードだけでなく束縛の集まりでも ある ❖ あれ、でもブロックの外側で count 的な変数を定義 してそれをイテレータブロックの中で増やしていくみ たいなコードって書けるよな ...? if true var = "if_true" end p var => "if_true" (1..1).each do var2 = "do_each" end p var2 => undefined local variable or method `var2' Q. スコープ
  21. ❖ ブロックはスコープを作ると同時にコンテキストという考え 方も持っている ❖ ブロックはクロージャであり、ブロック内の自由変数はブ ロックの外部環境(コンテキスト)に従う ❖ つまりメソッド実行時のローカル変数を参照できる count =

    1 3.times do count += 1 end p count => 4 Q. スコープ def create_counter count = 1 return Proc.new do count += 1 p count end end counter = create_counter p counter.class => Proc counter.call => 2 counter.call => 3 ❖ ブロックが参照している外部環境は、ブロックが存在する 限り保存されている ❖ create_counter メソッドの実行時コンテキストにおける ローカル変数 count は、 メソッドが返した Proc 以外か らは参照できない ➢ 内部状態を完全に隠蔽できる
  22. top_var = "top" MyClass = Class.new do p top_var define_method

    :my_method do p top_var end end MyClass.new.my_method => "top" => "top" ❖ ちなみに Ruby のスコープゲートは以下の 3つ ➢ class ➢ module ➢ def ❖ Ruby のインタプリタは上記のキーワードをもとにス コープを作るかどうかを判定している ❖ つまり右のような書き方でスコープゲートの利用を 回避して束縛を渡すことが可能 ❖ 他にも instance_eval とか... Q. スコープ
  23. def hello(name) puts "Hello #{name}" end hello_fn = hello hello_fn("kitty")

    Q. ファーストクラスオブジェクト 何が出力されるでしょう?
  24. ❖ func への代入時の右辺評価時に hello を実行しよ うとするが、引数がないためエラーになる ❖ Ruby では関数定義を変数に代入することはできな い

    ➢ 関数そのものがファーストクラスオブジェクト ではないため ❖ 関数定義を一級関数化するには Object.method でメソッドオブジェクト化してあげる def hello(name) puts "Hello #{name}" end hello_fn = hello => # wrong number of arguments (ArgumentError) hello_fn("kitty") Q. ファーストクラスオブジェクト def hello(name) puts "Hello #{name}" end hello_fn = Object.method(:hello) hello_fn.call("kitty") => "Hello kitty"
  25. def hello Proc.new { |name| puts "Hello #{name}" } end

    hello_fn = hello hello_fn.call("kitty") => "Hello kitty" hello = Proc.new do |name| puts "Hello #{name}" end hello.("kitty") => "Hello kitty" ❖ 実用的には Proc や lambda オブジェクトを使うこと が多い(と思う) ➢ Proc は手続きをまとめた”オブジェクト”なの で値として扱える ❖ call メソッドを呼ばないといけないのは、つけないと 変数として解釈されるから ❖ そのまま変数にぶち込めば無名関数として使える Q. ファーストクラスオブジェクト
  26. HOGE = "TOP" class A HOGE = "A" end class

    B < A def hoge p HOGE end end B.new.hoge Q. 探索 何が出力されるでしょう?
  27. class A HOGE = "A" def hoge p HOGE end

    end class B < A HOGE = "B" end B.new.hoge Q. 探索 何が出力されるでしょう?
  28. ❖ メソッド探索は継承ツリーに沿って行われ、スー パークラスのメソッドを呼び出せる ❖ メソッド呼び出し時のレキシカルスコープが有効に なるので class A の定数が参照される ❖

    この状態で class A の定数定義を削除すると、 HOGE が見つからずエラーになる ➢ 探索対象が A::HOGE だから class A HOGE = "A" def hoge p HOGE end end class B < A HOGE = "B" end B.new.hoge => "A" Q. 探索
  29. puts = "1" puts # 変数参照 puts() # メソッド参照 puts(puts)

    # = puts puts = メソッド参照 ❖ 呼び出し時に () がついていればメソッド ❖ () が省略されているだけとみなし、メソッドだと解釈 される Q. 探索
  30. def add_fuga(arr) arr.push("fuga") end arr1 = ["hoge"] p add_fuga(arr1) p

    arr1 Q. 参照の値渡し 何が出力されるでしょう?
  31. Q. 参照の値渡し def add_fuga(arr) arr.push("fuga") end arr1 = ["hoge"] p

    add_fuga(arr1) => ["hoge", "fuga"] p arr1 => ["hoge", "fuga"] ❖ add_fuga の引数には arr1 のポインタが渡されて いる ➢ 参照先が同じオブジェクトなので push の変 更が arr1 にも影響している
  32. def add_fuga_with_new(arr) arr = Array.new arr.push("fuga") end arr2 = ["hoge"]

    p add_fuga_with_new(arr2) p arr2 Q. 参照の値渡し 何が出力されるでしょう?
  33. Q. 参照の値渡し def add_fuga_with_new(arr) arr = Array.new arr.push("fuga") end arr2

    = ["hoge"] p add_fuga_with_new(arr2) => ["fuga"] p arr2 => ["hoge"] ❖ add_fuga_with_new の中で arr に再代入している ので、arr2 とは別のオブジェクトが生成される ➢ 元の arr2 には psuh の変更が影響しない ❖ 参照渡しの場合は引数 arr の参照自体が切り替 わってしまうため、arr2 の参照も置き換わるはずだ が、Ruby は参照ではなく参照の値(ポインタ)を渡し ているだけなのでこのような挙動になる ❖ Java や Ruby は参照渡しじゃないよおじさん「 Java や Ruby は参照渡しじゃないよ」
  34. ❖ ちなみに String や Intger なども同様に全て参照の 値渡し ➢ 知らずに破壊的変更を加えると普通にバグ ります

    def add_fuga(str) str << "fuga" end str = "hoge" add_fuga(str) p str => "hogefuga" Q. 参照の値渡し
  35. ❖ 引数で渡した段階ではポインタを渡しているので同 じオブジェクトを見ている ❖ 再代入したタイミングで新しいオブジェクトが生成さ れる ➢ メモリ効率を鑑みて、再利用されるまで実体 をコピーしないようになっている ❖

    スコープが異なるので元の str には影響しない def add_fuga(str) p str.object_id => 60 str = "fuga" p str.object_id => 80 end str = "hoge" p str.object_id => 60 add_fuga(str) p str => "hoge" Q. 参照の値渡し
  36. Fin