This commit is contained in:
Rikuoh Tsujitani 2024-03-31 22:42:16 +09:00
parent 68b1d47934
commit 1efe9a6e66
Signed by: riq0h
GPG key ID: 010F09DEA298C717
3 changed files with 99 additions and 74 deletions

View file

@ -384,4 +384,77 @@ variableパターンin節のパターンに変数を書いてローカル変
451ページから
## 3/31
arrayパターンin節に[]を使って配列の構造パターンを指定する利用パターン。[]の中に書いた変数には対応する要素の値が代入される
case [1,2,3]
in[a,b,c]
end
変数ではなく数値や文字列をそのままin節に指定すると「その値と等しいこと」がマッチの条件になる
アンダースコアで始まる変数これは「任意の要素」を表現する目的で例外的にin節で2回以上使うことができる
in節で*を使うと任意の長さの要素を指定したことになる
hashパターンin節に{}を使ってハッシュの構造パターンを指定する利用パターン。値に変数を指定すると、その変数に対応する値が格納される
case {name: 'Alice', age: 20}
in {name: name, age: age}
"name=#{name}, age=#{age}"
end
値の変数を省略するとキーと同じ名前の変数に値が代入される
hashパターンではハッシュの各要素がin節で指定したパターンキーと値、またはキーのみに部分一致すればマッチしたと判定される。ただし、in節に{}を書いた場合は例外的に「空のハッシュに完全一致」することがマッチの条件になる
hashパターンのin節はkey:value形式のパターンしか許容されていない。key => value形式を使おうとすると構文エラーが発生する
in節に書くのはあくまでパターンパターンは一見、配列やハッシュのように見える場合があるが、これらはあくまで記法を似せただけの「パターン」である。
asパターンパターンマッチでマッチしたオブジェクトを変数に代入する利用パターン
case {name: 'Alice', age: 20, gender :female}
in {name: String => name, age: 18.. => age}
"name=#{name}, age=#{age}"
end
一番外側に=>変数名と書くとマッチしたオブジェクト全体を取得できる
alternativeパターン2つ以上のパターンを指定し、どれかに1つマッチすればマッチしたと見なす利用パターン
case 2
in 0 | 1 | 2
'matched'
end
arrayパターン、hashパターン、asパターンとは組み合わせられるがvariableパターンとはできない
findパターンアスタリスクを2回使って「前と後ろにある任意の要素」をパターンとして表現できるRuby3.0以降の機能を活かしたパターン。これにより、配列野中から特定のパターンに合致する部分を抜き出すことができる。
case [13, 11, 9, 6, 12, 10, 15, 5, 7, 14]
in[*, 10.. => a, 10.. => b, 10.. => c, *]
"a=#{a}, b=#{b}, c=#{c}"
end
ガード式:
case 式
in パターン if 条件式
パターンにマッチし、なおかつ条件式が真になった場合に実行する処理
end
このような形式でin節に条件式を追加できる。case節の式がin節のパターンにマッチすることに加え、この条件式も真になった場合にin節に対応する処理が実行される。以上の条件式をガード式と言う
1行パターンマッチRubyのパターンマッチではcase節を省略して"評価したい式 in パターン"を一行で書くこともできる。マッチすればtrue、しなければfalseが返る
[1,2,3] in [Integer, Integer, Integer] # => true
[1,2, 'x'] in [Integer, Integer, Integer] # => false
また、1行パターンマッチは"式 => パターン"という記法も用意されている。主にパターンマッチを使った変数代入を利用するために使う
変数のスコープに関する注意点Rubyのパターンマッチは独自の変数スコープを作らないので、すでに同名のローカル変数が存在していると意図せず上書きされる可能性がある。パターンマッチ内で新たに定義された変数は外部でも使用できる
ここから第12章
よく発生する例外クラスとその原因
NameError単純なタイプミスであることが多いが、外部ファイルやライブラリのrequireを忘れている場合にも発生することがある
NoMethodError存在しないメソッドや可視性が制限されているため呼び出せないメソッドを呼び出そうとした場合に発生する。特にレシーバがnilになっているケースは非常に多いとされる
TypeError期待しない型がメソッドの引数に渡された時に発生する
ArgumentError引数の数が違ったり、期待する値ではなかったりした場合に発生する
ZeroDivisionError整数を0で除算しようとした時に発生する
SystemStackErrorスタックが溢れた時に発生する。特に間違ってメソッドを再帰呼び出しした場合に発生する
LoadErrorrequireやrequire_relativeの実行に失敗した時に発生する
SyntaxError構文エラー。endやカンマの数に過不足がある、カッコがきちんと閉じられていないなどの原因で起こる
Segmentation fault略してセグフォ。SEGVと呼ばれることもある。Ruby本体や拡張ライブラリに不具合がある時に発生する極めて稀なエラー。
tapブロックパラメータにレシーバをそのまま渡すメソッド。ブロックの戻り値は無視され、tapメソッド全体の戻り値はレシーバ自身になる
debug.gemの代表的なコマンド
step/s 実行を1行進めて停止する。その行にメソッド呼び出しがあればそのメソッドの中に入って停止する(ステップイン)
next/n 実行を1行進めて停止する。その行にメソッド呼び出しがあればそのメソッドを実行してから次の行で停止する(ステップオーバー)
finish/fin 現在実行中のメソッドを最後まで実行し、呼び出し元に戻ってきたところで停止する(ステップアウト)
continue/c プログラムを再開する。停止すべきポイント(ブレークポイント)がなければ、そのプログラムの最後までプログラムを実行する
p/pp Rubyのpやppのように指定された式の値を表示する
リターン 直前に実行したコマンドを繰り返す
help/h 使用可能なコマンドとその説明を表示する。help+コマンド名で特定のコマンドのヘルプを表示することもできる
quit/q デバッガを途中で終了する
パソコンの前から離れる:実際これで解決することがプログラミングに限らずよくある
504ページから
## 4/1

