D IT Y

タマキ工務店のIT日記

チェリー本輪読会 第6週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本輪読会の第6週目のエントリーになります。

輪読会の概要については第1週目にまとめています。

🍒 輪読会 第6週目まとめ

第4章4.9.1〜第5章5.3.2まで

期間:2021年06月28日〜2021年07月02日

さまざまな繰り返し処理

Rubyには、同じことを繰り返す方法がいくつかあります。

timesメソッド

繰り返しの回数が決まっているときはtimesメソッドがおすすめです。

timesメソッドは、イテレータとも呼ばれます。イテレータ「繰り返す(iterate) + もの(-or)」という意味です。Ruby 用語としては、もっぱら繰り返し処理のためのブロック付きメソッドのことを指します。

Integer#times (Ruby 3.0.0 リファレンスマニュアル)

Ruby 用語としては、もっぱら繰り返し処理のためのブロック付きメソッドのことを指す。

uptoメソッド、downtoメソッド

Integer#upto (Ruby 3.0.0 リファレンスマニュアル)

Integer#downto (Ruby 3.0.0 リファレンスマニュアル)

stepメソッド

開始値.step(上限値, 1度に増減する大きさ)

Numericとは「数、数的な、数に関するの意」

Numeric#step (Ruby 3.0.0 リファレンスマニュアル)

while文

while文は指定した条件が「真」である間、処理を繰り返します。

while 条件式 (真であれば実行)
    繰り返したい処理
end

until文

while文の反対で、条件が「偽」である間、処理を繰り返します。

until 条件式 (偽であれば実行)
    繰り返したい処理
end

for文

eachメソッド、mapメソッドで代用できるためほとんど出番のない構文です。

普段、輪読会メンバーもfor文を使っている人はいませんでした。

for文はメソッドではありません。Rubyの書き方のひとつです。

for 変数 in  配列やハッシュ
    繰り返し処理
end

loopメソッド

無限ループを作りたいときは、loopメソッドが使えます。

loop do
    無限ループ用の処理
end

例えば、puts foo と記述すると、無限に'foo'がputsされ続け大変なことになります。

そんな時、繰り返し処理を途中で抜ける方法があります。

  • break  繰り返し処理を中断し、繰り返しの中から抜けることができる
  • next  繰り返し処理を中断し、次の回の繰り返し処理へ移動することできる
  • return  繰り返し処理を中断し、メソッドから抜け出すことができる

逆に、繰り返し処理をやり直したい場合は以下を使います。

  • redo  同じ条件で繰り返し処理をやり直す

繰り返し処理用の制御構造

繰り返し処理の途中で、処理を中断したり、次の回に飛ばしたり処理を制御したいときがあります。そのような処理を制御するための構造がRubyでは用意されています。以下のキーワードで処理を制御することが可能です。

break

  • breakを使ってloop処理を抜けます。
    • 下記は、 変数mfujiwaraと同一になった場合、breakさせます。(繰り返し処理を脱出します)
    • 下記の例では、loopメソッドを使っていますが、for文やeachメソッドなど他の繰り返し記法でも利用できます。
irb(main):073:1* def choose_members
irb(main):074:1* members = ['fujiwara', 'masukawa', 'naoi', 'masu']
irb(main):075:2*   loop do
irb(main):076:2*   m = members.sample
irb(main):077:2*   puts m
irb(main):078:2*   break if m == 'fujiwara'
irb(main):079:1*   end
irb(main):080:1*   puts 'I am a composer!!'
irb(main):081:0> end
=> :choose_members
irb(main):082:0> choose_members
masukawa
naoi
masukawa
fujiwara
I am a composer!!
=> nil

next

  • nextを使って次の処理へ移動してみます。
    • next if m == 'fujiwara' がtrueとなった場合、次の繰り返し処理へ移動します。
irb(main):031:1* def check_main_songwriter
irb(main):032:2*   ['fujiwara', 'masukawa', 'naoi', 'masu'].each do |m|
irb(main):033:2*     next if m == 'fujiwara'
irb(main):034:2*     puts "#{m}さんは、違います"
irb(main):035:1*   end
irb(main):036:2*   ['作詞', '作曲'].each do |n|
irb(main):037:2*     puts "fujiwaraさんは、#{n}します"
irb(main):038:1*   end
irb(main):039:0> end
=> :check_main_songwriter
irb(main):040:0> check_main_songwriter
masukawaさんは、違います
naoiさんは、違います
masuさんは、違います
fujiwaraさんは、作詞します
fujiwaraさんは、作曲します
=> ["作詞", "作曲"]

breakとreturnの違い

breakは、上記の例で取り上げたように「繰り返し処理からの脱出」を行うために利用する記法です。

returnは、「メソッドからの脱出」を行うために利用されます。メソッドの実行を終了するとも言い換えられます。

  • returnを使ってメソッドから脱出させます。
    • 下記は、 変数mfujiwaraと同一になった場合、returnさせます。(メソッドを脱出します)
    • 下記の例では、loopメソッドを使っていますが、for文やeachメソッドなど他の繰り返し記法でも利用できます。
    • return if m == 'fujiwara' がtrueになった場合、メソッドから脱出します。メソッド内で記述しているputs 'I am a composer!!' は、実行されません。
irb(main):083:1* def choose_members
irb(main):084:1* members = ['fujiwara', 'masukawa', 'naoi', 'masu']
irb(main):085:2*   loop do
irb(main):086:2*   m = members.sample
irb(main):087:2*   puts m
irb(main):088:2*   return if m == 'fujiwara'
irb(main):089:1*   end
irb(main):090:1*   puts 'I am a composer!!'
irb(main):091:0> end
=> :choose_members
irb(main):092:0> choose_members
masukawa
masukawa
naoi
masu
fujiwara
=> nil

throw と catch を使った大域脱出

breakでは、繰り返し処理を中断し、一番内側の処理しか脱出できませんが、throwcatchを使えば、一気に外側のループまで脱出できます。(returnのようにメソッドの脱出ではありません。あくまでもループ全体の脱出になります)

:bumpがタグ文字といってcatchとthrowの後に記述します。同じタグ文字にする必要があります。

irb(main):095:0> records = [ 'LAMP', 'ダイヤモンド', '天体観測' ]
=> ["LAMP", "ダイヤモンド", "天体観測"]
irb(main):096:0> release = [1, 2, 3]
=> [1, 2, 3]
irb(main):097:1* catch :bump do
irb(main):098:2* records.shuffle.each do |record|
irb(main):099:3* release.shuffle.each do |n|
irb(main):100:3* puts "#{record}は、#{n}枚目のシングルです}"
irb(main):101:4* if record == '天体観測' && n == 3
irb(main):102:4* throw :bump
irb(main):103:3* end
irb(main):104:2* end
irb(main):105:1* end
irb(main):106:0> end
ダイヤモンドは、1枚目のシングルです}
ダイヤモンドは、2枚目のシングルです}
ダイヤモンドは、3枚目のシングルです}
LAMPは、3枚目のシングルです}
LAMPは、2枚目のシングルです}
LAMPは、1枚目のシングルです}
天体観測は、2枚目のシングルです}
天体観測は、1枚目のシングルです}
天体観測は、3枚目のシングルです}
*=> nil*

ハッシュ

ハッシュは、「キー」と「値」のセットでデータを管理するオブジェクトのことです。

他の言語では「連想配列」「辞書」「マップ」と呼ばれたりします。

英語のHashは、コマ切れ肉と野菜を炒めた料理の事を指し、ハッシュドビーフの語源でもあります。

ハッシュの3つの書き方

ハッシュロケットを使った書き方。キーは文字列です。

{ 'first_single' => 'LAMP', 'second_sigle' => 'ダイヤモンド', 'third_single' => '天体観測' }

ハッシュロケットを使った書き方。キーはシンボルです。

{ :first_single => 'LAMP', :second_sigle => 'ダイヤモンド', :third_single => '天体観測' }

ハッシュロケットを省略した書き方。キーはシンボルですが、:コロンの位置が右側に変わります。

{ first_single: 'LAMP', second_sigle: 'ダイヤモンド', third_single: '天体観測' }

ちなみに、改行して書くこともできます。

{ 
'first_single' => 'LAMP',
'second_sigle' => 'ダイヤモンド',
'third_single' => '天体観測' 
}

シンボル

シンボルは任意の文字列と一対一に対応するオブジェクトです。Rubyがメソッドなどの名前を識別するためのラベルをオブジェクトにしたものです。先頭に: をつけて表現します。

文字列との違いは、主に4つあります。

  • クラスが違います。文字列はStringクラスです。シンボルはSymbolクラスです。
# 文字列
irb#1(main):014:0> 'BumpOfChicken'.class
=> String

# シンボル
irb#1(main):015:0> :BumpObChicken.class
=> Symbol
  • 同じシンボルは同じオブジェクトIDです。文字列は毎度オブジェクトIDが変わります。
# 文字列(毎回、違う)
irb#1(main):016:0> 'BumpOfChicken'.object_id
=> 260
irb#1(main):017:0> 'BumpOfChicken'.object_id
=> 280
irb#1(main):018:0> 'BumpOfChicken'.object_id
=> 300

# シンボル(毎回、同一になる)
irb#1(main):019:0> :BumpOfChicken.object_id
=> 2296228
irb#1(main):020:0> :BumpOfChicken.object_id
=> 2296228
irb#1(main):021:0> :BumpOfChicken.object_id
=> 2296228
  • シンボルは整数として扱われるため、文字列より高速に処理できます。
    • 同じシンボルであれば同じオブジェクトである理由で、シンボルの方がメモリ効率がよいです。
    • シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。整数として扱えることが高速に処理できる理由です。class Symbol (Ruby 3.0.0 リファレンスマニュアル)
  • シンボルはイミュータブル(非破壊的)なオブジェクトです。
    • 文字列はミュータブル(破壊的)なオブジェクトのため変更は可能ですが、シンボルはイミュータブルなため変更できません。勝手に変更されては困る名前などの用途に向いています。
# 文字列は変更が可能です。
irb(main):002:0> name = 'bump'
=> "bump"
irb(main):003:0> name.upcase!
=> "BUMP"

# シンボルは変更できません。
irb(main):004:0> name = :bump
=> :bump
irb(main):005:0> name.upcase!
(irb):5:in `<main>': undefined method `upcase!' for :bump:Symbol (NoMethodError)

irbの情報を確認

irb_info コマンドで、irbの情報を確認することができます。

shiro@shiro:~$ irb
irb(main):001:0> irb_info
=>
Ruby version: 3.0.0
IRB version: irb 1.3.6 (2021-06-19)
InputMethod: ReidlineInputMethod with Reline 0.2.6
RUBY_PLATFORM: x86_64-darwin19

irbで、行を挿入する方法

option + Enter で、実行中のirbに行を挿入することができます。

とても便利で頻繁に使っています。

GitHub内をアドバンスドサーチ

advanced search とは、高度検索と訳せます。複数の検索パラメータを組み合わせることによって、指定された範囲でより正確な結果を得ることのできる機能です。

Googleなどが有名ですが、GitHubにも存在することを教えてもらいました。

Google 検索オプション

メソッドの使い方、記法など、希望する条件を入力し検索することができます。

GitHub · Where software is built

コントリビュートチャンス

チェリー本を読み進めていたとき、ふと英単語の誤植が目に入ってきました。

139頁の真ん中あたりのサンプルコード内で、

本来numbersの綴りが正しいはずですが、nunmbersとなっていました。

