「プロを目指す人のためのRuby入門」を読む!!!
はじめに
プロを目指す人のためのRuby入門[改訂2版] 3周目、開始しました🏃♀️💨
尊敬するRubiestはみなさん声を揃えて
「必読です!!」
「何度でも読んだ方がいい!!!」
「今でもたまに見かえします」
と言われます。
私は、過去2回読んでいますが、理解しないままスルーしている部分が多いため、今回は記事としてまとめることにしました。
アウトプット、アウトプット、アウトプット・・・・📝
各章で学んだこと
第1章 本書を読み進める前に(2023.11.08)
- Railsをやる前にまずRubyを知ることが大事!
- テキスト内の例題の進め方
1回目→見ながら一緒に解く
2回目以降→問題だけ見て自分で解く→解答と見比べる→リファクタリングする - Rubyとはどんな言語か
- ストレスなくプログラミングできる「楽しさ」を重視した言語
- Macで最新版のRubyのインストールをしたい場合はrbenvやRVMなどのようなRuby専用のパッケージ管理ツールを利用するのがベスト
- ※Homebrewなどでは必ずしも最新バージョンではないこともある
- エディタやIDEで不可欠な機能や設定について
- シンタックスハイライトの設定
- 複数行の一括コメントアウト、コメント解除
- 短形選択・矩形編集
- タブキーによるインデント幅の設定
- end キーワードの自動入力、自動インデント
- 複数ファイルの同時編集、タブ表示
- ウインドウの分割
- ファイルツリーの表示
- インデントの可視化
- 全角スペースの可視化
- やる気の出るカラースキームや背景画像
- 参考:【Ruby初心者向け】テキストエディタ、ちゃんと設定できてる?使いこなせてる?チェックリスト10項目
- 環境構築:Ruby3.0
- Rubyのバージョンを確認するコマンド
ruby -v
- Rubyのバージョンを確認するコマンド
- Rubyを実行する方法
irb
# rubyはスクリプト言語のため、コンパイルなしで直接のファイル名を指定しても実行できる
ruby ファイルパス
- Ruby公式リファレンスの使い方について
- URLにバージョンを指定することができる
- 例)https://docs.ruby-lang.org/ja/2.7.0/class/String.html
- バージョンのところをlatestとすると最新版で検索できる
- URLにバージョンを指定することができる
- この本を最後まで読み切るコツ
- 頭の中にインデックスを作っていく
- 各章の例題だけは最低限自分で書く
- 「必要になったらあとで読み返す」という気持ちでどんどん読み進める
- 必ず最後まで読み終える
第2章 Rubyの基礎を理解する(2023.11.08)
- Rubyはすべてがオブジェクト
- 基本型 オブジェクト、メソッド(引数1、引数2,引数3)
- 引数のカッコを省略することができる
- 引数がなければメソッド名だけ書いてもOK
- 改行ごとにメソッドが実行されるため、セミコロンなどの区切りは不要
- ( )内の改行など、文が続くことが明らかな場合はカッコが閉じられるまで改行してもエラーにならない
- バックスラッシュを使って文が続くことを明示的に示すこともできる
- コメントの書き方
- # をつかう
- =begin =end を使う
※特別な使い方をするため単なるコメントにはあまり使用しない
- 予約語は識別子(変数名やメソッド名、クラス名)として使えない
- 変数を宣言するには何かしらかの値を代入する必要がある
- 変数名はスネークケースで書く
- 多重代入 (2つ以上の値を同時に代入)することもできる
# 2つの値を同時に代入する
a,b =1,2
a #=> 1
b #=> 2
# 右辺の数が少ない場合はnilが入る
c. d = 10
c #=> 10
d #=> nil
# 右辺の数が多い場合ははみ出した値が切り捨てられる
e, f = 100,200,300
e #=> 100
f #=> 200
- 文字列の比較の方法について
- 式展開などを行いたい場合はダブルクォートを使う
- ==や>=などを使って文字列を比較することができる
- 数字の比較の方法について
- 小数点以下の値が必要な場合は、計算する値のいずれかに小数点の.0を付ける
- 変数に整数が入っている場合は、to_sメソッドを呼ぶことで整数から少数に変更できる
- Rubyにおける真偽値について
- falseまたはnilであれば偽
- それ以外はすべて真
- if文は最後に評価された式を戻り値として返す
- else節がなく、かつどの条件にも合致しない場合はnilが返る(falseではない)
- 後置ifを使うとスッキリ書ける
- メソッド名はスネークケースで書く
- 最後に評価された式がメソッドの戻り値となる
- returnは省略するのが一般的
- returnはメソッドを途中で抜ける時にあえて使われることが多い
- 引数がない場合は( )をつけない
- 引数があっても( )を省略できる
- 例題の「FizzBuzzプログラム」を作ってメソッドに対する理解を深めた
- いろんな書き方etc
-
%q! !
:シングルクオートで囲んだことと同じ -
%Q! !
、%! !
:ダブルクオートで囲んだことと同じ - 「ヒアドキュメント」をつかうと複数行にわたる長い文字列をスッキリ書ける
- 識別子は大文字で書くことが多い
- 例)TEXT、HTMLなど
-
- 「sprintf」メソッドを使うと指定されたフォーマットの文字列を作成できる
- いろいろな方法で文字列を作成できる
to-s
.join
*演算子
- 数値の扱い方についてあれこれについて
- 式における真偽値と条件分岐について
- 式全体が真または偽であることが決定するまで左から順に式を評価する
- 式全体の真または偽が確定すると、式の評価を終了し、最後に評価した式の値を返す
- 「and、or、not」と「&&、||、‼︎」では優先順位の違いのため、似ているが異なる動きをする
- if+否定条件は、unless +背定条件に書き直すことができる
- コードの読みやすさによって使い分けるとgood
- ==true や== falseは冗長なので書かない
- == nilや!= nilも書かない
- 「対象データが未存在である」ということを明示するために、「nil?メソッド」が使われることがある
- nil?メソッドはオブジェクトがnilだった場合にtrue を返すメソッド
- 「対象データが未存在である」ということを明示するために、「nil?メソッド」が使われることがある
- elseifが増えてきたらcaseを使ってみるとコードがスッキリする場合がある
- 条件演算子(三項演算子)を使う時も、複雑な条件文のときはかえってわかりづらくなるため注意が必要
- 常にコードの可読性を考慮することが大切
- メソッドを定義する際にデフォルト値を設定しておくと引数なしで呼び出してもエラーにならない
- Ruby3.0ではエンドレスメソッドが使える
- endを省略して=に続けて処理や戻り値を書く
- Rubvの変数にはオプジェクトそのものではなく、オプジェクトへの参照が格納されている
-
require 標準ライブラリ名
で読み込む -
require_relative 自作プログラム
で読み込む - 出力メソッドの使い分けについて
-
puts
改行する、自身の戻り値はnil -
print
改行しない -
p
改行する、文字列は" "で囲まれる、引数で渡されたオブジェクトそのものが戻り値となる -
pp
大きく複雑な配列やハッシュ、オブジェクトの内容を整形して出力する、引数で渡されたオブジェクトそのものが戻り値となる
-
第3章 テストを自動化する(2023.11.09)
- プログラマの三大美徳「怠惰・短気・傲慢」を実現するためにテストの自動化をしよう!
- Minitestでテストを行なっていく
- Rubyインストールと同時にインストールされる
- 学習コストが比較的低い
- Railsに活かしやすい
- Minitestのバージョンを確認する
ruby -r minitest -e "puts Minitest::VERSION"
- Minitestを使う手順
- プログラムの実行結果を検証するテストコードを書く
- テストコードを実行する
- 実行結果をチェックして、その結果が正しいか間違っているか報告する
- Minitestテストコードの雛形
# ライブラリを読み込む
require 'minitest/autorun'
# SampleTest という名前のクラスを作成する
# <Minitest::Testというクラスを継承している
class SampleTest < Minitest::Test
# Minitest はtest_で始まるメソッドを探して実行する
def test_sample
assert_equal 'RUBY', 'ruby' .upcase
end
end
- assert_equalメソッドの使い方
基本構文assert_equal 期待する結果,テスト対象となる値や式
# aがbと等しければパスする
assert_equal b, a
# aが真であればパスする
assert a
# aが偽であればパスする
refute a
参照:minitest: Ruby Standard Library Documentation
- テストが失敗するとそのテストメソッドはそれ以上実行されない
- 実行対象のテストメソッドが複数あった場合は次のテストメソッドの実行に移る
- Minitest以外のテストフレームワーク
- test-unit:Rubyをインストールすると自動でインストールされる
- RSpec:gemでインストールする必要がある
第4章 配列や繰り返し処理を理解する(2023.11.10)
配列とは複数のデータをまとめて格納できるオブジェクト
配列内の要素は順番に並んでいて、インデックスを指定することでそのデータを取り出すことができる
instance method Array#[]
- 配列の中に配列を含めることもできる
- 存在しない要素を指定するとnilが返る(エラーにはならない!)
- sizeメソッド、lengthメソッドで要素の個数を取得できる
- 添字を指定して値を代入すると上書きすることができる
- 元の大きさよりも大きい添え字を指定するとそこ番号に指定した値が設定でき、間の値はnilで埋まる
- << を使うと配列の最後に要素を追加できる
- 特定の位置にある要素を削除する時はdeleted_atメソッドを使う
- 多重代入もできる
- 繰り返し処理について他の言語との比較
- JavaScriptではfor分やforEachメソッドを使う
- Rubyはeachメソッドとブロックを使うことが多い
- 配列に対してeachメソッドを仕様して配列の要素を最初から最後まで順番に取り出す
- 取り出した要素をどう処理するかの内容をブロック内に技術する
- eachメソッドから渡された配列の要素を|ブロックパラメータ| に格納する
- ブロックの戻り値は最後に評価された式
- ブロックパラメータを使わない時は省略できる
- ブロック内の変数はブロックの中でのみ有効
-
do〜end
まで1行で書いても良い -
do〜end
を{ }で書いても良い - ブロックで使えるメソッドいろいろ
-
map
:ブロックを評価した結果を新しい配列にして返す -
select
:ブロックを評価した戻り値が真の要素を集めた配列を返す -
reject
:ブロックを評価した戻り値が真の要素を集めた配列を返す -
find
:ブロックの戻り値が真になった最初の要素を返す -
sum
:要素を合計する- 文字列の連結にも使える
- 単純な要素の連結であればjoinメソッドでもOK
- 条件が揃ったらブロックの代わりに&:メソッドを引数として渡すこともできる
- ブロックパラメータが1個だけである
- ブロックの中で呼び出すメソッドには引数がない
- ブロックの中では、ブロックパラメータに対してメソッドを1回呼び出す以外の処理がない
-
- Rangeオブジェクトで範囲を指定できる
- 他の言語では標準サポートでない場合が多い
(例題)テスト駆動型でRGB変換プログラムを作成していく
- RGBカラー:1つの色を表すためにRed(赤)、Green(緑)、Blue(青)の3色を数値化したもので以下の2パターンで表すことができる
- 10進数の整数で表現される
- 2桁の16進数を3つ並べた文字列で表現される(例:#4169e1 )
- RGB変換プログラムの仕様
- 10進数を16進数に変換するto_hexメソッドと、16進数を10進数に変換するto_intsメソッドの2つを定義する。
- to_hexメソッドは3つの整数を受け取り、それぞれを16進数に変換した文字列を返す。文字列の先頭には“#”を付ける
- to_intsメソッドはRGBカラーを表す16進数の文字列を受け取り、R、G、Bのそれぞれを10進数の整数に変換した値を配列として返す
- rjustメソッド
- 第1引数には桁数を指定する
- デフォルトは空白(半角スペース)で桁揃えされる
- 第2引数を指定すると空白以外の文字列を埋めることができる
- scanメソッドは正規表現にマッチした文字列を配列にして返す
- 上級編のリファクタリングは一緒にやってみたけど、まだインデックスにとどまっている感じ😅
- テスト駆動型の基本パターン
- 先にテストを書いて失敗させる
- テストがパスするような最小限のコードを書く
- リファクタリングする。
- テストを書きながらコードを書く時の進め方
- テストコードを書く
- テストが失敗することを確認する
- 1つのテストをパスさせるための仮実装を書く
- テストがパスすることを確認する
- 別のテストパターンを書く
- テストが失敗することを確認する
- 仮実装ではなく、ちゃんとしたロジックを書く
- テストがパスすることを確認する
- ロジックをリファクタリングする
- テストがパスすることを確認する
- concatメソッドを使って配列を連結すると、元の配列 が変更される
- +を使うと元の配列を変更せず、新しい配列を作成する
- 配列の和集合、差集合、積集合の方法を使って配列を操作することができる
- Setクラスを使うと配列よりも効率よく集合を扱える
- %記法を使って文字列の配列を作ることができる
- カンマではなく空白文字(スペースや改行)が要素の区切り文字となる
- 文字列をシングルクオートやダブルクオートで囲む必要もないため、結果として[]を使う場合よりもコードが短くなる
- charsメソッド、splitメソッドなどをつかって文字列を分解して配列に変換することができる
- Array.newに引数を渡すと、その個数分の要素が追加される
- デフォルト値はnil
- 第2引数を指定すると、nil以外のデフォルト値を設定できる
# 要素数が10で、1, 2, 3, 1, 2, 3...と繰り返す配列を作る
a = Array.new(10) { |n| n % 3 + 1 }
a #=> [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
- イミュータブル(変更できない)な値(クラス)のデータ型
- 数値(IntegerクラスやFloatクラス)
- シンボル(Symbolクラス)
- true/false(TrueClassクラスとFalseClassクラス)
- nil(NilClassクラス)
- 範囲(Rangeクラス)
- 文字列では[]や<<を使って配列と同じような操作をすることができる
- each_with_indexメソッドを使うとブロックパラメータの第2パラメータに添え字を渡してくれる
- スクールのプラクティス「カレンダープログラム」作る時に使ったやつ🎵
- with_indexメソッド はmapメソッド、delete_ifメソッドなどと組み合わせて使うこともできる
- 添え字を0ではなく指定した数字から付けることもできる
- 引数にインデックスを開始する数字をわたしてやる
- each_with_indexでは添え字の開始を指定できない
- each.with_index(1)の形で呼び出す必要符がある
- ブロックパラメータに配列を渡す方法はさまざまなパターンがあるため、取り出したいデータのかたちに応じてコードがなるべく簡素になるよう工夫が必要
- その他の繰り返し処理
- メソッド:times、upto、downto、step、loop
- 構文:while、until、for
- 再帰呼び出し
- 似たような機能のメソッドや構文でも、変数のスコープの範囲が異なっていたりするため、使用条件に合ったメソッドや構文を選んで使うことだ大切
- 繰り返し処理のための制御構造についてはインデックス・・・😓
- break、throw〜catch、next、redoなど
第5章 ハッシュやシンボルを理解する(2023.11.16)
ハッシュはキーと値の組み合わせでデータを管理するオブジェクト(※ほかの言語では連想配列)
class Hash
#空のハッシュを作る{}
#キーと値の組み合わせ(要素)を3つ格納する
ハッシュ{キー1=>値1,キー2=>値2,キー3=>値3}
- 新しい要素を追加する場合
ハッシュ[キー]=値
- eachメソッドを使うと、キーと値の組み合わせを順に取り出せる
- キーと値は格納した順に取り出される
currencies{'japan'=>'yen','us'=>'dollar','india'=>'rupee'}
currencies.each do|key,value|
puts"#{key}:#{value}"
end
#=>japan:yen#us:dollar#india:rupee
- ブロックパラメータを1つにするとキーと値が配列に格納される
currencies{'japan'=>'yen','us'=>'dollar','india'=>'rupee'}
currencies.each do|key_value|
key=key_value[0]
value=key_value[1]
puts"#{key}:#{value}"
end
#=>japan:yen#us:dollar#india:rupee
※並び順が異なっていてもキーと値がすべて同じならtrue
- シンボルとは任意の文字列と一対一に対応するオブジェクト
class Symbol - 文字列の代わりに用いることもできる
- 必ずしも文字列と同じ振る舞いをするわけではない
- 表面上は文字列っぽいので、プログラマにとって理解しやすい
- 同じ内容のシンボルはかならず同一のオブジェクト
- 内部的には整数なので、2つの値の比較などの場合コンピュータは高速に値を比較できる
- 同じシンボルであればまったく同じオブジェクト
- 大量に使用した場合、文字列に比べてシンボルのほうがメモリの使用効率が良い
- イミュータブルなオブジェクトなので、勝手に値を変えられる心配がない
- ソースコード上では名前を識別できるようにしたいが、その名前が必ずしも文字列である必要はない場合などに使われる
- ハッシュのキーにシンボルを使うと、文字列よりも高速に値を取り出すことができる
(例題)長さの単位変換プログラムを作成
- メートル(m)、フィート(ft)、インチ(in)の単位を相互に変換する。
- 第1引数に変換元の長さ(数値)、第2引数に変換元の単位、第3引数に変換後の単位を指定する。
- メソッドの戻り値は変換後の長さ(数値)とする。端数が出る場合は小数第3位で四捨五入する。
- Columnのメソッド定義のキーワード引数を参照する際、メソッドを呼び出す場合の引数に関する「シンボルか否か?」についてはあまりよく理解できていないけど、使いながら慣れれば良いと書かれていたので、一旦スルー😓
- ハッシュて使用できるメソッド
- keys、values
- has _key?/key?/include?/member?
- **をハッシュの前につけると{ }内でハッシュの要素を展開することができる
- 類似キーワード引数という使い方があるが、あまり使わないほうがよさそう
- 任意のキーワードを同時に受け取りたい場合は**を付けた引数を使う
- **を付けた引数は最後に使わなければエラーになる
- to_aメソッド:ハッシュを配列に変換する
- to_hメソッド:配列をハッシュに変換するこ
- nil以外の値をハッシュのデフォルトに設定したい場合
- Hash.newでハッシュを作成し、引数にデフォルト値となる値を指定する
- #キーがなければ'hello'を返す
- h=Hash.new('hello')
- h[:foo]#=>"hello"
- 文字列や配列などのミュータブルなオブジェクトをデフォルト値として返す場合はHash.newとブロックを組み合わせてデフォルト値を返すことで破壊的な変更を防ぐことができる
- Hash.newでハッシュを作成し、引数にデフォルト値となる値を指定する
- to_symメソッド:文字列をシンボルに変換する
- to_sメソッド:シンボルを文字列に変換する
- 「nilガード」という高度な技を使って変数にnil以外の値を入れておくことができる
第6章 正規表現を理解する(2023.11.17)
(例題) ハッシュ記法変換プログラム
=>を使う記法で書いた文字列を変換メソッドに渡すと、=>を使わないハッシュ記法の文字列が戻り値として返ってくる
伊藤淳一さんのQiita記事で、正規表現の基本をおさらい!
初心者歓迎!手と目で覚える正規表現入門
- 正規表現を使うときは、最初に検索対象文字列の 「法則(パターン)」 をうまく見つけだすのが大切
メタ文字 | 役割 |
---|---|
\d | 1個の半角数字 |
\D | 半角数字以外 |
\w | 英単語を構成する文字 |
\W | 英単語の構成文字以外(記号や空白文字など) |
{n,m} | 直前の文字が n 個以上、m 個以下 |
{n,} | 直前の文字が n 個以上 |
{,n} | 直前の文字が n 個以下 |
{n} | ちょうど n 文字 |
AB | AまたはBのいずれか1文字 |
[a-z] | ハイフンで範囲を表す |
? | ~が1文字、または無し ※2文字以上の文字列に対して使う場合は、対象となる文字列を ( ) で囲んでグループ化する |
. | 任意の1文字 |
+ | 直前の文字が 1文字以上 ※貪欲なマッチに注意が必要 |
* | 直前の文字が0文字以上 ※貪欲なマッチに注意が必要 |
*?、 +? | (控えめなマッチ)を使うと最短の(最長ではなく)マッチを結果として返す(最小量指定子) |
\t | タブ文字 |
\s | 空白文字全般 |
\S | 空白文字以外 |
\n | 改行文字が含まれる |
^ | [ ]の最初に ^ が入ると否定の意味になる ※否定条件になるのは [ ] の先頭に ^ が来たときだけ |
[^A] A以外の 任意の文字 | |
[^AB] AでもなくBでもない任意の1文字 |
アンカー | マッチした 「位置」 を示す |
---|---|
^ | 行頭 |
$ | 行末 |
\b | 単語の境界 |
\B | 単語の境界以外 |
条件 | |
---|---|
(?<=abc) | "abc" 文字列そのものではなく、その文字列の「直後の位置」にマッチする(肯定の後読み) ※Rubyの scan メソッドや、JavaScriptの match メソッドで使用すると便利 |
(?=abc) | "abc" 文字列そのものではなく、その文字列の「直前の位置」にマッチする(肯定の先読み ) |
(?<!abc) | "abc" 文字列 以外 の「直後の位置」にマッチする(否定の後読み) |
ABC|DEF | 文字列ABC、または文字列DEF ※OR条件の範囲を明確にするため、(ABC|DEF) のようにグループ化の ( ) と一緒に使われることが多い |
- グループ化
- ( ) を使うとその部分がキャプチャ(捕捉)され、連番が付けられる
- キャプチャする必要がないがグループ化したい(マッチした文字列全体を取得したい)場合は(?: ) をつける
- 含まれる文字が言語や環境によって異なることに注意する
- 特殊文字を検索したい場合はバックスラッシュを付けてエスケープする
- その他豆知識
- タブ文字区切りにすると、ExcelやGoogleスプレッドシートにコピペで値を貼り付けられる
Rubyで正規表現を使う時の技法
-
スラッシュでパターンを囲んで作成する
- /正規表現/
-
文字列と正規表現のマッチを試みる方法
- =~
- 正規表現がマッチした場合(真)は文字列中の最初にマッチした位置(0以上の数値)を返す
- マッチしなかった場合(偽)はnilを返す
- if文でよく使われる
- !~
- マッチしなかったときにtrueを返す
- マッチしたときにfalseを返す
- matchメソッド
- キャプチャを活用する方法の1つ
- 文字列が正規表現にマッチすると、MatchDataオブジェクトが返る
- マッチしない場合はnilが返る
- MatchDataは[]を使って正規表現の処理結果を配列と同じような方法で取得で切る
- match?メソッド
- .scan( ) 引数で渡した正規表現にマッチする部分を配列に入れて返す
-
(?<name>) キャプチャに名前を付けることができる
- シンボル、文字列、連番で名前を指定してキャプチャの結果を取得できる
- 左辺に正規表現リテラルを直接書いて、右辺に文字列を置いて=~演算子を使うと、キャプチャの名前がそのままローカル変数に割り当てられる
- 左辺に文字列、右辺に正規表現リテラルだと使えないので注意!!
- 正規表現オブジェクトをいったん変数に入れた場合も使えない
-
[ ]、sliceに正規表現を渡すと、文字列から正規表現にマッチした部分を抜き出す
- マッチする部分が複数ある場合は、最初にマッチした文字列が返る
- キャプチャを使うと第2引数で何番目のキャプチャを取得するか指定できる
- 名前付きキャプチャは名前をシンボルや文字列で指定することもできる
-
splitに正規表現を渡すと、マッチした文字列を区切り文字にして文字列を分解し、配列として返す
-
gsubメソッド
- 条件を絞り込み、精度を上げることが可能
- 第1引数の正規表現にマッチした文字列を第2引数の文字列で置き換える
- 第2引数にハッシュを渡して、変換のルールを指定することもできる
- 第2引数を渡す代わりに、ブロックの戻り値で置き換える文字列を指定することもできる
- 正規表現に()を使うと、キャプチャされた部分が配列の配列になって返ってくる
-
/ /で囲む以外の正規表現の作成方法
- Regexp.new
- %r
-
正規表現オブジェクト作成時のオプション
-
/正規表現/オプション
- オプションは複数同時に使うこともできる
オプション | 機能 |
---|---|
i | アルファベットの大文字小文字を区別しない |
m | 任意の文字を表すドット(.)が改行文字にもマッチする |
x | 空白文字が無視される ※ #をつかったコメントが書ける |
空白を無視したくない時はバックスラッシュでエスケープする |
第7章 クラスの作成を理解する(2023.11.27)
(例題) 改札機プログラム
3つの駅と運賃が決められている時、乗降する2駅による運賃と購入したチケットの金額により出場できるか否か判断するプログラム
※自分1人ではまだプログラム書けない・・・一気に本格的😭
クラスとは
- 一種のデータ型
- 「オブジェクトの設計図」「オブジェクトのひな形」
- Rubyではオブジェクトは必ず何らかのクラスに属している
- クラスが同じであれば、保持している属性(データ項目)や使えるメソッドは(原則として)同じ
- 内部にデータを保持し、自分が保持しているデータを利用する独自のメソッドを持つことができる仕組み
- データとそのデータに関するメソッドがセットのため、データとメソッドの整理がしやすい
- 大規模のプログラムほど、クラスを使用するメリットが大きい
オブジェクト、インスタンス、レシーバ
- 同じクラスから作られたオブジェクト(=インスタンス:クラスをもとにして作られたデータのかたまり)は同じ属性(データ項目)やメソッドを持つ
- 属性の中に保持されるデータ(名前や数値、色など)はオブジェクトによって異なる
- メソッド
- オブジェクトが持つ「動作」や「振る舞い」などの何らかの処理をひとまとめにして名前を付け、何度も再利用できるようにしたもの
- ほかのプログラミング言語での「関数」や「サブルーチン」
- initializeメソッド
- インスタンスを初期化するために実行したい処理を実装する
- newメソッドを呼び出したときにinitializeメソッドが呼ばれる
- デフォルトでprivateメソッドになっているため外部から呼び出すことはできない
- 引数を付けると、newメソッドを呼ぶときにも引数が必要
- インスタンス変数
- インスタンス変数とは同じインスタンス(同じオブジェクト)のクラス内部で共有される@変数
- 作成(値を代入)する前にいきなり参照してもエラーにならない
- まだ作成されていないインスタンス変数を参照した場合はnilが返る
- ローカル変数は参照する前に必ず=で値を代入して作成する必要があり、まだ作成されていないローカル変数を参照しようとするとエラーが発生する
- クラスの外部から参照することができない
- インスタンス変数の内容を外部から変更したい場合は変更用のメソッドを定義する
- =で終わるメソッドを定義すると、変数に代入するような形式でそのメソッドを呼び出すことができる
- nameメソッドのように値を読み出すメソッドを「ゲッターメソッド」、name=メソッドのように値を書き込むメソッドを「セッターメソッド」という
- 単純にインスタンス変数の内容を外部から読み書きするのであれば、attr_accessorというメソッドを使う
- インスタンス変数の内容を読み取り専用にしたい場合はattr_readerメソッドを使用する
- インスタンス変数の内容を書き込み専用にしたい場合はattr_writerメソッドを使用する
- メソッド名の前にself.を付けるとクラスメソッドを定義することができる
Class名.メソッド名
で呼び出せる
Product.default_price #=> 0 クラスメソッド
product = Product.new
product.default_price #=> 0 インスタンスメソッド
定数について
- 定数は必ず大文字で始める
- アルファベットの大文字と数字、アンダースコアで構成されることが多い
- name=のようなセッターメソッドを呼び出したい場合は必ずselfを付ける
-
self.name = 'Bob'
のように書けば、ローカル変数の代入とは構文が異なるので確実にname=メソッドを呼び出すことができる - Rubyでは「メソッド内にスコープを限定した定数」は定義できない
- メソッドの内認で定数を定義しようとするとエラーになる
- 定数の定義は必ずクラス構文の直下、もしくはトップレベルで行う必要がある
- Rubyの定数は「わざわざ変更するなよ」と周りに念を押した変数のようなもの
- そのままの状験では定数には再代入が可能
- 定数の値を後から書き換えることができる
- 再代入を防ぐにはクラスや定数の値をfreezeする
Class.freeze
定数.freeze
- 定数を参照する方法
クラス名::定数名
- 定数をprivateにする方法
class クラス名
定数名 = 値
private_constant :定数名
end
※ private_constant :クラス名
でクラスをprivateにすることもできる
クラスの継承
- 性質や概念が共通しているかどうか(クラスの継承が適切かどうか)を判断するにはis-aの関係かどうかを見分ける
- 「サブクラスはスーパークラスの一種である(サブクラス is a スーパークラス)」
- 継承できるスーパークラスは1つだけ
- 継承元を指定せずに作成したクラスはデフォルトでObjectクラスを継承する
- クラスを継承させるときの構文
class サブクラス<スーパークラス
end
- クラスを継承するとクラスメソッドも継承される
- スーパークラスを継承したサブクラスの方でもスーパークラスのメソッドが呼び出せる
- スーパークラスと同じメソッド名でサブクラスに定義したら、サブクラスでメソッドのオーバーライド(上書き)される
メソッドの可視化
- publicメソッド
- クラスの外部から自由に呼び出せる
- initializeメソッド以外のインスタンスメソッドはデフォルトでpublic
- privateメソッド
- クラスの外からは呼びどせない
- レシーバがselfに限定される
- privateキーワード以下に定義したらインスタンスメソッドがprivateメソッドとなる※この方法ではクラスメソッドはprivateにならない
- スーパークラスで定義されているprivateメソッドはサブクラスでも呼び出せる
- protectedメソッド
- そのメソッドを定義したクラス自身と、そのサブクラスのインスタンスメソッドから「レシーバ付きで」呼び出せる
※コードの例をみたらなんとなく意味はわかるけど、自分でどうやって使って良いかは思いつかない・・・
- そのメソッドを定義したクラス自身と、そのサブクラスのインスタンスメソッドから「レシーバ付きで」呼び出せる
変数のあれこれ
- インスタンス変数
- クラスをインスタンス化(クラス名.newでオブジェクトを作成)した際に、オブジェクトごとに管理される変数
- クラスインスタンス変数
- インスタンスの作成とは無関係に、クラス自身が保持しているデータ(クラス自身のインスタンス変数)
- クラス自身もインスタンス変数を保持できることと、インスタンス変数とは異なりスーパークラスとサブクラスでは同じ名前でも別の変数になる
- クラス変数
- スーパークラスとサブクラスででも同一の変数として代入・参照可能な変数
@@変数名
- インスタンス変数と違い、未定義のクラス変数を参照することはできない(nilではなくエラーになる)
- グローバル変数
$変数名
- クラスの内部、外部を問わず、プログラムのどこからでも参照可能
- 未定義のグローバル変数を参照したらnilが返る
- グローバル変数の使用は出来る限り避けた方が良い
その他
- Minitestではsetupメソッドを定義するとメソッド実行前に毎回setupメソッドが呼び出される
第8章 モジュールを理解する(2023.11.28)
(例題) rainbowメソッド
- このメソッドを呼ぶと、to_sメソッドで得られる文字列が以下のように1文字ずつ異なる色で出力される文字色変更プログラム
- 31から36(赤~シアン)の6色を1文字ずつ順番に変えながら出力する
- メソッドは文字列(Stringクラス)や配列(Arrayクラス)のように、特定のクラスだけでなくさまざまなクラスで呼べるようにする
1.to_sメソッドを使って自分自身の文字列表現を取得する。
2.取得した文字列を1文字ずつループ処理する。
3.各文字の手前にANSIエスケープシーケンスを付与する。文字色は31から36まで順に切り替え、最後まで進んだらまた31に戻る。
4.各文字を連結して1つの文字列にする。
5.最後に文字色をリセットするための\e[0mを付与する。
リファクタリング後のコード、簡単になりすぎて元コードの面影なし😂
何度も順を追って一緒にリファクタリングやってみたけど、end.joinできる部分くらいしか追いつけず・・・
- モジュールとは
- 複数のクラスにまたがって同じような機能が必要になる場合に共通の処理を共有することができる機能
- モジュールとクラスの違い
- モジュールからインスタンスを作成することはできない
- ほかのモジュールやクラスを継承することはできない
- ミックスインとはモジュールをクラスにincludeして機能を追加すること
- publicメソッドにする必要がなければ、モジュール側でprivateメソッドとして定義しておく
- includeしたクラスでもそのメソッドがprivateメソッドとなる
- クラスの構文の中でincludeやextendを使う、もしくはクラス名.include、またはクラス名.extendの形で呼び出す
- include?メソッド
- クラスオブジェクトに対して、引数で渡したモジュールがincludeされているかどうかがわかる
- included_modulesメソッド
- includeされているモジュールの配列が返る
- ancestorsメソッド
- モジュールだけでなくスーパークラスの情報も配列になって返ってくる
- 名前空間
- 名前の衝突を防ぐために使用する
- クラスのグループ分け/カテゴリ分けをする目的でも使われる
- クラスを探索する際は、クラス、モジュールの入れ子関係を順に外側に向かって探索する
- 他のクラスを参照する場合は入れ子の有無によって挙動が異なる
module Baseball
class Second
def file_with_nesting
# 入れ子ありのクラス定義でFileクラスを参照する
puts File
end
end
end
class Baseball:: Second
def file_without_nesting
# 入れ子なしのクラス定義でFileクラスを参照する
puts File
end
end
- モジュールに特異メソッド(self.メソッド)を定義すれば、モジュール名.メソッド名で呼び出せる
- インスタンスを作らなくて良い(newしなくて良い)場合に便利
- 二重コロン(::)とドット(.)の違い
- ドットの右側は必ずメソッド!
- 二重コロンの左側はクラス、モジュール以外の定数やメソッドなど、どんなオブジェクトでもOK
- モジュール名::クラス名::メソッド名で呼び出すこともできる
第9章 例外処理を理解する
(例題)正規表現チェッカープログラム
ターミナル上で動作する対話型のCUI(character user interface)プログラムとする。
・起動すると“Text?: ”の文言が表示され、正規表現の確認で使うテキストの入力を求められる。
・テキストを入力すると、“Pattern?: ”の文言が表示され、正規表現パターンの入力を求められる。
・正規表現として無効な文字列だった場合は、“Invalid pattern: ”の文言に続いて具体的なエラー内容が表示され、再度パターンの入力を求められる。
・正規表現の入力が終わると、“Matched: ”の文言に続いて、すべてのマッチした文字列がカンマ区切りで表示される。
・1つもマッチしなかった場合は“Nothing matched.”の文言が表示される。
・マッチング結果を表示したらプログラムを終了する
- 例外が発生した箇所がbegin~rescueで囲まれていない場合、処理を中断してメソッドの呼び出しを1つずつ戻っていく
- メソッド呼び出しを戻る途中にその例外を捕捉するコードがあれば、そこから処理を続行する
- messageメソッド
- 例外発生時のエラーメッセージを返す
- backtraceメソッド
- バックトレース情報(メソッドの呼び出し履歴)を配列にして返す
begin
# 例外が起きうる処理
rescue => 例外オブジェクトを格納する変数
# 例外が発生した場合の処理
end
- すべての例外クラスはExceptionクラスを継承している
- その下に多数の例外クラスがサブクラスとしてある
- StandardErrorクラス
- 通常のプログラムで発生する可能性の高い例外を表すクラス
- rescue節に何もクラスを指定しなかった場合
- StandardErrorとそのサブクラスが捕捉される
- StandardErrorのようなスーパークラスを最後に指定すると捕捉もれがなくなる
- rescue節でretry文を実行すると、begin節の最初からやりなおすことができる
- 無条件にretryし続けると、例外が解決しない場合に無限ループを作ってしまう
- カウンタ変数を用意してretryの回数を制限するのが良い
- raiseメソッドを使うと意図的に例外処理を発生させることができる
- raiseメソッドに文字列を渡すと、その文字列がエラーメッセージになるためわかりやすい
- 「例外が発生したらrescueで捕捉すればいい」と安易に考えるのは❌
- 例外が発生したら即座に異常終了、もしくはフレームワークの共通処理に全部丸投げする
- 例外処理にensure節を加えることで、例外が発生してもしなくても必ず実行される処理を書くことができる
第10章 yieldとProcを理解する
(例題)英単語(英文)を加工する「ワードシンセサイザー」を作成するプログラム
- リバース:各単語を逆順にする。
- エコー:指定された回数だけ各文字を繰り返す。
- ラウド:すべて大文字に変換し、各単語の末尾に指定された回数だけ”!"を付ける。
正直、injectメソッドによるリファクタリングの部分は・・・??😓
yield
- メソッドでブロックを利用する際、ブロックの処理を呼び出すのに使う
- 引数を渡すことができる
- ブロックの戻り値を受け取ることができる
- 渡す引数の数が違ってもエラーにならない
- ブロックをメソッドの引数として受け取る場合は引数を&引数 とする
- ブロックを実行するには引数.call
Procクラス
- ブロックをオブジェクト化するクラス
- 「何らかの処理のかたまり」を表す
- procオブジェクトを実行するためには.callメソッドで呼び出す
- arity メソッドは(ブロックパラメータの個数がわかる)Procクラスのインスタンスメソッド
- arityメソッドが呼び出せるのは、メソッド呼び出し時に使ったブロックがProcオブジェクトになっているから
- ラムダを使ってProcオブジェクトを作る方法
-> (a, b) ( a + b}
lambda ( la, bl a + b }
- ラムダはProc.newよりと引数のチェックが厳密
- メソッド呼び出しと同様、引数の数に過不足があるとエラーとなる
- メソッドチェーンでは、メソッドごとに改行させると、コードの横幅を抑えることができ、どんな処理が順に適用されるのか把握しやすくなる
def self.loud(level)
->(words) do words
•split(' ')
•map € Iword word.upcase + '!' * level }
•join(' 1)
end
Discussion