51
main.rb
View file

@ -1,37 +1,22 @@
module Effects
def self.reverse
lambda do |words|
words.split(' ').map(&:reverse).join(' ')
end
end
require 'net/http'
require 'uri'
require 'json'
def self.echo(rate)
lambda do |words|
words.each_char.map { |c| c == ' ' ? c : c * rate }.join
end
end
module LogFormatter
def self.format_log
uri = URI.parse('https://samples.jnito.com/access-log.json')
json = Net::HTTP.get(uri)
log_data = JSON.parse(json, symbolize_names: true)
def self.loud(level)
lambda do |words|
words.split(' ').map { |word| word.upcase + '!' * level }.join(' ')
end
end
end
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
log_data.map do |log|
case log
in {request_id:, path:, status: 404 | 500 => status, error:}
"[ERROR] request_id=#{request_id}, path=#{path}, status=#{status}, error=#{error}"
in {request_id:, path:, duration: 1000.. => duration}
"[WARN] request_id=#{request_id}, path=#{path}, duration=#{duration}"
in {request_id:, path:}
"[OK] request_id=#{request_id}, path=#{path}"
end
end.join("\n")
end
end

View file

@ -1,46 +1,13 @@
require 'minitest/autorun'
require_relative 'main'
class EffectTest < Minitest::Test
def test_reverse
effect = Effects.reverse
assert_equal 'ybuR si !nuf', effect.call('Ruby is fun!')
end
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!')
class LogFormatterTest < Minitest::Test
def test_format_log
text = LogFormatter.format_log
lines = text.lines(chomp: true)
assert_equal '[OK] request_id=1, path=/products/1', lines[0]
assert_equal '[ERROR] request_id=2, path=/wp-login.php, status=404, error=Not found', lines[1]
assert_equal '[WARN] request_id=3, path=/products, duration=1023.8', lines[2]
assert_equal '[ERROR] request_id=4, path=/dangerous, status=500, error=Internal server error', lines[3]
end
end