T_Y_CODE

プログラミング学習記録

学習記録 3日目

今日も頑張っていきます。三日坊主にならないよう習慣化したいです。

学習計画

  • 正規表現
  • プロを目指す人のためのRuby入門 9章
  • リーダブルコード 8章

学習内容

正規表現

以下のサイトを読んで正規表現についてさらに深く学んでいきます。
https://qiita.com/jnchito/items/b0839f4f4651c29da408
学んだことを書いていきます。

  • 英単語の抽出

英単語やメソッドの抽出には\bを使用する。

# sounds that are pleasing to the ear.
# ear is the organ of the sense of hearing.
# I can't bear it.
# Why on earth would anyone feel sorry for you?

regexp = /ear/ # hearのearも抽出されてしまう
regexp = /\bear\b/ # \bはスペース, ピリオド, ダブルクオート, 行頭, 行末等を表してくれる。
  • ファイル名の抽出

今まで学習した内容だと以下の正規表現で抽出出来る。

text = <<~TEXT
  type=zip; filename=users.zip; size=1024;
  type=xml; filename=posts.xml; size=2048;
TEXT
regexp = /filename=([^;]+)/ # これでも抽出できる
filename = text.match(regexp).to_a #1つしか抽出出来ない。scanメソッドを使用すると配列の配列になってしまう。
#=> ["users.zip"], ["posts.xml"]
# .to_aしないとMatchDaataオブジェクトとして帰ってくる

後読みを使用することでキャプチャの必要がなくなる。

text = <<~TEXT
  type=zip; filename=users.zip; size=1024;
  type=xml; filename=posts.xml; size=2048;
TEXT
regexp = /(?<=filename=)[^;]+/ # (肯定の)後読み
filename = text.scan(regexp) #=> ["users.zip"], ["posts.xml"]
#配列の配列になっていない。
  • 先読み, 否定形

先読みはマッチさせたい文字列の後に続く文字列の正規表現を指定する。

text = <<~TEXT
  John:guitar, George:guitar, Paul:bass, Ringo:drum
  Freddie:vocal, Brian:guitar, John:bass, Roger:drum
TEXT
regexp = /\w+(?=:bass)/ # :bassの前にある\w+を取得
names = text.scan(regexp) #=> ["Paul"], ["John"]

後読みの否定形はマッチさせたい文字列の前に続く文字列が正規表現と合っていないものを探す。

text = <<~TEXT
  東京都
  千葉県
  神奈川県
  埼玉都
TEXT
regexp = /(?<!東京)/ # 東京以外に都が続く文字列を探す。
text.gsub!(regexp, '') # 県とは限らないがこれは使用例のためこのままでいきます。
#=> "東京都\n千葉県\n神奈川県\n埼玉県\n"

先読みの否定はマッチさせたい文字列の後に続く文字列が正規表現と合っていないものを探す。

text = <<~TEXT
  つぼ焼きにしたサザエはおいしい
  日曜日にやってるサザエさんは面白い
TEXT
regexp = /サザエ(?!さん)/ # 1行目のサザエにマッチする。
  • ツイート日時を抽出する

まずは秒分時を抜き出す。

text = <<~TEXT
  You say yes. - @jnchito 8s
  I say no. - @BarackObama 12m
  You say stop. - @dhh 7h
  I say go go go. - @ladygaga Feb 20
  Hello, goodbye. - @BillGates 11 Apr 2015
TEXT
regexp = /\d+[smh]/
date = text.scan(regexp) # => ["8s", "12m", "7h"]

1日以上前の場合は「アルファベット3文字+数字」となっている。

#省略
regexp = /[A-Z][a-z]{2} \d+/
date = text.scan(regexp) # => ["Feb 20", "Apr 2015"]

1年以上前のツイートは「数字+アルファベット3文字+数字」となっている。
|

1日以上前の場合は「アルファベット3文字+数字」となっている。

#省略
regexp = /(?:\d+ )?[A-Z][a-z]{2} \d+/ #(?:)でキャプチャの対象外にする
date = text.scan(regexp) # => ["Feb 20", "11 Apr 2015"]

OR条件で組み合わせ日時を抜き出す。

text = <<~TEXT
  You say yes. - @jnchito 8s
  I say no. - @BarackObama 12m
  You say stop. - @dhh 7h
  I say go go go. - @ladygaga Feb 20
  Hello, goodbye. - @BillGates 11 Apr 2015
TEXT
regexp = /\d+[smh]|(?:\d+ )?[A-Z][a-z]{2} \d+/
date = text.scan(regexp) # => ["8s", "12m", "7h", "Feb 20", "11 Apr 2015"]

()外に+や*が来る場合パフォーマンスが悪くなる。以下のサイトでパフォーマンスをある程度チェック出来る。
regex101.com

プロを目指す人のためのRuby入門 9章

  • Rubyではエラーが発生したらその時点でプログラムが停止する。
puts 'Start.'
module Greeter
  def hello
    'hello'
  end
end
greeter = Greeter.new
puts 'End.'
# => Start.
#    NoMethodError: undifined method 'new' for Greeter:Module
  • 例外処理は以下のようにbegin - rescue - end構文で行う。ただし以下の例文はあまり好ましいコードではない。
puts 'Start.'
module Greeter
  def hello
    'hello'
  end
end
begin
  greeter = Greeter.new
rescue
  puts '例外が発生したが、このまま続行する'
end
puts 'End.'
# => Start.
#    例外が発生したが、このまま続行する
#    End.
  • Rubyでは発生した例外自身もオブジェクトのため例外オブジェクトのメソッドを使用することで例外の情報を取得できる。
begin
  1 / 0