nunmbersのループは脱出するが、fruitsのループは継続する

コントリビュートというと大げさではありますが、人生初めての誤植発見というのもあり、嬉しくてテンション上がりました。笑

その後、すぐにフィヨルドブートキャンプ内のバグ・誤字脱字の窓口へ報告を行いました。

なんと直接、著者の伊藤さんにご対応いただき、無事に技術評論社の正誤表へ反映いただきました。

gihyo.jp

スタンプ

そんなこともあり、輪読会メンバーの(id:yana_g)さんが「コントリビュートチャンス」スタンプを作ってくれました笑。こういった遊び心とても素敵ですね。

絵文字ジェネレーター - Slack 向け絵文字を無料で簡単生成

f:id:shirotamaki:20210722140250p:plain

参考書籍

🍒 まとめ

今週で6週目を終えました。

始めた当初は苦手意識のあった「irb」と「るりま」ですが、日々使用することで親しみが増し、今ではだいぶ仲良くなれたと思います。わからないことに出くわしたときは、すぐに「るりま」を参照する習慣が身につきました。また「irb」を使用し手元で動きを確認することも、今では自然と手がスッと動くようになり、身についてきたと思います。

輪読会を通して、この大きな2つの武器を手に入れられたことは大きな収穫です。

引き続き、この武器を活用し技術を磨いていきたいと思います。

では、また来週!(次回、第7週目)

チェリー本輪読会 第5週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本輪読会の第5週目のエントリーになります。

輪読会の概要については第1週目にまとめています。

🍒 輪読会 第5週目まとめ

第4章4.8.1〜第4章4.8.8まで

期間:2021年06月21日〜2021年06月25日

each_with_indexメソッド

これまで、eachメソッドを使って繰り返し処理を行ってきましたが、繰り返し処理をしつつかつ処理している要素の添え字(インデックス)も習得したいとき、このメソッドの出番になります。

ブロックの第2引数に添え字(インデックス)渡すことができます。添え字は、0からカウントします。

Enumerable#each_with_index (Ruby 3.0.0 リファレンスマニュアル)

