diff --git a/cherry.txt b/cherry.txt index bba73e2..be2200c 100644 --- a/cherry.txt +++ b/cherry.txt @@ -295,7 +295,7 @@ refinements:独自のスコープを設定できる。有効にするにはusi ## 3/29 TypeError:Rubyでは例外も例外クラスのインスタンスになっている。TypeErrorは例外オブジェクトのクラス名 -予期しない補足:例外が発生した箇所がbegin〜rescueで囲まれていない場合、例外が発生するとそこで処理を中断してメソッドの呼び出しを1つずつ戻っていく。途中に例外を補足するコードがあればそこから処理を続行する +予期しない捕捉:例外が発生した箇所がbegin〜rescueで囲まれていない場合、例外が発生するとそこで処理を中断してメソッドの呼び出しを1つずつ戻っていく。途中に例外を捕捉するコードがあればそこから処理を続行する backtraceメソッド:バックトレース情報を配列にして返す messageメソッド:例外発生時のエラーメッセージを返す NoMethodError:これも例外クラスの一つ。存在しないメソッドを呼び出した時に現れる @@ -303,12 +303,12 @@ ZeroDivisionError:0で除算した時に現れる例外クラスの一つ。 例外クラスの指定: begin 例外が起きうる処理 -rescue 補足したい例外クラス +rescue 捕捉したい例外クラス 例外が発生した場合の処理 end のような形式で特定の例外が起こった場合にのみ捕捉させることができる 例外クラスの継承関係:すべての例外クラスはExceptionクラスを継承しており、そこから下はStandardErrorのサブクラスとそれ以外の例外クラスに分かれている。 -rescure節の挙動:なにもクラスを指定しなかった場合にはStandardErrorとそのサブクラスが捕捉される。指定した場合はそのクラス自身とサブクラスが捕捉される +rescue節の挙動:なにもクラスを指定しなかった場合にはStandardErrorとそのサブクラスが捕捉される。指定した場合はそのクラス自身とサブクラスが捕捉される 捕捉すべき対象:最上位のExceptionクラスを指定するとNoMemoryErrorなど特殊なエラーも対象に含めてしまうため、通常はStandardErrorかそのサブクラスに捕捉対象を限定すべきである 例外を書く順番:スーパークラスよりも手前にサブクラスを書く retry:例外発生時に処理をやり直させることができる。 @@ -322,5 +322,66 @@ Date.valid_date?:正しい日付かどうか確認できる ensure:例外の有無にかかわらず処理を実行させる 392ページから +## 3/30 +ensureの代わりにブロックを使う +File.open('some.txt', 'w') do |file| + file << 'Hello' +end +上記のような形式で「使用したら必ずリソースを解放する」といった処理をブロック付きメソッドに任せることができる。 +例外処理のelse +Rubyの例外処理では例外が発生しなかった場合に実行されるelse節を書くこともできる。ただし使われるケースはあまり多くない +例外処理と戻り値:例外発生せず最後まで正常に処理が進んだ場合はbegin節の最後の式が戻り値になる。捕捉された場合はrescue節の最後の式が戻り値になる +ensure節ではreturnを使わない:使うと正常時も例外発生時もensureの値がメソッドの戻り値になる +パース:特定の書式に従って文字列を解析し、プログラムで利用可能な別のデータ構造に変換すること +rescue修飾子:rescueを修飾子として用いるとbegin endを勝利略できる。`例外が発生しそうな処理 rescue 例外が発生した時の処理`という形で記述する +↑ただし補足する例外クラスを指定することはできない。rescue修飾子ではStandardErrorとそのサブクラスのみが捕捉される +$!と$@に格納される例外情報:Rubyでは最後に発生した例外は組み込み変数の$!に格納される。またバクトレース情報は$@に格納される +begin/endを省略できるケース:メソッドの中身全体が例外処理で囲まれている場合は省略できる。また、do/endブロックの内部でも省略できる +rescueした例外を再度発生させる:rescue節の中でraiseメソッドを使うこともできる。この時、raiseメソッドの引数を省略するとrescue節で捕捉した例外をもう一度発生させることができる +独自の例外クラスを定義する:StandardErrorクラスかそのサブクラスを継承することで、独自の例外クラスを定義できる +ここから10章 +yield:渡されたブロックを実行するためにメソッド内で使う +block_given?:ブロックが渡されている場合にtrueを返す +ブロックを引数として明示的に受け取る:ブロックを引数として受け取る場合は引数名の前に&をつける。また、そのブロックを実行する場合はcallメソッドを使う +ブロックを引数にするメリット:ブロックを他のメソッドに引き渡せるようになる。また、渡されたブロックに対してメソッドを呼び出し、必要な情報を取得したりブロックに対するなにかしらの操作を実行したりできるようになる +Proc:ブロックをオブジェクト化するためのクラス。なんらかの処理そのものを表している。Proc.newにブロックを渡すことにより作成できる。実行する時はcallメソッドを使う。 +Procオブジェクトの作り方まとめ +Proc.new { |a, b| a + b} +proc { |a, b| a + b } +->(a,b) {a + b } +lamda { |a, b| a + b} +->を使う記述をラムダリテラルと呼ぶ +ラムダはProc.newよりも引数のチェックが厳格になる +lamda?:ProcクラスのインスタンスがProc.newとして作られたのかラムダとして作られたのか判断してくれる +メソッドチェーンを使ってコードを書く:あるメソッドを呼び出す➜その戻り値のメソッドを呼び出す➜またその戻り値のメソッドを呼び出す……と次々とメソッドを呼び出していくコーディングスタイルのことをメソッドチェーンと呼ぶ +def self.loud(level) + ->(words) do + words + .split(' ') + .map { |word| word.upcase + '!' * level } + .join(' ') + end + end + このようにメソッドごとに改行させるスタイルもメソッドチェーンではよく見かける +inject:畳み込み演算を行うメソッド +Procオブジェクトを===で呼び出す:case文のwhen節でProcオブジェクトを使えるようにするためにこのような方法が設けられている +Procオブジェクトをブロックとして渡すには:引数の前に&を付けなければならない。この時の&の役割は単にProcオブジェクトをブロックと認識させるのみならず、厳密には右辺のオブジェクトにto_procメソッドを呼び出し、その戻り値として得られたProcオブジェクトをブロックを利用するメソッドに与えている +Procオブジェクトとクロージャ:通常、メソッドの引数やローカル変数は実行が終わると参照できなくなるが、Procオブジェクト内ではメソッドの実行が完了しても引き続きアクセスし続けることができる。生成時のコンテキスト(変数情報など)を保持している関数をクロージャ(関数閉包)と呼ぶ +ここから11章 +パターンマッチ:比較的新しい機能。条件分岐の一種。配列やハッシュの構造に着目して条件分岐させたい時に有効 +パターンマッチの構文 +case 式 +in パターン1 + パターン1にマッチした時の処理 +in パターン2 + パターン1にマッチせず、パターン2にマッチした時の処理 +else + パターン1にも2にもマッチしなかった時の処理 +end +パターンマッチのハッシュ:in節で値を省略してキーだけを書いた場合は自動的にキーと同じ名前のローカル変数が作成され、そこに値が代入される +valueパターン:in節に数値や文字列を直接指定できる利用パターン。thenを使って条件にマッチした時の処理を一行で書くこともできる。ただし、case文とは異なりパターンが1つもマッチしないと例外が発生する。else節を容易することで回避できる +variableパターン:in節のパターンに変数を書いてローカル変数の宣言と代入を同時に行う利用パターン。in節で事前に定義された変数の値を参照したい時はピン演算子(^)を用いる。ただし指定できる変数はローカル変数のみで、インスタンス変数を使おうとすると構文エラーが起きる。 +451ページから +## 3/31 diff --git a/main.rb b/main.rb index 8a14c3a..644a9ad 100644 --- a/main.rb +++ b/main.rb @@ -1,18 +1,37 @@ -print 'Text?: ' -text = gets.chomp +module Effects + def self.reverse + lambda do |words| + words.split(' ').map(&:reverse).join(' ') + end + end -begin - print 'Pattern?: ' - pattern = gets.chomp - regexp = Regexp.new(pattern) -rescue RegexpError => e - puts "Invalid pattern: #{e.message}" - retry + def self.echo(rate) + lambda do |words| + words.each_char.map { |c| c == ' ' ? c : c * rate }.join + end + end + + def self.loud(level) + lambda do |words| + words.split(' ').map { |word| word.upcase + '!' * level }.join(' ') + end + end end -matches = text.scan(regexp) -if matches.size > 0 - puts "Matched: #{matches.join(', ')}" -else - puts 'Nothing matched.' +class WordSynth + def initialize + @effects = [] + end + + def add_effect(effect) + @effects << effect + end + + def play(original_words) + words = original_words + @effects.each do |effect| + words = effect.call(words) + end + words + end end diff --git a/main_test.rb b/main_test.rb index e7ece3c..d6da660 100644 --- a/main_test.rb +++ b/main_test.rb @@ -1,14 +1,46 @@ require 'minitest/autorun' require_relative 'main' -class RainbowableTest < Minitest::Test - def setup - Object.include Rainbowable +class EffectTest < Minitest::Test + def test_reverse + effect = Effects.reverse + assert_equal 'ybuR si !nuf', effect.call('Ruby is fun!') end - def test_rainbow - expected = "\e[31mH\e[32me\e[33ml\e[34ml\e[35mo\e[36m,\e[31m \e[32mw\e[33mo\e[34mr\e[35ml\e[36md\e[31m!\e[0m" - assert_equal expected, 'Hello, world!'.rainbow - expected = "\e[31m[\e[32m1\e[33m,\e[34m \e[35m2\e[36m,\e[31m \e[32m3\e[33m]\e[0m" - assert_equal expected, [1, 2, 3].rainbow + + def test_echo + effect = Effects.echo(2) + assert_equal 'RRuubbyy iiss ffuunn!!', effect.call('Ruby is fun!') + + effect = Effects.echo(3) + assert_equal 'RRRuuubbbyyy iiisss fffuuunnn!!!', effect.call('Ruby is fun!') + end + + def test_loud + effect = Effects.loud(2) + assert_equal 'RUBY!! IS!! FUN!!!', effect.call('Ruby is fun!') + + effect = Effects.loud(3) + assert_equal 'RUBY!!! IS!!! FUN!!!!', effect.call('Ruby is fun!') + end +end + +class WordSynthTest < Minitest::Test + def test_play_without_effects + synth = WordSynth.new + assert_equal 'Ruby is fun!', synth.play('Ruby is fun!') + end + + def test_play_with_reverse + synth = WordSynth.new + synth.add_effect(Effects.reverse) + assert_equal 'ybuR si !nuf', synth.play('Ruby is fun!') + end + + def test_play_with_many_effects + synth = WordSynth.new + synth.add_effect(Effects.echo(2)) + synth.add_effect(Effects.loud(3)) + synth.add_effect(Effects.reverse) + assert_equal '!!!YYBBUURR !!!SSII !!!!!NNUUFF', synth.play('Ruby is fun!') end end