rescue => e
  puts "エラークラス: #{e.class}"
  puts "エラーメッセージ: #{e.message}"
  puts e.backtrace
end
# => エラークラス: ZeroDivisionError
#    エラーメッセージ: divided by 0
#    省略
  • エラークラスを指定してrescueすることが可能。rescueは複数記載可能。
begin
  1 / 0
rescue ZeroDivisionError
  puts "0で除算しました"
rescue NoMethodError
  puts "存在しないメソッドが呼び出されました"
end
# => 0で除算しました
  • 例外クラスを複数指定したり、変数へ格納することも可能。
begin
  1 / 0
rescue ZeroDivisionError, NoMethodError => e
  puts "0で除算したか、存在しないメソッドが呼び出されました"
  puts "エラー: #{e.class} #{e.message}"
end
# => 0で除算したか、存在しないメソッドが呼び出されました
#    エラー: ZeroDivisionError divided by 0
  • 例外クラスはExceptionクラスを継承している。ExceptionクラスのサブクラスはStandardErrorクラスとそれ以外の例外クラスがいます。通常の例外はStandardErrorクラスのサブクラスに該当する。(NoMethodError等) rescue節に補足される例外はこのStandardErrorクラスを継承したクラスに該当する例外のみである。(クラスの親子関係はP.337 図9-3参照)
  • rescueが複数ある場合上から順番に検証される。例外クラスの継承関係を考えてrescueの順番を決定しないと実行されないrescue節が出てきてしまう。
begin
  # NoMethodErrorを発生
  'abc'.foo
rescue NameError
  puts "NameErrorです"
rescue NoMethodError
  puts "NoMethodErrorです"
end
# => NameErrorです
  • ネットワークエラーのように一時的に発生している問題が原因であれば、何度かやりなおすことで正常にプログラムが実行される可能性がある。retryを使用するとbegin節の最初からやり直してくれる。
  • 意図的に例外を発生させるにはraiseメソッドを使用する。raiseメソッドの第1引数に例外, 第2引数にエラーメッセージを渡す。省略可だが通常は原因を特定しやすくするため省略はしない。
  • 安易にrescueは使用しない方がいい。例外が発生したら即座に異常終了させるかフレームワーク(Rails等)の共通処理に任せる。
  • rescueする場合、後に原因調査が出来るように例外時の状況を記録に残しておく。最低でも例外クラス名, エラーメッセージ, バックトレースの3つはログやターミナルに出力するべき。
  • 例外処理を書く場合は、例外が発生しそうな箇所と発生しそうな例外クラスを予測し、予測をコードに反映させる。例外処理の範囲が広すぎると本来異常終了すべき例外も拾ってしまう。(具体例はP344-345のコード参照)
  • 条件分岐で対応出来るものは例外処理は使わない。(具体例はP345のコード参照) 条件分岐を使用する方が可動性やパフォーマンスの面で有利。
  • case文で条件分岐する場合はelseに例外処理を入れる。
def currency_of(country)
  case country
  when :japan
    'yen'
  when :us
    'dollar'
  else
    raise ArgumentError, "無効な国名です。#{country}"
  end
end
currency_of(:italy) #=>ArgumentError, "無効な国名です。italy
  • getsメソッドを使用するとターミナル上でユーザーの入力を待つ。
  • gets.chompと記載することで改行\nを削除出来る。
  • 例外の有無に関わらず実行する処理はensure節に記載する。
  • 但しRubyの場合ensureを使用しなくてもブロックを使って同等の処理を実現している場合がある。(例. File.open)
  • 例外が発生しなかった場合の実行処理はelse節に記載する。ただしbegin節に例外が発生しなかった場合の処理を記載してしまえばいいためelse節はあまり使用されていない。
begin
  # 例外が発生するかもしれない処理
rescue
  # 例外が発生した場合の処理
else
  # 例外が発生しなかった場合の処理
ensure
  # 例外の有無に関わらず実行する処理
end
  • 例外処理は最後の式が戻り値になる。変数やメソッドへ格納できる。
  • ensure節にreturnを使用してしまうと正常時・例外時問わずenture節の値が戻り値になってしまうため記載してはいけない。
  • rescue修飾子を使用することでコードを簡潔に出来る場合がある。
begin
  Date.parse(string)
rescue ArgumentError
  nil
end
#上記のコードは以下のコードに書き換え可能
Date.parse(string) rescue nil
  • Rubyでは最後に発生した例外は組み込み変数の$!に格納される。バックトレース情報は$@に格納される。
  • メソッドの最初から最後まで例外処理の対象になっている場合はbegin-endを省略できる。
def method(n)
  # 処理
rescue
  # 例外処理
end
  • 例外クラスはStandardErrorクラスを継承することで独自に定義することが可能。

リーダブルコード 8章

  • if文の条件式が複雑な場合は一旦変数に代入してあげる。変数名で式を表す。このことを説明変数という。
  • 条件式を変数にいれその式がどういう内容なのかを変数名で表す。このことを要約変数という。
  • ド・モルガンの法則を使用すると条件式が短くなる
if !(A && !B)
# 以下と同意
if !A || B
  • 巨大な文が出来上がってしまったら分割を考える。要約変数として関数の最上部に抽出してあげる。DRY原則に沿ったコードにもなる。

本日の総括

  • 正規表現の先読み・後読み・否定形は使用していかないと覚えられないと感じた。とりあえずそういうものがあると覚えておき都度文法の確認をして定着させていきたい。
  • 例外処理は例外が発生しそうな場合でもよく考えて使用しなければならない。Railsを使用する場合は基本的にはRailsの処理に任せてしまった方がいい。
  • 説明変数・要約変数は使用していこうと思った。コードを書いていくとひと目で分かりづらいif文を作ってしまうことが多い。