irb(main):007:0> records = [ '存在しません', 'Please Please Me', 'With
The Beatles' ]
=> ["存在しません", "Please Please Me", "With The Beatles"]
irb(main):008:0> records.each_with_index { |record, i| puts "#{i}枚目の
アルバムは #{record}" }
0枚目のアルバムは 存在しません
1枚目のアルバムは Please Please Me
2枚目のアルバムは With The Beatles
=> ["存在しません", "Please Please Me", "With The Beatles"]

引数を指定することで、添え字を0以外から始めることもできます。

records.each.with_index(1) として、添え字を1から開始させています。

irb(main):029:0> records = [ 'Please Please Me', 'With The Beatles', 'A
 Hard Days Night' ]
=> ["Please Please Me", "With The Beatles", "A Hard Days Night"]
irb(main):030:0> records.each.with_index(1) { |record, i| puts  "#{i}
目のアルバムは #{record}" }
1枚目のアルバムは Please Please Me
2枚目のアルバムは With The Beatles
3枚目のアルバムは A Hard Days Night
=> ["Please Please Me", "With The Beatles", "A Hard Days Night"]

mapメソッドとwith_indexメソッドの組み合わせ

mapメソッドを組み合わせて使ってみましょう。

配列になって戻り値が返ってきます。

irb(main):013:0> records = [ '存在しません', 'Please Please Me', 'With
The Beatles' ]
=> ["存在しません", "Please Please Me", "With The Beatles"]
irb(main):014:0> records.map.with_index { |record, i| "#{i}枚目のアルバ
ムは #{record}" }
=>
["0枚目のアルバムは 存在しません",
 "1枚目のアルバムは Please Please Me",
 "2枚目のアルバムは With The Beatles"]

mapメソッドも引数を渡すことで添え字の始める数字を変更できます。

irb(main):033:0> records = [ 'Please Please Me', 'With The Beatles', 'A
 Hard Days Night' ]
=> ["Please Please Me", "With The Beatles", "A Hard Days Night"]
irb(main):034:0> records.map.with_index(1) { |record, i| puts "#{i}枚目
のアルバムは #{record}" }
1枚目のアルバムは Please Please Me
2枚目のアルバムは With The Beatles
3枚目のアルバムは A Hard Days Night
=> [nil, nil, nil]

Enumeratorクラス

with_indexメソッドは、Enumeratorクラスのインスタンスメソッドです。

Enumeratorクラスとは、each 以外のメソッドにも Enumerable の機能を提供するためのラッパークラスになります。(ラッパークラスとは、オブジェクト指向プログラミング言語で、他のクラスやプログラムの中でオブジェクトとして扱いたい対象を、クラスとして定義したもの)Enumerable(列挙可能な、数え上げられるの意)とは、Enumerableモジュールのことを指します。

f:id:shirotamaki:20210709132937p:plain

class Enumerator (Ruby 3.0.0 リファレンスマニュアル)

module Enumerable (Ruby 3.0.0 リファレンスマニュアル)module Enumerable (Ruby 3.0.0 リファレンスマニュアル)

まとめると、EnumeratorクラスはEnumerableモジュールをincludeしていることになります。 さまざまな継承しているメソッドを利用できます。

f:id:shirotamaki:20210709213238p:plain

他にも、Arrayクラス、Rangeクラスなど「繰り返し処理に関連するメソッドはEnumerableモジュールに定義されていることが多いです。

f:id:shirotamaki:20210709133015p:plain

以下、ブロックなしでメソッドを呼び出した場合、そのメソッドのクラスが返ってくるようになっていますが、以下で呼び出すとEnumeratorが返ってきます。これにより、Enumeratorクラスであることが確認できます。

irb(main):015:0> records = [ '存在しません', 'Please Please Me', 'With
The Beatles' ]
=> ["存在しません", "Please Please Me", "With The Beatles"]
irb(main):016:0> records.each
=> #<Enumerator: ...>
irb(main):017:0> records.map
=> #<Enumerator: ...>

File.openメソッド

テキストファイルへ文字列を書き込む際に利用します

ファイルがopenされた後、処理が実行され完了すると自動でcloseされます。他の言語だとopenするとcloseは手動で行うことが多いようですがFile.openメソッドを利用すれば、Rubyがよしなに処理してくれます。エイリアスメソッドとして、File.newが用意されていますが、こちらブロックを渡すことができないため注意が必要です。

class File (Ruby 3.0.0 リファレンスマニュアル)

適当なファイルを準備し、ファイルに書き込んでみます。

irb(main):005:1* File.open("./beatles.text", "w") do |file|
irb(main):006:1* file.puts("1枚目のアルバムは Please Please Me")
irb(main):007:1* file.puts("2枚目のアルバムは With The Beatles")
irb(main):008:1* file.puts("3枚目のアルバムは A Hard Days Night")
irb(main):009:0> end
=> nil

ファイルの中身を確認してみます。ちゃんと書き込みが行なわれています。

shiro@shiro:~/rindokukai$ cat beatles.text
1枚目のアルバムは Please Please Me
2枚目のアルバムは With The Beatles
3枚目のアルバムは A Hard Days Night

do...endより、 {} のほうが結合度が強い

ブロックで使える2つの記法。

基本的にどっちを使っても結果は同じですが、結合度が違うので注意が必要です。

下記のrecords配列をdo...endを使い処理してみます。

deleteメソッドを使い、引数に渡した文字列が配列の中に存在する場合は削除されます。

irb(main):017:0> records = ['1st', '2nd', '3rd']
=> ["1st", "2nd", "3rd"]
irb(main):018:1* records.delete('1st') do
irb(main):019:1*   '見つかりません'
irb(main):020:0> end
=> "1st"
irb(main):021:0> records
=> ["2nd", "3rd"]

配列に存在しない場合は、ブロックの中身が実行され見つかりませんと返ってきます。

irb(main):025:0> records = ['1st', '2nd', '3rd']
=> ["1st", "2nd", "3rd"]
irb(main):026:1* records.delete('4th') do
irb(main):027:1*   '見つかりません'
irb(main):028:0> end
=> "見つかりません"

()を省略しても、同じ動きになります。

irb(main):005:1* records.delete '4th' do
irb(main):006:1*   '見つかりません'
irb(main):007:0> end
=> "見つかりません"

次に、do...end を、{}へ置き換えてみます。

エラーになります。これは、{}の結合度が高いため、records.delete '4th'と認識されずに、'4th' { '見つかりません' }と解釈されて処理されてしまうためです。

irb(main):008:0> records.delete '4th' { '見つかりません' }
/Users/shiro/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.6/lib/irb/workspace.rb:116:in `eval': (irb):8: syntax error, unexpected '{', expecting end-of-input (SyntaxError)
records.delete '4th' { '見つかりません' }
                     ^

()で囲うことでエラーを解決できます。

可読性からいっても、引数に()は付けたほうが良いと思われます。records.delete ('4th')

irb(main):009:0> records.delete ('4th') { '見つかりません' }
=> "見つかりません"

メソッドチェーン

変数を使わずにメソッドの戻り値に対して直接ほかのメソッドを呼び出していくコーディングスタイルのことをメソッドチェーンと呼びます。

個人的にも、Rubyキマってるね!(後述)と言いたくなる記法のひとつで、一筆書きのようにスラスラ〜と書けたら、とても気持ちよさそうです。

mapメソッドを使い、best_records変数を用意して書いてみます。

irb(main):032:0> records = ['1st', '2nd', '3rd']
=> ["1st", "2nd", "3rd"]
irb(main):033:0> best_records = records.map { |record| "#{record}アルバム" }
irb(main):034:0> best_records.join('')
=> "1stアルバムと2ndアルバムと3rdアルバム"

メソッドチェーンを使って書き換えてみます。

irb(main):037:0> records = ['1st', '2nd', '3rd']
=> ["1st", "2nd", "3rd"]
irb(main):038:0> records.map { |record| "#{record}アルバム" }.join('')
=> "1stアルバムと2ndアルバムと3rdアルバム"

do...end を使って書くこともできますが、どちらかと言うと{}を利用するほうが読みやすく、推奨されています。

APIドキュメント

APIドキュメントって何だろう?」

チェリー本にも頻繁に出てくる用語ですが、疑問に思い輪読会で取り上げてみました。 参加されていた受講生の方も同じ疑問を持たれており、過去に直接著者の伊藤さんがこの問いに答えているドキュメントを共有してもらいました。(フィヨルドブートキャンプ用のドキュメントのためブログへは非公開)

私の元々持っていたAPIの言葉としてのイメージはこうでした。

  • Application Programming Interfaceの略。
  • アプリケーションとプログラムをつなぐ何かのこと。アプリケーションとプログラムを繋ぐ接点のこと。
  • Twitterやインスタグラムなど、外部のアプリケーションを簡単に利用できる仕組みのこと。

このように、ぼんやりとした理解でいました。

上記の理解が間違っているわけではないですが、APIという言葉は非常に幅が広い用語であり、今回のAPIドキュメントについての答えとしては不十分です。 共有してもらった情報を元に再定義してみたいと思います。

  • APIとは、プログラマに対して用意されている公式に公開されている便利な機能のことを指します。何かしたい実装があるときに「これを使うと(APIという機能)簡単にこんなことが出来て、こんな結果が返ってくる。車輪の再発明をする必要はないですよ」ということです。 Rubyには無数のAPIがあります。Enumeratorクラスにはeachメソッドがあり、また他にも多くのクラスやメソッドがあります。チェリー本で出てくるAPIドキュメントとは、これらのクラスやメソッドなどのAPI(公式に公開されている機能)の説明が書かれているドキュメントを指しています。 チェリー本で指しているAPIドキュメントとは、るりまのことなんです。

TwitterやインスタグラムなどのAPI(WebAPIという)も言い換えると、「Twitterの色々な機能を簡単に呼び出して使えるようにしたので、自由に使ってご自身のプログラムに取り入れてくだい」ということです。APIを使うと簡単にTwitterやインスタグラムなどの機能を利用できるので、ゼロからプログラミングする必要がなく開発の効率があがります。

下記は、今回APIの疑問について情報を共有いただいたSaki さん(id:Saki-Htr)と、フィヨルドブートキャンプのアドバイザーでもあるうづら さん(id:udzura)とのTwitter上でのやり取りになります。

こちらのやり取りもとても参考になりました。(Sakiさん、情報共有ありがとうございました!)

HackMDの =記法

輪読会では、HackMDを使いファイルを共有し、参加者が気づきや感想を書き込めるようにしています。

Markdownが使え共同編集ができ、そして軽い。便利なエディタです。 HackMDの使い方 - HackMD

そんな日々お世話になっているHackMDですが、コードブロックに行番号を明示できることを教えてもらいました。コードを書き込むことも多いのですが、コードブロックの後ろに= と記入するだけで、行番号を表示できます。多くの言語をサポートしており、=bash =ruby などのように入力することで、その言語のリストは自動補完されます。

機能紹介 - HackMD

Rubyをキメると気持ちいい

輪読会からは脱線しますが、Rubyを学習する中でMatzさん関連のweb記事を読むことが増え、その際にふと見かけた気になる言葉がありました。

Rubyをキメると気持ちいい

Rubyはプログラミングの「たのしさ」を最大化することを目標として設計、開発されていることは知っていましたが、キメるってどういうことだろう?と、ちょっとした興味で、メンターさんに質問してみました。その際、メンターさんに教えていただいた動画がこちらになります。他にも、メンターさんから「Rubyをキメた」経験などもお伺いでき、改めてRubyの「たのしさ」「気持ちよさ」がわかった気がします。

プログラミング梁山泊(前編) - まつもとゆきひろ - ニコニコ動画

プログラミング梁山泊(後編) - まつもとゆきひろ - ニコニコ動画

発言箇所は、後編の18:50〜20:30の質疑応答でのやり取りにて出てきます。

参考書籍

🍒 まとめ

今週は、いろいろな言葉の意味について深堀りすることができて良かったです。

プログラマは「言語化能力が大事」とよく耳にしますが、輪読会が言語化の良いトレーニングにもなっていると感じています。

読んだ内容についてHackMDへ書き込み、その後自分の言葉でその内容について考えを述べ説明したり、質問したり答えたり、また感想を述べたりします。自分の考えを相手に簡潔にわかりやすく伝えるため、「どのような言葉で伝えればいいのか?どの順番で伝えるべきか?」など、言語化する上でいろいろと考えます。 もちろん、輪読会でなくても日報を書く時やチャットでのやり取りなど、普段から言語化を意識することは多いです。しかし、輪読会では自分以外のメンバーの言語化をリアルタイムに体感でき、自分とは違う言語化を見聞きすることでとても学びがあります。例えば、自分が伝えたい物事に対して、「それはこういう事ですね。例えば○○が△△と表現できますね」など、言語化した物事に対してレビューを受けている感覚です。これは非常に勉強になります。具体的にしか表現できなかったことを抽象的な言葉一言で説明することができたり、その逆に抽象的すぎる物事を具体的に説明することができたり。それまで理解できなかったことが、パッと理解できる。輪読会の大きな効用のひとつでもあります。

言語化。 プログラミングに限らず普段から心がけていきたいものです。

では、また来週!(次回、第6週目)

チェリー本輪読会 第4週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本輪読会の第4週目のエントリーになります。

輪読会の概要については第1週目にまとめています。

🍒 輪読会 第4週目まとめ

第4章4.6.4〜第4章4.7.14まで

期間:2021年06月14日〜2021年06月18日

リファクタリングすべきポイントは「繰り返し処理」部分

先週取り掛かったコードをリファクタリングしました。

リファクタリングとは?

外から見た振る舞いは保ったまま、理解や修正が簡単になるように内部のコードを改善することです。 出典:チェリー本

DRY(Don't repeat yourself)の原則に従い行います。

DRY原則 | プログラマが知るべき97のこと)

簡単な例。

以下は、引数ひとつに対し一行で記述した例です。

irb(main):210:1* def add_second_name(vo, gu, ba, dr)
irb(main):211:1*   puts vo.to_s + 'Ramone'
irb(main):212:1*   puts gu.to_s + 'Ramone'
irb(main):213:1*   puts ba.to_s + 'Ramone'
irb(main):214:1*   puts dr.to_s + 'Ramone'
irb(main):215:0> end
=> :add_second_name
irb(main):216:0>
irb(main):217:0> add_second_name('Joey', 'Johnny', 'DeeDee', 'Marky')
JoeyRamone
JohnnyRamone
DeeDeeRamone
MarkyRamone
=> nil

以下は、each文を使い配列で処理しています。

繰り返し書いている箇所をまとめることができ、DRYに従ったリファクタリングと言えます。

irb(main):218:1* def add_second_name(vo, gu, ba, dr)
irb(main):219:2*   [vo, gu, ba, dr].each do |n|
irb(main):220:2*     puts n.to_s + 'Ramone'
irb(main):221:1*   end
irb(main):222:0> end
=> :add_second_name
irb(main):223:0> add_second_name('Joey', 'Johnny', 'DeeDee', 'Marky')
JoeyRamone
JohnnyRamone
DeeDeeRamone
MarkyRamone
=> ["Joey", "Johnny", "DeeDee", "Marky"]

添字を使った配列の取得方法

配列[位置、取得する長さ]

下記は、2つ目の要素から3つ分を取り出すコードです。

irb(main):001:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):002:0> ramones[1,3]
=> ["Johnny", "DeeDee", "Marky"]

他にもさまざま取り出し方法があります。

正の値を指定することで配列を取り出すことは問題なく理解できていましたが、負の値を指定した際に取り出す方法の理解が曖昧でした。

改めて見直してみます。

irb(main):234:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):235:0> ramones[-1]
=> "Marky"
irb(main):236:0> ramones[-3]
=> "Johnny"

2番目の要素を指定してみます。正の値で指定した動きと同じです。

しかし、取り出しをスタートする地点から右に数えて取得しています。

irb(main):013:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):014:0> ramones[-3, 2]
=> ["Johnny", "DeeDee"]

他に、lastメソッド配列の最後の要素を取得できます。

引数を指定してあげることで、

最後の要素から順番に指定した分要素を取り出せます。

ちなみに、lastとまったく動きが逆になるfirstメソッドもあります。

irb(main):019:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):020:0> ramones.last
=> "Marky"
irb(main):021:0> ramones.last(2)
=> ["DeeDee", "Marky"]

concatメソッド

Array#concat (Ruby 3.0.0 リファレンスマニュアル)

2つの配列を連結したいときに使うメソッドです。

Excelの、CONCATENATE 関数 を思い出しました。 当時、Excel表計算していた際は大変お世話になった関数です。

CONCATENATE 関数 - Office サポート

irb(main):022:0> ramones =  ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):023:0> kana = ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]
=> ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]
irb(main):024:0> ramones.concat(kana)
=> ["Joey", "Johnny", "DeeDee", "Marky", "ジョーイ", "ジョニー", "ディーディー", "マーキー"]

ramones変数は変更されます。(破壊的)

kana変数は変更されません。

irb(main):025:0> ramones
=> ["Joey", "Johnny", "DeeDee", "Marky", "ジョーイ", "ジョニー", "ディーディー", "マーキー"]
irb(main):026:0> kana
=> ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]

ちなみに、 + を使っても連結できますが、こちらは非破壊的なので変数は変更されません。

irb(main):027:0> ramones =  ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):028:0> kana = ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]
=> ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]
irb(main):029:0> ramones + kana
=> ["Joey", "Johnny", "DeeDee", "Marky", "ジョーイ", "ジョニー", "ディーディー", "マーキー"]
irb(main):030:0> ramones
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):031:0> kana
=> ["ジョーイ", "ジョニー", "ディーディー", "マーキー"]

チェリー本では、concatメソッドは破壊的なメソッドなため、思わぬ不具合を与えてしまい兼ねないため、+演算子を使うことを推奨しています。

キャット?カット?

輪読会恒例になりつつある?笑 「どう発音するか?」問題で盛り上がりました。 イギリス英語、アメリカ英語で違いがあるようです。

CONCATENATE | Cambridge Dictionary による英語での発音

確か、canもイギリスだと「カン」と言っているのを思い出しました。

でもビートルズはキャンって歌っている…

せっかくなので調べてみました。なるほど、納得!

ビートルズはアメリカ英語で歌っていた(230) - ★ビートルズを誰にでも分かりやすく解説するブログ★

# frozen_string_literal: true

普段、Rubyのコードを書く際におまじないのように書いている下記のコード。

# frozen_string_literal: true

こちらも疑問に思ったため調べてみました。

Object#freeze (Ruby 3.0.0 リファレンスマニュアル)

凍結されるのはオブジェクトであり、変数ではありません。代入などで変数の指すオブジェクトが変化してしまうことは freeze では防げません。 freeze が防ぐのは、 `破壊的な操作' と呼ばれるもの一般です

frozen_string_literal はマジックコメントといい、# frozen_string_literal: true と書くことで、上記のるりまで説明があるように文字列が最初からfreezeされるようです。ただし、文字列リテラルに限られます。

文字列リテラルとは?

リテラル (Ruby 3.0.0 リファレンスマニュアル)

破壊的メソッドと非破壊的メソッドの見分け方

まずはるりまを読む。これが鉄則です。

例えば、先程のconcatメソッド。るりまにはこう書いてあります。

  • 配列 other を自身の末尾に破壊的に連結します。

Array#concat (Ruby 3.0.0 リファレンスマニュアル)

! で終わるメソッド

エクスクラメーションマーク(感嘆符)が付くメソッドは、全て破壊的なメソッドであると思っていましたが、これは勘違いでした。!で終わるメソッドは、慣習的に「使用する際には注意が必要ですよ」という意味を持っているだけで、必ずしも破壊的メソッドになるわけではありません。ここは注意が必要です。

cocatメソッドのように、! が付かなくても破壊的なメソッドは存在します。

他には、deleteメソッド、clearメソッドも、!が付かない破壊的メソッドです。

以下は、Matzさんのツイートになります。

なるほど。納得できました。

配列の和集合、差集合、積集合

irb(main):001:0> ramones_a = ["Joey", "Johnny"]
=> ["Joey", "Johnny"]
irb(main):002:0> ramones_b = ["DeeDee", "Marky"]
=> ["DeeDee", "Marky"]
irb(main):003:0> ramones_a | ramones_b
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):002:0> ramones_a = ["Joey", "Johnny", "DeeDee"]
=> ["Joey", "Johnny", "DeeDee"]
irb(main):003:0> ramones_b = ["DeeDee", "Marky"]
=> ["DeeDee", "Marky"]
irb(main):004:0> ramones_a - ramones_b
=> ["Joey", "Johnny"]
irb(main):001:0> ramones_a = ["Joey", "Johnny", "DeeDee"]
=> ["Joey", "Johnny", "DeeDee"]
irb(main):002:0> ramones_b = ["DeeDee", "Marky"]
=> ["DeeDee", "Marky"]
irb(main):003:0> ramones_a & ramones_b
=> ["DeeDee"]

上記と同じことがしたい場合、RubyにはSetクラスが用意されています。一般的にはSetクラスを使って集合演算を行います。

class Set (Ruby 3.0.0 リファレンスマニュアル)

splat展開

*を使って1つの引数ではなく、「複数の引数」として配列を展開することができます。

*splat展開なしの場合。

二次元配列になります。入れ子のデータ構造のことです。

irb(main):001:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):002:0> name = []
=> []
irb(main):003:0> name.push(ramones)
=> [["Joey", "Johnny", "DeeDee", "Marky"]]

*splat展開ありの場合。

一次元配列で格納できます。

irb(main):001:0> ramones = ["Joey", "Johnny", "DeeDee", "Marky"]
=> ["Joey", "Johnny", "DeeDee", "Marky"]
irb(main):002:0> name = []
=> []
irb(main):003:0> name.push(*ramones)
=> ["Joey", "Johnny", "DeeDee", "Marky"]

メソッドの可変長引数(variable arguments)

コンピュータプログラムの一部として定義された関数などに渡される引数のうち、あらかじめ数が固定されておらず、任意の数(あるいは事前に定められた範囲の数)を取ることができるものを可変長引数(可変引数、可変個引数)といいいます。チェリー本では、「個数に制限のない引数」と説明されています。

  • splat展開がない場合
    • joinメソッドを使い、引数に渡した配列の文字列を間に挟み連結しした文字列を返します。
    • 引数が配列じゃない場合は、エラーになるります。
irb(main):004:1* def greeting(names)
irb(main):005:1* "#{names.join('')}、ハロー!!"
irb(main):006:0> end
=> :greeting
irb(main):007:0> greeting([ "DeeDee", "Marky"])
=> "DeeDeeとMarky、ハロー!!"
irb(main):008:0> greeting( "DeeDee", "Marky")
(irb):4:in `greeting': wrong number of arguments (given 2, expected 1) (ArgumentError)
  from (irb):8:in `<main>'
  from /Users/shiro/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.6/exe/irb:11:in `<top (required)>'
    from /Users/shiro/.rbenv/versions/3.0.0/bin/irb:23:in `load'
  from /Users/shiro/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
  • splat展開をした場合
    • *name とすることで、greeting("DeeDee", "Marky") としている引数が配列として展開されて、joinメソッドに渡されています。
irb(main):009:1* def greeting(*names)
irb(main):010:1* "#{names.join('')}、ハロー!!"
irb(main):011:0> end
irb(main):012:0*
=> :greeting
irb(main):013:0> greeting("DeeDee", "Marky")
=> "DeeDeeとMarky、ハロー!!"

splatは、アスタリスクという意味でもあるようです。

splatの意味・使い方・読み方|英辞郎 on the WEB

個人的にはこのゲームのイメージから英語の意味を紐付けたりしました。

スプラトゥーン2 | Nintendo Switch | 任天堂

%記法

配列を[ ] を使わずに作成できます。%w %W を使います。

  • %w の場合
    • スペース区切りで配列に格納されます。
irb(main):023:0> ramones = %w(We are Ramones! Hey\nho\nlet's\ngo!!)
=> ["We", "are", "Ramones!", "Hey\\nho\\nlet's\\ngo!!"]
irb(main):024:0> puts ramones
We
are
Ramones!
Hey\nho\nlet's\ngo!!
  • %W 大文字の場合。
    • 改行文字\n タブ文字\tなどは、irb上の返り値では、そのまま出力されるが、変数などに代入しputsなどで出力すると実際に改行、タブで区切られ出力されます。
irb(main):021:0> ramones = %W(We are Ramones! Hey\nho\nlet's\ngo!!)
=> ["We", "are", "Ramones!", "Hey\nho\nlet's\ngo!!"]
irb(main):022:0> puts ramones
We
are
Ramones!
Hey
ho
let's
go!!
=> nil

\n や\t は文字コードではなくバックスラッシュ記法です。

リテラル (Ruby 3.0.0 リファレンスマニュアル)

charsメソッド、splitメソッド

charsメソッドは、文字列を一文字一文字分解して配列にするメソッドです。

irb(main):025:0> 'RAMONES'.chars
=> ["R", "A", "M", "O", "N", "E", "S"]

splitメソッドは、引数で渡した区切り文字(今回は,で区切っている)で文字列を配列に分解するメソッドです。

irb(main):027:0> 'Joey, Johnny, DeeDee, Marky'.split(',')
=> ["Joey", " Johnny", " DeeDee", " Marky"]

chars の読み方

今週2回目の「どう発音するか?」問題。笑

今回はcharsがターゲットになりました。 元々の単語は、character になります。

チャー派とキャラ派。どちらもいい勝負でしたが、結果は....? ちなみに私はチャー派で、ギタリストのcharに馴染みがあるため、自然とチャーと言ってました。

結論ですが、どっちでもいいらしいです。笑

ですが、こういった小ネタ的な話題で盛り上がれるのも輪読会のひとつの楽しみでもあります。

ちなみに、Paizaが取ったアンケートではチャー派の勝利です!!

Array.new

配列は[ ] で作成することが多いですが、Array.newを使って作成する方法もあります。

ちなみに、[ ] はメソッドでもあります。

Array# (Ruby 3.0.0 リファレンスマニュアル)

String# (Ruby 3.0.0 リファレンスマニュアル)

下記、同じ意味のコードです。

irb(main):029:0> ramones = []
=> []
irb(main):030:0> ramones = Array.new
=> []

第1引数を渡すと、その個数分の要素が追加されます。初期値はnilです。

第2引数を渡すと、要素が置き換わります。

irb(main):032:0> ramones = Array.new(4)
=> [nil, nil, nil, nil]
irb(main):033:0> ramones = Array.new(4, 'Hey')
=> ["Hey", "Hey", "Hey", "Hey"]

ミュータブル、イミュータブル

普段馴染みのない単語なので、意味を忘れがちなミュータブル、イミュータブル。

ミュータブルmutable(変更可能な)と、イミュータブルimmutable(変更できない)が「どっちがどっち?」と、ごっちゃになるが、ミュータント・タートルズで覚えることにしました。突然変異。

あと、illiegal < = > ligalの対義語と同じ法則なので、覚えておきたいと思います。

Rubyにはイミュータブルなクラスや値がいくつかあります。基本のデータ型は下記の4つです。

  • 数値(Integerクラス、Floatクラス)
  • シンボル(Symbolクラス)
  • true / false
  • nil

言い替えると、Integerクラスやシンボルなどは、そもそも破壊的なメソッドは存在しないということです。

参考書籍

🍒 まとめ

今週は、配列や繰り返し処理ついて深堀りしていくことができました。

破壊的メソッドについて、深く考えて使うことがなかったのですが、今週の学びを通してプログラムに不具合を与えかねない危険なメソッドであることが理解できました。ミュータブル、イミュータブルも今回しっかり学べ理解することができました。

「同じ値で同一のオブジェクト」なのか?「同じ値で異なるオブジェクト」なのか?意識してプログラムを書く必要があります。今後、不具合が起きたときに、今回学んだことを活かしデバックしていきたいと思います。

では、また来週!(次回、第5週目)

チェリー本輪読会 第3週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本輪読会の第3週目のエントリーになります。

輪読会の概要については第1週目にまとめています。

🍒 輪読会 第3週目まとめ

第4章4.1.1〜第4章4.6.5まで

期間:2021年06月07日〜2021年06月11日

今週から第4章の「配列や繰り返し処理を理解する」へ突入!

hex と ints

  • hexは、16進数(hexadecimal numberの略)のことです。

  • 進数の英語表現

    • 2進数: binary number
    • 8進数: octal number
    • 10進数: decimal number
    • 16進数: hexadecimal number

進数 - Wikipedia

  • ints は、integer(整数型)のことです。

でもなぜ? int ではなくて、 ints なのか?

以下、メンターさんに質問してみたところ回答をいただきました。

Ruby界隈はrailsの影響で特に配列が入ってる変数は複数形にするという文化が強いからできた略語に見えますね。

JavaC言語などでは、int と表現されるようです。

なるほど…ややこしい...汗

プログラミングでよく使う英単語のまとめ【随時更新】 - Qiita

ちなみに、るりま(Rubyリファレンスマニュアル)でsが付く単語が他にもありました。

arguments(実引数)を、args と表現、

parameters(仮引数)を、params と表現してありました。

仮引数(parameter)と実引数(argument)

この2つの「引数」もややこしいので、整理したいと思います。

まずは、parameterから

  • メソッドを定義するときに受け取る引数のこと。
  • 仮引数のこと。仮引数とは、「プログラム言語・数学などで関数を定義しているところに書かれている仮の数字・変数(パラメーター)」のことを指す。
  • params(パラムス)、またはparam(パラム)と略する。

paramsハッシュ 特殊な使い方

argument

  • メソッドを呼び出すときに、メソッドに渡す値や変数のこと。
  • 実引数のこと。実引数は、「プログラムの実行時に関数に引き渡される値となる引数」のことを指す。
  • args(アーグス) と略する。

paramsとargsの例

定義したcalculate(n)nがparams

呼び出しているcalculate(100)100がargs

irb(main):013:1* def calculate(n)
irb(main):014:1*   puts n * 10
irb(main):015:0> end
=> :calculate
irb(main):016:0> calculate(100)
1000
=> nil

配列を使った多重代入

  • 基本形
irb(main):017:0* a, b = 1, 2
=> [1, 2]
  • 応用編

競プロでよく使うパターン

標準入力からスペース区切りの数値を受け取り、配列にして返します。

irb(main):019:0> gets.split.map(&:to_i)
100 200 300 400 500
=> [100, 200, 300, 400, 500]

split('foo')で、区切ることもできます。

+ を区切りで指定してみます。

irb(main):022:0> gets.split('+').map(&:to_i)
100+200+300+400+500
=> [100, 200, 300, 400, 500]

do..end と  { } の使い分け

do...end、{} ともに、ブロックの範囲を表しています。

eachメソッドを例に説明します。

do..endがブロックの範囲です。eachメソッドで「配列の要素を最初から最後まで取り出す」指示を実行し、取り出した要素を順にブロックへ渡しています。

# 公式
変数名.each do |ブロック変数|
    繰り返したい処理
end

irb(main):037:0> beatles = ["YEAH!","YEAH!YEAH!","YEAH!YEAH!YEAH!"]
=> ["YEAH!", "YEAH!YEAH!", "YEAH!YEAH!YEAH!"]
irb(main):038:1* beatles.each do |x|
irb(main):039:1*   puts "ビートルズがやってくる#{x}"
irb(main):040:0> end
ビートルズがやってくるYEAH!
ビートルズがやってくるYEAH!YEAH!
ビートルズがやってくるYEAH!YEAH!YEAH!
=> ["YEAH!", "YEAH!YEAH!", "YEAH!YEAH!YEAH!"]

{ }ブロックで書き換えてみます。

同じ結果が返ります。1行でコンパクトに書きたいときは {}ブロック を使うとよさそうです。

irb(main):001:0> beatles = ["YEAH!","YEAH!YEAH!","YEAH!YEAH!YEAH!"]
=> ["YEAH!", "YEAH!YEAH!", "YEAH!YEAH!YEAH!"]
irb(main):002:0> beatles.each { |x| puts "ビートルズがやってくる#{x}" }
ビートルズがやってくるYEAH!
ビートルズがやってくるYEAH!YEAH!
ビートルズがやってくるYEAH!YEAH!YEAH!
=> ["YEAH!", "YEAH!YEAH!", "YEAH!YEAH!YEAH!"]

map / collect(エイリアス) メソッド

今のところ、一番使ったメソッドナンバーワンのmapメソッド。

輪読会でも好きなメソッドとして一番名前が上がっていました。

チェリー本にも記載がありましたが、配列処理の大半はmapでいけます。

空の配列を用意して、ほかの配列をループ処理した結果を空の配列に詰め込んでいくような処理の大半は、mapメソッドに置き換えることができるはずです。

irb(main):040:0> names = ["Lennon", "McCartney", "Harrison", "Starr"]
=> ["Lennon", "McCartney", "Harrison", "Starr"]
irb(main):041:0> names.map { |n|  "Mr." + n }
=> ["Mr.Lennon", "Mr.McCartney", "Mr.Harrison", "Mr.Starr"]

ちなみに、eachで書き換えるとこうなります。

irb(main):036:0> names = ["Lennon", "McCartney", "Harrison", "Starr"]
=> ["Lennon", "McCartney", "Harrison", "Starr"]
irb(main):037:1* name_mr = []
=> []
irb(main):038:0> names.each { |n| name_mr << "Mr." + n }
=> ["Lennon", "McCartney", "Harrison", "Starr"]
irb(main):039:0> name_mr
=> ["Mr.Lennon", "Mr.McCartney", "Mr.Harrison", "Mr.Starr"]

slect / find_all(エイリアス) メソッド

戻り値が真trueの要素を集めた配列を返すメソッドです。真になる要素がひとつもなかった場合は空の配列を返します。ブロックを省略した場合は、各要素に対しブロックを評価し真になった値の配列を返すような Enumerator を返します。class Enumerator (Ruby 2.5.0 リファレンスマニュアル)

ブロックなしでメソッドを呼ぶと返ってくる値について、メソッドのクラスが返ってくるようです。

irb(main):003:0> [1,2,3,4,5].select { |num| num.even? }
=> [2, 4]

irb(main):004:0> [1,3,5].select { |num| num.even? }
=> []

irb(main):005:0> [1,2,3,4,5].select
=> #<Enumerator: [1, 2, 3, 4, 5]:select>

reject メソッド

selectメソッドの反対。戻り値が偽falseになった要素を返します。

rejectは、拒否するの意。

5で割り切れない数だけを返します。

irb(main):014:0> [1,5,10,12,15,100,202,500 ].reject { |n| n % 5 == 0 }
=> [1, 12, 202]

find / detect(エイリアス) メソッド

findメソッドは、selectと似ていますが、ブロックの戻り値が真になった「最初」の要素を返します。

irb(main):021:0> [10,50,111,200,333].find { |n| n.odd? }
=> 111

inject / reduce(エイリアス) メソッド

たたみ込み演算を行うメソッドです。

まず、ブロックの第1引数には、メソッドの引数を入れ、その後は第2引数へブロックの戻り値を順番に入れていきます。

irb(main):022:0> num = [10, 100, 1000, 10000]
=> [10, 100, 1000, 10000]
irb(main):023:0> num.inject(1) { |result, n| result + n }
=> 11111
  • まずは、resultへ1が入ります。
  • その後、1 + n の nへ、配列から取り出した10が入ります。 1+ 10=11 この結果がresultへ入る。
  • その後、11 + n  の nへ、配列から取り出した100が入ります。 11+100=111 この結果がresultへ入る。
  • その後、111 + n  の nへ、配列から取り出した1000が入ります。 111+1000=1111 この結果がresultへ入る。
  • その後、1111 + n  の nへ、配列から取り出した10000が入ります。 111+10000=11111 この結果がresultへ入る。
  • 結果、11111 が戻り値になります。

&:メソッド名で短くキメる

ブロックの中身を短く簡潔に書く方法です。

競プロでよく使っている記法だったので理解しやすかったです。

以下の条件のときに使えます。

  • ブロック引数が1個
  • ブロックの中で呼び出すメソッドには引数がない
  • ブロックの中では、ブロック引数に対してメソッド1回呼び出す以外の処理がない
irb(main):026:0> ["1", "2", "3"].map{|v| v.to_i}
=> [1, 2, 3]

irb(main):028:0> ["1", "2", "3"].map(&:to_i)
=> [1, 2, 3]

範囲 Range

範囲オブジェクトは、Rangeクラスのオブジェクトです。

FizzBuzz問題をはじめ、いろんな場面で応用が効くので覚えておきたいオブジェクトです。

# 公式
(1..10) 1 2 3 4 5 6 7 8 9 10  最後の値を含む
(1...10) 1 2 3 4 5 6 7 8 9 最後の値を含まない

irb(main):031:1* (1..10).each do |n|
irb(main):032:1*   puts "#{n}回目のYEAH!"
irb(main):033:0> end
1回目のYEAH!
2回目のYEAH!
3回目のYEAH!
4回目のYEAH!
5回目のYEAH!
6回目のYEAH!
7回目のYEAH!
8回目のYEAH!
9回目のYEAH!
10回目のYEAH!
=> 1..10

irb(main):001:1* (1...10).each do |n|
irb(main):002:1*   puts "#{n}回目のYEAH!"
irb(main):003:0> end
1回目のYEAH!
2回目のYEAH!
3回目のYEAH!
4回目のYEAH!
5回目のYEAH!
6回目のYEAH!
7回目のYEAH!
8回目のYEAH!
9回目のYEAH!
=> 1...10

範囲オブジェクトで、to_aメソッドすると、配列になります。

(1..5).to_a など、繰り返し処理にも応用できます。

irb(main):004:0> (1..5).to_a
=> [1, 2, 3, 4, 5]

irb(main):005:0> (1..5)
=> 1..5

*splat展開でも同じように処理できます。[] で囲います。

irb(main):006:1* [*1..5]
=> [1, 2, 3, 4, 5]

irb(main):007:0> [1..5]
=> [1..5]

[ ] もメソッド

String#[] (Ruby 3.0.0 リファレンスマニュアル)

[] もメソッドなのは知りませんでした。

指定した位置(添字)の文字を返します。

irb(main):015:0> "TheBeatles"[3]
=> "B"

範囲オブジェクトと組み合わせてみます。

irb(main):012:0> name = "TheBeatles"
=> "TheBeatles"
irb(main):013:0> name[3..6]
=> "Beat"

著者の伊藤さんに質問してみた

輪読会内で解決できなかった疑問を直接質問してみました(第2週目に取り組んだ内容)

直接著者に質問できる輪読会。改めて凄いです....

伊藤さん(id:JunichiIto)、ご回答ありがとうございました!

質問内容

「Minitestのテストが失敗した場合のログについて」

今日のチェリー本の輪読会で3.3「FizzBuzzプログラムのテスト自動化」(p.76~)の項目を読み、テストコードと同じような流れで実際にモブプロ形式でテストをやっていたのですが、失敗した時に表示されるログのdiffの部分が分かりそうで分からないみたいな状態になってしまったので質問したいと思います。

# p.76より抜粋
 1) Failure:
FizzBuzzTest#test_fizz-buzz [lib/fizz_buzz.rb:23]:
--- expected
+++ actual
@@ -1 +1,2 @@
-"Fizz Buzz"
+# encoding: US-ASCII
+"16"

このログの中の、

@@ -1 +1,2 @@

の数字が具体的に何を表しているのかがよく分からなかったので、わかりやすく解説してもらえると嬉しいです。よろしくお願いします。

回答

Minitestは内部的にdiffコマンドを実行してdiffを表示しているはずです。

というわけで、Unixのdiffコマンドの仕様を調べるのが良さそうです。

この場合はUnified形式の説明が該当しそうです。

【Linux】diffコマンドで二つのファイルを比較する - Man On a Mission

冒頭で「--- 1つ目のファイル名」と「+++ 2つ目のファイル名」が表示されます。 その後に「@@ -1つ目のファイルの開始行と表示行数 +2つ目のファイルの開始行と表示行数 @@」が表示され、続いて該当箇所の内容が出力されます。

上記、最初の差異を例に取ると、「@@ -1,7 +1,6 @@」は、a.txtの1行目から7行分、b.txtでは1行目から6行分に該当する箇所が表示されていることを意味しています。 次の差異、「@@ -9,3 +8,4 @@」は、a.txtの9行目から3行分、b.txtでは8行目から4行分に街頭する箇所の表示を意味しています。

この説明で考えると、-1 +1,2

  • 1 = 期待値の1行目を表示中(つまり"Fizz Buzz"を指す)
  • +1,2 = 実際の値の1行目から2行分を表示中(つまり# encoding: US-ASCII"16"を指す)

ということになると思います。

TDD とは?

コラムで取り上げられていたテスト駆動開発

輪読会メンバーの yana_giさん(id:yana_g)に教えていただいた、TDDの権威、@t_wadaさんの動画を見てみました。

TDDについて理解しやすい。おすすめ動画です。

f:id:shirotamaki:20210625172120p:plain
「ライオンに怒られる」http://www.publickey1.jp/2018/t_wada01.gif

動画:50 分でわかるテスト駆動開発

channel9.msdn.com

以下、動画内容をまとめと感想になります。

  • TDDのゴール「動作するきれいなコード Kent Beck
  • 2つの道がある。
    • きれいな設計を考えてきれいなコードを書く。(完璧主義)
    • 書いて動かしてからきれいにしていく。(堕落、焦り、恐れ) ← TDDはこっち。
  • TDDのサイクル
    1. 次の目標を考える
    2. その目標を示すテストを書く
    3. そのテストを実行して失敗させる(Red)
    4. 目的のコードを書く
    5. 2で書いたテストを成功させる(Green)
    6. テストが通るまでリファクタリングを行う(Refactor)
    7. 1~6を繰り返す
  • TDDと黄金の回転

  • リファクタリング
    • リファクタリングの軸はブレやすい。(忙しいから。後でいいか。とりあえず動けばいいし。直近で恩恵はないし。自己満だし…)
    • つねにプログラミングの中に組み込もう。TDDの中に組み込む。
    • テストは増やすのは簡単だが減らすのは難しい。
  • 具体的に検証内容を書く(こうなってほしい)
  • ゴールから書いていく。(具体的な例を元に逆算して考えることがポイント)
  • 「ゴールはこうなるから、こうなってほしい」と、具体的に。
  • 小さく考えて、積み上げていくイメージ。
  • 問題を小さく分割し、To-Do リストを作成する。
  • 「テストコードにバグがあったらどうする?」問題。
    • テストから先に書いてエラーを出す。その後に実装する。

競プロでテストを書いてみた

TDDに触発され、早速競プロでもテストを書いてみました。 A問題なのですごく簡単なテストですが、今後はB、C問題でも書けるようになりたいと思います。

A - kcal

def atcoder(x, y)
  if x == y
    x
  else
    3 - x - y
  end
end


require 'minitest/autorun'

class AtcoderTest < Minitest::Test
  def test_atocoder
    assert_equal 1, atcoder(2,0)
    assert_equal 2, atcoder(2,2)
  end
end

参考書籍

🍒 まとめ

先週に続き例題問題では、Minitestを実際に動かすことでテストの理解がより深まりました。 また、TDDについて深堀りすることができたのも良かったです。今後も競プロでMinitestを書いてみたりと日頃から使うことで慣れていきたいと思います。

では、また来週!(次回、第4週目)

チェリー本輪読会 第2週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本輪読会の第2週目のエントリーになります。

第1週目のエントリーはこちら

輪読会の概要については第1週目にまとめています。

🍒 輪読会 第2週目まとめ

第2章2.9.1〜第3章3.4まで

期間:2021年05月31日〜2021年06月04日

ビット演算

  • ビット
    • ビットは、コンピュータの世界における「0か1が入る箱」(1bit)のことを指しています。コンピュータが処理する最小単位です。ちなみに、この箱の数が8個集まるとbyteバイト(8bit = 1byte)となります。ビット - Wikipedia
  • ビット演算
    • ビット(0か1)に着目して、そのON/ OFFを操作すること。これをビット演算と言います。
  • ビット演算子
    • ビット演算子とは、整数に対してビット単位で演算を行う演算子のことです。
    • &|など。(演算子とは、式を記述する際に用いる演算内容を表す記号などのことです)

以下、ビット演算を使い2進数の数字を演算してみます。

0b は、数値の頭につけると2進数の表記になります。返り値は10進数です。

(8進数は、0、16進数は0xです)

以下は、10進数の4649を、頭に0bと付けることで2進数で表現しています。

irb(main):031:0> 0b1001000101001
=> 4649

.to_s(2) とすることで、結果を2進数で確認できるようにしています。

irb(main):018:0> (0b1111).to_s(2)
=> "1111"
  • & ビット積

2つを比較して、どちらも1なら1、どちらか片方が0の場合は0を返します。

irb(main):019:0> (0b1111 & 0b1010).to_s(2)
=> "1010"
  • | ビット和

2つを比較して、どちらかが1なら1、どちらも0の場合は0を返します。

irb(main):001:0> (0b1111 | 0b1010).to_s(2)
=> "1111"
irb(main):002:0> (0b1110 | 0b1010).to_s(2)
=> "1110"

2つを比較して、どちか片方だけ1なら1、どちらも0またはどちらも1の場合は0を返します。

irb(main):025:0> (0b1010 ^ 0b00110).to_s(2)
=> "1100"
  • >> 右ビットシフト

スイッチのON/OFFの状態を、右に1個ずらします。

irb(main):039:0> (0b1100 >> 0b1).to_s(2)
=> "110"
irb(main):040:0> (0b1100 >> 0b10).to_s(2)
=> "11"
  • << 左ビットシフト

スイッチのON/OFFの状態を、左に1個ずらします。

irb(main):028:0> (0b1000 << 0b1).to_s(2)
=> "10000"
irb(main):029:0> (0b1000 << 0b10).to_s(2)
=> "100000"

数値クラス(Numeric)

  • Integerクラス(整数)
  • Floatクラス(実数、少数)
  • Ratinalクラス(有理数
  • Complexクラス (複素数

複素数がよくwakaran状態だったので深堀りしてみました。

  • 実数  real number(実際に存在する数)
  • 虚数 imaginary number(想像上の数)
  • 複素数(complex number 複合、混合)

    • 実数+虚数の組み合わせを複素数といいます。
  • 有理数 は整数および分数のことを指します。

  • 無理数 は分数で表せない √2、π(円周率)などを指します。

これ以上深追いすると沼から抜け出せそうにないのでこのへんで切り上げたいと思います。 データサイエンス難しい…!

ちなみに最近読んでいる本をご紹介。数学的な考え方が勉強になりました

www.hyuki.com

Rubyの式の評価基準

大原則、Rubyでは「左辺から順番に式を評価」していきます。

  • &&の戻り値と評価を終了するタイミング

左から評価された式は、式全体のまたはが確定すると終了になります。

全部、の場合は最後に評価された値が返ります。以下のGeogeが返ってきてる例です。

今回の例は文字列を&&で比較していますが、文字列の内容は関係ありません。文字列であれば真の評価となります。

irb(main):056:0> "Paul" && "Paul" && "Paul"
=> "Paul"
irb(main):057:0> "Paul" && "Ringo" && "Jhon" && "George"
=> "George"

false と、nilが存在する場合は、そこで式の評価が終了となり、false、nilが返ってきます。

irb(main):058:0> "Paul" && nil && "Paul"
=> nil
irb(main):059:0> "Paul" && false && "Paul"
=> false
  • || の戻り値と評価を終了するタイミング

||を使う場合も同じルールに沿って評価されますが、||は「またはor」と訳せるので演算子の違いによる戻り値の変化に注意が必要です。以下は、PaulまたはRingとなりますが、'Paul' の時点でと評価されるのでPaulが返ります。

irb(main):074:0> "Paul" || "Ringo"
=> "Paul"

以下は、falsenilが左辺にありますが、Ringoを評価した時点でなら全体もになるので、最初のRingoが返ってきています。

irb(main):078:0> nil || "Ringo" || "Jhon"
=> "Ringo"
irb(main):079:0> false || "Ringo" || "Jhon"
=> "Ringo"

演算子の優先順位

普段、優先順位を気にすることが少ない演算子

andorは条件分岐ではあまり使われないようです。用途としては制御フロー(コンピュータプログラムで、命令が実行される流れを定めたもの)に 向いています。

高い
        !
        &&
        ||
        not
        and or
低い

if文と反対の意味で使えるunless文

条件が偽になった場合に処理を実行する条件分岐です。

まずは、普通のif文で表現してみます。!= は、値が違えばOKと判断されます。

irb(main):085:0> shot_count = 9
=> 9
irb(main):086:1* if shot_count != 10
irb(main):087:1* puts "ざんねん!"
irb(main):088:1* else
irb(main):089:1* puts "ストライク!"
irb(main):090:0> end
ざんねん!
=> nil

次は、unless文を使って書き換えてみます。

irb(main):117:0> shot_count = 7
=> 7
irb(main):118:1* unless shot_count == 10
irb(main):119:1* puts "ざんねん!"
irb(main):120:1* else
irb(main):121:1* puts "ストライク!"
irb(main):122:0> end
ざんねん!
=> nil

複数の条件を指定する時に便利なcase文

elsifでも表現できますが、「比較したいオブジェクトや式」があり、そこを基準に「場合分け」したい場合に使うとシンプルに書けます。

case 比較したいオブジェクトや式
when11に一致する場合の処理
when22に一致する場合の処理
else
    どれにも一致しない場合の処理
end

まずは、elsifを使ったパターン。

irb(main):012:0> name = 'Paul'
=> "Paul"
irb(main):013:1* if  name === 'Ringo'
irb(main):014:1*  "I play the drums!"
irb(main):015:1* elsif name == 'Paul'
irb(main):016:1* "I play the bass guitar!"
irb(main):017:1* else
irb(main):018:1* "I play the guitar!"
irb(main):019:0> end
=> "I play the bass guitar!"

case文を使い書き換えてみます。

case name が基準となり、nameがtrueを返したら実行されます。elsifを使ったパターンと比べても可読性がよくシンプルに書くことができます。 

irb(main):020:0> name = 'Paul'
=> "Paul"
irb(main):021:1* case name
irb(main):022:1* when 'Ringo'
irb(main):023:1* "I play the drums!"
irb(main):024:1* when 'Paul'
irb(main):025:1* "I play the bass guitar!"
irb(main):026:1* else
irb(main):027:1* "I play the guitar!"
irb(main):028:0> end
=> "I play the bass guitar!"

三項演算子

コードレビューを何度か受けて使えるようになり、今は普段から意識して使っている三項演算子

コードを短く書くことができるので、何となくイケてる感じがだせるので好きです笑。輪読会でも一行でバシッと書けてカッコいい!という意見が多かった人気の書き方です。

とはいえ、何でもかんでも使えばいいものではないようで、可読性には注意する必要があります。

  • 公式 式 ? 真だった場合の処理 : 偽だった場合の処理

当時、コードレビューで指摘を受けたコードを取り上げたいと思います。

まずは、自分で考えて書いたベーシックなif文のプログラムです。

if options["y"]
  year = options["y"].to_i
else
  year = Date.today.year
end

上記のif文を三項演算子で書き換えてみます。スッキリ!

year = options["y"] ? options["y"].to_i : Date.today.year

角谷さんにレビューしてもらった話

少し前の出来事ですが、三項演算子 ternary operatorについてもうひとつ。

TwitterでBadコードと、Goodコードをツイートしたところ、 なんと、角谷さんにレビューしていだきました!

Badコードの例

自分なにり考えてみたGoodコードの例

角谷さんにレビューいただいたGreatコードの例

odd と even の覚え方

odd(奇数)even(偶数)をいつも取り違えてしまう問題...

覚え方について輪読会で質問してみました(輪読会はこういった小さな疑問も気軽に聞けるのがいいですね) みんなの覚え方を聞いて面白く参考になりました。

  • oddは3文字なので奇数。evenは4文字なので偶数。
  • デヴィット・ボウイ(odd eyes、Space Oddity)

これまた完全な余談ですが、デヴィット・ボウイの名前を聞くとなぜか曲より先に思い出してしまう戦場のメリークリスマス。「メリークリスマス、ミスターローレンス!」声に出して言いたくなる名台詞です。

メソッド定義はメソッド名の symbol が返り値になる

メソッドを定義すると返ってくる、:foo

気になって調べたところ、メソッド名のシンボルでした。

irb(main):017:1* def foo
irb(main):018:1* end
=> :foo

エイリアスメソッド alias method

メソッドの別名のことです

るりまにも書いてありましたが、最初はまったくわかっていませんでした(気づきませんでした)。

自分でエイリアスメソッドを定義することも可能です。

f:id:shirotamaki:20210620132803p:plain String#length (Ruby 3.0.0 リファレンスマニュアル)

aliasという英単語、プログラミング学習を始めてから初めて知った言葉のひとつだけど、「偽名」という意味で使うことを今日犯罪ドキュメンタリーを見ていて知った。

トミーさん(id:eatplaynap329)が取り上げていたエイリアスの補足情報!なるほど〜おもしろい!

require、require_relatve、loadの使い分け

requireは、「re(再び)quire(求める)〜を必要とする、要求する」の意味があります。 標準ライブラリやgem(外部ライブラリ)は、事前にライブラリを読み込まないと使えません。 ライブラリを読み込む際には、ファイルへrequire 'ライブラリ名' と書きます。

まずは、requirerequire_relativeの使い分けについてです。 結論としては、ライブラリやgemをrequireするときはrequireを使い、自前で書いたコードを読み込むときはrequire_relativeが望ましいようです。

requireは、自分が今実行しているディレクトリ(カレントディレクトリ)が基点になります。

require_relativeは、自分のファイルパスが起点として、読み込むファイルを指定できます。

Kernel#require (Ruby 3.0.0 リファレンスマニュアル)

$ tree
    /rindokukai
          └── /ruby-book
                ├── /lib
                │   └── rgb.rb
                └── /test
                    └── rgb_test.rb
  • require

自分が今いるディレクトリが基点になります。

下記の例では相対パスを記述しています。実行しているパスが基点になりますので、下記で記述しているパスの意味は、ruby-bookの一段上のディレクトリを基点(つまり/rindokukaiディレクトリに今いるという事)としてrequireする。と表しています。例えば、/libディレクトリに移動してテストを実行しすると、エラーが表示されます。

require 'minitest/autorun'
require './ruby-book/lib/rgb'

または、相対パスではなく、絶対パスを記述することでrequireすることもできます。

絶対パスを記述すれば、自分が今いるディレクトリは関係ないので予測しづらいエラーを回避できます。

require 'minitest/autorun'
require '/Users/shiro/rindokukai/ruby-book/lib/rgb'
  • require_relative

現在のrgb_test.rbファイルを基点として指定します。下記はrgb_test.rbファイルにに記述してあります。自分のファイルパスが起点となりますので、自作のプログラムではrequire_relativeが推奨されています。

require 'minitest/autorun'
require_relative '../lib/rgb'
  • load

無条件に毎回読み込みが行なわれます。逆にrequireは一度しか読み込まれません。一般的に、loadは設定などのファイルを読みこませることが主な用途のようです。load絶対パスの指定が必要になります。

putsメソッド、printメソッド、pメソッド

こちも似たようなメソッドの代表格になります。

ひとつずつ見ていきたいと思います。

  • putsメソッド
    • 出力後に改行される
    • 要素ごとに改行される
    • 配列をputsすると各要素が改行されて出力される
    • 呼び出すメソッドはto_s (Stringを返す)
    • 戻り値はnil
irb(main):044:0* a = 1
=> 1
irb(main):045:0> puts a
1
=> nil
irb(main):046:0> a.class
=> Integer

irb(main):029:0> puts [1,2,3]
1
2
3
=> nil
  • printメソッド
    • 出力後に改行されない
    • 呼び出すメソッドはto_s (Stringを返す)
    • 戻り値はnil
irb(main):042:0> print [1,2,3]
[1, 2, 3]=> nil
irb(main):043:0> print 123
123=> nil
irb(main):047:0> p [1,2,3]
[1, 2, 3]
=> [1, 2, 3]

irb(main):067:0> p "123\nabc\t"
"123\nabc\t"
=> "123\nabc\t"

Minitestを使うためにはrequireが必要

Minitestとは、テストを自動化するためのテスティングフレームワークです。

library minitest/unit (Ruby 1.9.3)

Rubyには最初からインストールされています。

しかし使用するためには、`require 'minitest/autorun' と書き、ライブラリを事前に読み込む必要があります。

また、classに継承するためにはMinitest::Testと書く必要があります。(クラスの継承)

require 'minitest/autorun'

class MusicTest < Minitest::Test
    def test_music
        asser_equal 'BEATLES', 'beatles'.upcase
  end
end

Minitestで使用する主要な3つのメソッド

以下の検証メソッドを使います。他にもたくさんメソッドが容易されているようですが、チェリー本では以下3つだけが使用されています。まずはこの3つを使いこなせるようにしたいと思います。

  • assert_equal b, a aがbと等しい場合はパスする。
  • assert a aが真であればパスする。
  • refute a aが偽であればパスする。

assert は「結びつける、〜ということを断言する」の意。

refute は、「〜の誤りを証明する」の意

assertion (アサーション)とは、オブジェクトや式を評価して、期待された結果が得られるかどうかをチェックするコードのことです。

refureメソッドとassert_notメソッドの違い

上記のrefuteメソッドと似たassert_notメソッドなるものが存在します。

輪読会で上記の疑問が持ち上がり、卒業生の(id:masuyama13)さんのブログを参考にさせていだきました。

Ruby refute と assert_not の違いを調査 - No Solution for Life

irb

irb は Interactive Ruby の略です。interactive(双方向、対話式) irb を使うと、Ruby の式を標準入力から簡単に入力・実行することができます。

library irb (Ruby 3.0.0 リファレンスマニュアル)

普段からirbを使い実際の動きを確認するようにしていますが、今までちゃんと活用できていないことを痛感しました。その中でもirbを起動する場所(カレントディレクトリ)が影響することについては、まったく知りませんでした。どこで起動しても同じものだと思っていました。

こういった普段何気なく使っているツール等についても、輪読会を通してinteractiveに学習することで、気付きがあり学ぶことができています。

参考書籍

🍒 まとめ

自信のなかったMinitestの項目を無事終えることができました。

第3章は、プラクティスを進める上でテストコードが必須ではなかったため当時は軽く読み流していました。そのためwakaran状態だったのですが、今回輪読会を通してwakaru に変わり、今週も輪読会効果は絶大でした!

特に、モブプロのドライバーとして自分で手を動かしながらコードを確認することができたのが大きかったです。エラーコードを出したり、パスさせたり、「何がどうなっているのか?」実際に動かしてみることが理解の助けになりました。

では、また来週!(次回、第3週目)

チェリー本輪読会 第1週目まとめ

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

チェリー本の輪読会に参加し4週間が経ちました。 https://shirotamaki.hatenablog.com/about 6月29日に登壇するLT会【初心者歓迎】Ruby LT会@オンライン - connpassのネタ探しも兼ね、輪読会で学んだことをブログ記事にまとめたいと思います。

まずは第1週目のまとめ記事になります。2週目以降については随時エントリーしていきたいと思います。

では早速。

フィヨルドブートキャンプでは、有志が集い輪読会が開催されています。

主催者の トミー さん(id:eatplaynap329)の呼び掛けにより始まった輪読会(トミーさんいつもありがとうございます!)

今ではレギュラーメンバー7名に加え、日によりますがラジオ参加を含めると最大16名近く集まる一大派閥? 笑 です。 オンライン上の集まりとあって、始めた当初はぎこちなかったのですが、今ではみんなとワイワイ楽しく開催しています。

まずは簡単に輪読会の概要を書いてから、本題の学んだ内容を書いていきたいと思います。

🍒 概要

課題本

gihyo.jp

通称:チェリー本 (当ブログでは今後チェリー本と記載を統一させていただきます)

輪読会の目的

  • Rubyを楽しむ
  • チェリー本を深く理解する
  • 得た知識をみんなで共有する
  • 疑問をたくさん持ち、著者の伊藤さんに直接質問してみる(伊藤さんはフィヨルドブートキャンプのメンターもされています)

輪読会の形式

  • 特に期限を設けた輪読会ではないため、第1章から順番に読み進めていく形式を取っています。
  • 必要に応じてモブプロを行い、実際に動きを確認しながら進めていきます。

参加条件

開催日時・場所

  • 平日 9:00〜10:00
  • Discordの輪読会チャンネル

🍒 輪読会 第1週目まとめ

では本題へ。

第1週目で学んだことについて書きたいと思います。

第1章1.1.1〜第2章2.8.6まで

期間:2021年05月25日〜2021年05月28日

Rubyのバージョンは?

Rubyのバージョンは「3.0.0」で進めていくことにしました。(輪読会スタート当時の安定版は3.0.1)

本書はRuby2.4.0を元に書かれていますが、必要に応じて著者の伊藤さんのQiita記事で補完すれば、バージョンは3.0.0で問題なさそうです。

Ruby 3.0で発生する「プロを目指す人のためのRuby入門」との差異について - Qiita

Rubyのバージョンの見方

例: 3.0.0

Ruby のバージョンは、major.minor.teeny という3つの形式で表されています。

  • majorバージョンは、アーキテクチャやコンセプトの変更が行われるとバージョンアップします。
  • minorバージョンは、大幅な仕様変更や機能追加されるとバージョンアップします。
  • teenyバージョンは、軽微な仕様変更、小さな機能追加等が行われるとバージョンアップします。(teenyは「小さな」の意)
$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]

エディタは何を使うの?

エディタについては、各々自由に好きなものを使うことになっています。

しかし、参加メンバーに伺うとRubyMineが圧倒的多数を占めていました。 私自身、VSCodeとRubyMineしか使ったことがなく使用歴も浅いため、RubyMineの便利さにまだそこまでメリットを感じていませんでした。しかし、長くRubyMineを使っている方や現役のプログラマの方のご意見を伺うと(著者の伊藤さんもRubyMineを愛用している)、「Rubyを使うならRubyMine!!」と、圧倒的な人気を誇っているようで、Rubyを書くことに適したとても拡張性の高いエディタであるのは間違いなさそうです。まだまだ使いこなせていないので、輪読会を通してRubyMineとも仲良くなりたいと思います。

丸め誤差

チェリー本ではコラムの項目で取り上げられている内容ですが、きちんと理解できていなかったため輪読会では時間を多めに取り見直しました。同じ受講生の方が、この話題をブートキャンプ内で取り上げておりそちらのまとめた日報を参考にさせていただきました。

他、以下書籍も参考にしました。

キタミ式イラストIT塾 基本情報技術者 令和03年:書籍案内|技術評論社

実際にirbを使い動かしてみます。 コンピュータの計算では、10進数を2進数へ変換して計算されるため以下のような結果となります。

irb#1(main):001:0> 0.1 * 3.0
=> 0.30000000000000004

Rational(有理数クラス)を使うことで丸め誤差問題を解決できます。 有理数とは、「整数および分数」のことです。

irb#1(main):003:0> 0.1r * 3.0r
=> (3/10)   

# 10分の3 

rationalizeメソッドを使うことでRationalクラスの数値に変換できます。

Float#rationalize (Ruby 3.0.0 リファレンスマニュアル)

irb#1(main):008:0> a = 0.1
=> 0.1
irb#1(main):009:0> b = 3.0
=> 3.0
irb#1(main):010:0> d = a.rationalize * b.rationalize
=> (3/10)

小数点に戻したい場合は、to_ffはfloatのf。浮動小数点数の意。

irb#1(main):011:0> d.to_f
=> 0.3

Rubyでは(Ruby以外の言語でもよく発生するらしい)丸め誤差問題を回避するため、上記の対応を行う必要があります。少数を扱う計算で思わぬ結果に出くわしたとき、今回の件を思い出し対処したいと思います。

falseとnil以外はすべて真(true)

Rubyの真偽値。 falsenilであれば偽になる。他の言語だと珍しいことのようです。 各言語におけるtrue/falseまとめ - 昼メシ物語

if文のelseは省略可能

条件分岐で多用しているif文。else(elsifも同様)が不要なのは知らなかったです。 今までずっと付けていた気がする....汗。今後は外していきます。

後置if

Rubyのif文は修飾子として文の後ろに置くことができます。 まずは通常のif文から。

irb(main):004:0> x = "foo"
=> "foo"
irb(main):005:1* if x == "foo"
irb(main):006:1*   puts "bar"
irb(main):007:1* end
bar
=> nil

後置ifを使い書き換えてみます。 一行でシンプルに書けます。競プロとかで使いこなしたいです。

irb(main):008:0" puts result = "bar" if x == "foo"
bar
=> nil

printfメソッドとsprintfメソッドの違い

printfsprintfメソッドのfは、formatから来ています。formatはエイリアスメソッド。 指定されたフォーマットの文字列を作成することができるメソッドです。

  • pritntfメソッドは、整形した文字列をコンソールに出力する。
  • sprintfメソッドは、整形した文字列をオブジェクトにして返す。

%bは、整数を2進数表現で出力する指定子です。 Kernel.#format (Ruby 3.0.0 リファレンスマニュアル)

printfメソッド

nilが返ってきます。

irb(main):005:0> printf('%b', 4)
100=> nil

sprintfメソッド

文字列オブジェクトが返ってきます。

irb(main):006:0> sprintf('%b', 4)
=> "100"

メソッド名は「ひらがな」「漢字」、そして「絵文字」もOK

普段、英語表記でしかメソッド名を命名したことがなかったので読み直してみて改めて驚きました。

絵文字もメソッド名にできるなんて遊び心があって面白いですね。

irb(main):004:1* def ちぇりー
irb(main):005:1*   puts "さくらんぼ"
irb(main):006:1* end
=> :ちぇりー
irb(main):007:0> ちぇりー
さくらんぼ
=> nil

irb(main):008:1* def 錯乱坊
irb(main):009:1*   puts "赤い果物"
irb(main):010:1* end
=> :錯乱坊
irb(main):011:0> 錯乱坊
赤い果物
=> nil

irb(main):012:1* def 🍒🍒🍒
irb(main):013:1*   puts "絵文字"
irb(main):014:0> end
=> :🍒🍒🍒
irb(main):015:0>  🍒🍒🍒
絵文字
=> nil

returnは、メソッドを途中で脱出するときに使われることが多い

普段は省略されることの多いreturn文

他の言語経験者だと違和感が強いらしいです。

returnを書かなくても結果は同じ。以下は書いているパターン。

irb(main):007:1* def add(a,b)
irb(main):008:1*   return a + b
irb(main):009:1* end
=> :add
irb(main):010:0> add(5,7)
=> 12

上記の使い方とは別で、returnはメソッドを途中で脱出するときに使わることが多いです。 下記、条件が一致したときにメソッドをすぐに終了させる例です。

irb(main):014:1* def min(x, y)
irb(main):015:2*   if x < y
irb(main):016:2*     return "xの方が小さい"
irb(main):017:1*   end
irb(main):018:1*   "yの方が小さい"
irb(main):019:1* end
=> :min
irb(main):020:0> min(4, 6)
=> "xの方が小さい"
irb(main):021:0> min(7, 3)
=> "yの方が小さい"

ガード節(Guard Clause)

Rubyに限らず、多くのプログラミング言語リファクタリングで有用な実装手法です。 「ガード節」「ガード条件」「ガード構文」などと呼ばれています。

return foo if bar (barがtrueだったらfooを実行)といった形式を覚えておきたいです。

ちなみに、ガード節を書いた文の下一行は空けておくのがお作法らしいです。

また、完全なる余談ですが、ブログを執筆している今日6月18日はポールマッカトニーの誕生日です。 ハッピーバースデーポール!!

irb(main):016:1* def play(name)
irb(main):017:1*   return 'nameを入力してください' if name.nil?
irb(main):018:1*
irb(main):019:2*   if name == 'Paul'
irb(main):020:2*     'I play bass guitar!'
irb(main):021:2*   elsif name == 'Ring'
irb(main):022:2*     'I play drums!'
irb(main):023:2*   else
irb(main):024:2*     'I play guitar'
irb(main):025:1*   end
irb(main):026:0> end
=> :play
irb(main):027:0> play('Paul')
=> "I play bass guitar!"
irb(main):028:0> play(nil)
=> "nameを入力してください"
irb(main):029:0>

参考書籍

🍒 まとめ

第一週目を終えた率直な感想としては、「え?こんなこと書いてあったけ?」の連続で、改めて人間は忘れる生き物であると思い知らされました。

忘却曲線 - Wikipedia

しかし、何度も読み返すことで知識が定着し、そこから多くの学びを得ることができているのは間違いありません。良い教科書を何度も読み直すことの大切さを改めて痛感し、知識が知恵になっている感覚です。 また、輪読会の効果をとても感じています。疑問を持ちその場で共有し解決していけることや、自分では読み取れなかった情報を他のメンバーからの共有で気づけたりなど、これは一人では得れない学びです。

今回記事にまとめた第1週目は、本書を読み進めると同時に輪読会の進め方の意見を出し合いつつ、時間配分、司会決めなどルール整備も行いました。ルール整備とはいってもガチガチな取り決めはなく、上記輪読会の目的に沿って、まずは「楽しむ」を念頭におき、みんなでワイワイやっています。

まだ始まったばかりですが、会の前後では雑談をしたり、「チェリー本終わったら別の本でもやりたいですね〜」と、次回の候補本の話しがあったり、輪読会を通して同じ目標を持つ仲間ができとても楽しいです。

今後も上記スケジュールで随時開催しておりますので、気になった受講生の方はもちろん、卒業生、メンター、アドバイザーのみなさん!ぜひ遊びに来てください!お待ちしております〜!

最後に、著者の 伊藤さん(id:JunichiIto)、卒業生でもあり先輩プログラマの 森塚さん(id:sanfrecce-osaka)には日頃から多くのアドバイスをいただき感謝申し上げます!

今後ともどうぞよろしくお願いします!!

初めてのLT会vol.7を開催しました

f:id:shirotamaki:20210427213411p:plain
初めてのLT会vol.7

はじめに

2021年4月24(土)にフィヨルドブートキャンプ内で開催されたLT会。 今回、開催内容についてまとめましたのでブログに書きたいと思います。

speakerdeck.com

フィヨルドブートキャンプとは?

プログラマーとして転職を目指せるだけのスキルを身につけることを目標としたオンラインプログラミングスクールです。 現在、私もここで学習しておりプログラマ転職を目指しています。幅広く網羅された学習カリキュラムや豪華メンター陣。卒業までに平均900時間かかると言われており、日々の学習はとても大変ですが、メンターや卒業生、そして受講生同士との距離が近く、とてもアットホームなスクールです。

bootcamp.fjord.jp

そもそもLTって何?

LT(エルティー)は Lightning Talk(ライトニングトーク)の略で、Lightning は「稲妻」という意味。 5分程度の短いプレゼンのことで、短いこと以外基本的にルールはありません。 フィヨルドブートキャンプ内で行われるLT会は、主に「初めてLTをする方」向けの受講生が自主的に行うイベントになります。 Remoを使い、オンライン上で開催しています。

remo.co

LT(Lightning Talk)の歴史

giantech.jp

テーマ「プログラミング学習を通して得た成長」

今回のテーマは「成長」 6名の登壇者に皆さんそれぞれが学び感じた「成長」について発表してもらいました。

登壇者

@kasai441さん 「ログをみてできるようになること」

つい焦ってログを見ずに先走ってしまう傾向のある私には、改めてログの大事さが分かり勉強になりました。「英語は嫌だな〜」「ログ見ないでも何とかなるでしょ〜」と、理由を付けて避けてきましたが、しっかりとログを読む訓練をすることが、今後先の成長につながると思いました。逃げずに向き合いたいと思います!トップバッターという緊張感の中、とても落ち着いておられ、声も聞き取りやすく素敵でした。

speakerdeck.com

@udaikueさん 自分の学習ペースをつかむまで」

カープファンのudaさん。2番セカンドの大役を真正面から受け止め、そして打ち返す。とても素晴らしい発表でした! フィヨルドブートキャンプはカリキュラム量も膨大なので、そう簡単には卒業できません。私も同じく未経験からのスタートのため、今後先の事が不安になることも多く、udaさんの悩みにとても共感できました。自分の悩みや考えにしっかりと向き合い、自分の言葉で伝える。そんな姿に胸打たれました。「プラクティスファースト」この言葉は胸にしっかりと刻みたいと思います。

speakerdeck.com

@Hossoさん 「焦らずじっくり学習に取り組む大切さ」

ベーシストのHossoさん。リズムよく軽快な発表内容でとても聞きやすく楽しく拝聴できました。 自分も焦って進めてうまくいかない事が多いのでとても勉強になる内容でした。「負のスパイラル」まさにこの状態に陥ることがよくあり、他人は他人と、頭では分かっていても、つい気になってしまい上手くいかない(無限ループ)...。この悪循環にはまり込まないように、少しでも目に見える形でのアウトプットを意識し、焦らず学習に取り組んでいきたいと思いました。 speakerdeck.com

@otomiさん 「Rackから見るミドルウェアの世界」

先のプラクティスの内容なため少し難しい内容でしたが、そんな未経験者にも分かりやすい発表内容で、ミドルウェアの世界を垣間見ることができとても勉強になりました。特に、実演(デモ)では実際に動くコードを発表されていてイメージしやすく分かりやすかったです。DJ活動もされているotomiさん、LT後の質疑応答でDJの話になりましたが、好きなことに熱く面白くお話しできるのってとても素敵だなと思いました。 speakerdeck.com

@shirotamaki 「Rubyリファレンスマニュアルと仲良くなろう」

私のLT。前回ブログにまとめた内容を元にLT資料を作成しました。現在、Rubyのプラクティスに取り組み日々お世話になっている公式ドキュメント。今回発表することで、改めて一次情報の大切さを痛感した学びの多いLTでした。

以下、みなさんから頂いたあたたかいお言葉を一部ご紹介!

  • るりまとの付き合い方がとても参考になりました!一次情報大事!
  • 公式ドキュメントの大切さを改めて感じましたー!自分も逃げずにしっかり向き合おうと思いました。
  • 自分もRubyのプラクティスに入ったばかりのころはるびまを読むことに苦手意識があったのですが、結局欲しい情報は公式ドキュメントに全部書いてあるんですよね。 自分は現在比較的るびまの読み方に慣れてきた頃なんですが、改めてるびまをどう読めばいいか考えるきっかけになりました。また、一次情報の重要性も再認識しました。 イシューから始めよ、読んでみようと思います!
  • るりま難しいですよね!!技術書を読んだりるりまのヘルプで語彙を増やしたりという、始めたばかりの頃知りたかった具体的なアドバイスがたくさんあってよかったです!

speakerdeck.com

@kamiokanさん 「フィヨルドブートキャンプに参加して私の身に起きた8つの変化」

今回、LT登壇順は公平を期するために「あみだくじ」で行いました。そんな中決まった登壇最後のkamiokanさんの番。大トリにふさわしいとてもダイナミックな楽しいLTでした!フィヨルドブートキャンプに参加して起きた8つの変化。そのすべてが共感できました。なかでも7番目の『「わからない」ということに絶望しなくなった。』コツコツと積み上げ、「わからない」は伸びしろであるとポジティブに捉えること。絶望することはまったくない。とても勇気をもらえました。

speakerdeck.com

オーガナイザーとしての感想

以上、6名の方にご登壇いただいた今回のLT会。 皆さんそれぞれが学んだこと、感じたことをスライドにし、そして言葉にし自由に発表する、とても素敵な楽しい会でした! オンライン上という事もあり、北は北海道、南は沖縄まで幅広い方にご参加いただきました。 (メンターやアドバイザー、卒業生、そして現役生、参加人数は40名ほど) ご視聴いただいた方とは、オンラインのためチャット上でのやり取りになりましたが、とても盛り上がり皆さんに楽しんでもらえた会となりました。

今回、企画運営から携わらせていただきましたが、とても貴重な経験になりました。 普段学習では使わない、keynoteやRemo などのアプリケーションを覚えるよい機会にもなりました。

今回、オーガーナイザーをやろうと思ったきっかけでもある、Yusukeさんのブログ記事。 まさに、コンフォートゾーンを抜け成長できたとても貴重な経験になりました。 yskmtg.hatenablog.com www.lifehacker.jp

ゼロかイチでは大違い。メンターの伊藤さんがおっしゃっていた通り、 ゼロからイチになった瞬間、今回のテーマでもある一番の「成長」を感じた、そんなLT会でした。

さいごに

今回で第7回を終えた「初めてのLT会」

フィヨルドブートキャンプのLT会の目的にもある「LTの練習」

こんなにもよい環境でLTができる機会はあまりないのではないか?終わってみて改めて感じます。 LT会後に頂戴したアンケート結果や、チャットや日報などでいただくあたたかいお言葉やアドバイス

まだ登壇されたことのないフィヨルド生にはぜひ!登壇をオススメします! そして、まだ参加されたことのないフィヨルド生の方は、ぜひ一度参加してみてください!

ご登壇、ご参加いただいた皆さん、 そして運営のkomagataさん、machidaさん、 この度はこのような貴重な機会をいただきありがとうございました!