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

ワクワク!Rubyクイズ!!

 ワクワク!Rubyクイズ!!

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

RyotaNakaya

June 30, 2021
Tweet

More Decks by RyotaNakaya

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