Roll With IT

tamakiのIT日記

あんちぽさんトークイベント感想

先日、フィヨルドブートキャンプ内でトークイベントが開催されたのですが参加してきました!

スペシャルゲストとして、GMOペパボ株式会社 取締役CTO CTO室室長の栗林 健太郎さんを招いての、まさにスペシャルな最高の夜でした。

またなんと!運良くトークイベント後の交流タイムには、直接お話しすることもできました。

当初、個人の日報に書こうと思いましたが、思ったより長文になってしまったため…。 せっかくなので、ブログに書きたいと思います。

tech.pepabo.com

注:以下はあんちぽさんの意見はこうだった。ということではなく、トークイベントや直接お話しをお伺いする中での僕の解釈、感想になります。

イベントのスライド

speakerdeck.com

キャリアキーノート

以前に一度スライド等を拝見したことはあったのですが、直接キャリアキーノートについてのお話しを聞け興味深かったです。

過去、ペパポさんで働いている方のお話しを伺う機会が何度かあったりしたのですが、皆さんそれぞれ自分自身の軸(価値観)をしっかり持っておられる方が多い印象で、言語化の凄さも相まってか強く印象に残っています。それもこのような取り組みが影響しているのかなと感じました。

僕の直近の課題は、能力(プログラミングスキル)を得ることが最優先ではありますが、価値観を中心とした内的キャリアについての大事さを考えさせられました。

blog.hifumi.info

speakerdeck.com

他人の人生を生きない

そのつもりはなくても、つい他人の人生と自分の人生を比較し落ち込んだり...。 他人が決めた人生に勝手に憧れ、勝手に失望しているのは自分なんですが、こういった考えも自分の価値観がブレているせいであることが理解できました。これは人それぞれだとは思いますが、僕自身は上手くいかないことが続くと価値観がブレてしまうことが多いので「おい!今、他人の人生生きてるよ。危険だよ。」と気づけるように注意していきたいです。

欲望の三角形の話しは初めて聞きましたが勉強になりました。こういったことを知り意識するだけでも全然違うなと思いました。

rinnsyou.com

「逃げ切り」マインド

生存者バイアスからの「逃げ切り」マインドのお話しも面白かったです。

「逃げ切る」ということを考えたことはないですが、確かに最近耳にすることが増えたフレーズだと思いました。Fireとかもその関係から派生した言葉なんですかね?もちろん、お金に困る生活はしたくないですし、働かずとも十分な収入を生むことができる仕組みを持つことは素晴らしいと思います。Fire自体は経済的に自立し、そこから逃げるわけではなく、自由に何かに挑戦していくことなのかもしれませんが、早期リタイアという言葉が絡んでくるので、何かモヤモヤしますね。

Fireについては、このnoteが面白かったのでご紹介。

comemo.nikkei.com

いずれにせよ、個人的な思いとしては健康でい続けれる限り、逃げ切りマインドではなく、追い続けたい(仕事も含めて)と思っています。死ぬまで自分の価値観を元に、やりたくてしかたがない(必然性)そんな仕事ができたら最高です。

Unknown unknowns

普段、何も考えず生きていると、つい「Known, Knowns」になりがちだなと思いました。

「Unknown unknowns」のような事柄にも、勇気を持って踏み出せるように心がけたいです。

ちょっと違うかもしれませんが、新商品が出たら買ってみたり、普段行かない道を通ってみたり、小さなことですが出来ることから行動しようと思いました。それから、仕事や人生の大事な場面でも「Unknown unknows」を楽しめる自分でいたいです。

自分を小さな価値観に矮小化するぐらいなら自分を放り捨てる

少し耳の痛い話でした。

捨てたいんだけど捨てられない。そんな僕にとっては必要ないはずの価値観に固執することがあります。とくに無意識だったり、いろいろと心に余裕がないと、その必要ない価値観が出てきて邪魔をしてきます。そんなもの捨ててしまえ!と、わかってはいるんだけど捨てられない。「必然性」の話しもされていましたが、僕にとっての必然性のレベルが、まだまだ低いのだと痛感しました。これは、僕に取っては簡単な問題ではないですが、今回の話しも参考に考えていきたいと思いました。

紹介されていた書籍

books.bunshun.jp

www.chikumashobo.co.jp

面白そうで早速購入してみました。 読書についてのお話しもお伺いしたのですが、定期的に書店に足を運ばれ、上から下まで新書をチェックされているそうです。「Amazonなどはその人の興味に合う本を紹介してくるのでなかなか新しい本との出会いが少ない。書店へ出向くことで新しい出会いがある。」たしかに。納得です。

ベーシックなスキル

ベーシックなスキルについて「非常に変わりにくい部分」が重要という話しもありました。

  • 変わりやすい -> 変わりにくい
  • 具体的 -> 抽象的

自分のイメージだとこんな感じです。

(これが合ってるかはわかりません。今現状の自分の解釈です)

三角形で表すと、頂点に近づくと抽象的な物事で、逆に底辺に近づくとより具体的な物事なイメージです。それぞれが分断されているというわけではなく、つながりをもって存在しているイメージです。オブジェクト指向の継承みたいなことなのかなと思いました。

f:id:shirotamaki:20211002101313p:plain

非常に変わりにくい部分は、より抽象的な物事で実体として理解しづらいです(知能、創造性、概念的能力…)。実際にこの能力を伸ばすためには具体的に何をすればいいのか?ハッキリとはわかりませんが、価値観を育てることが、ベーシックなスキルの助けになることは理解できました。

しかし、これも本を読んだり、自分で考えたり、経験してみたり、普通のこと(以下で書いてみました)を継続してやり続けるしかないのかなと思いました。プログラムを書く前に行うタスクばらしのように、具体的な物事に小さく切り分けてみたり、それからクラスやメソッドなどで、抽象的な物事としてひとつにまとめたりするように、具体と抽象を行ったり来たりしながら、さまざまな物事をプログラミングと同じく当てはめて考えていけるようになることが大事なのかなと思いました。

扱うテーマの選択は大事ですが、重要なベーシックなスキル取得への道は、地道な抽象・具体の行動の積み重ねしかないのかなと思いました(近道はない)。

普通のことをちゃんとやる

あんちぽさんの今までの経歴ややってきたことを見聞きしていると、何か特別なことをしてきたんではないかと思うこともあります。しかし、超人にしかできないような特別なことはなく、本を読んだり、興味のあることにとことんハマってみたり、自分の価値観をベースに淡々と積み上げられてこられたんだなという印象でした。結局は、普通のことをちゃんとやる。プログラミングなら技術書を読む。コードを書く。何か作ってみる。そこに、価値観の幅を広げ育てていき、興味が変わったら方向を変えてもいいし、辞めてもいいし、それからまた戻ってきてもいい。小さいことを積み重ねていくことが大事なんだと感じました。しかし、話しを聞いてる分には簡単そうに聞こえはしましたが、実際のところは人の何倍も努力し試行錯誤し、またチャレンジし続けているんだろうなと思いました。多くのさまざまな経験の中から語られているだろうその言葉は何だか重く厳しさも感じました。

コミュニティ

コミュニティに関する考え、関わり合いについてもお伺いできました。 フィヨルドブートキャンプでは、Discordをメインにコミュニティが築かれているのですが、量、質ともにそこで得られるものがとても大きいことを改めて感じました。卒業生の方とお話しする機会もありますが、どんどん良くなっているフィヨルドブートキャンプの話しを聞く度に、良いタイミングで関われたことはとても幸運です。

そんなお話しを聞きながら、コミュニティについては、以下、高橋征義さんの記事を思い出したりしました。 これからも、フィヨルドブートキャンプに限らず、コミュニティへ積極的に関わっていきたいと思いました。

magazine.rubyist.net

やりたくねえことやってる暇はねえ

トークイベント後、価値観について考えていたときマーシ―の言葉を思い出しました。

ザ・クロマニヨンズ真島昌利が、当時ザ・ブルーハーツというバンドで「やりたくねえことやってる暇はねえ」と歌っている曲があります。10代の頃初めて聴いたとき「うぉ!カッコいい!」となりましたが、だいぶ大人になった今でも、僕の価値観のベースが変わらないでいることが少し嬉しかったりしました。

しかし、それにしても人生は短い。

THE BLUE HEARTS ブルースをけとばせ 歌詞 - 歌ネット

何かをやるためには、ついでにやらなければいけないことがある

しかし、やりたくねぇからやらない。ではダメなこともわかっています。 社会に出ていろいろと経験していくなかで、「何かをやるためには、ついでにやらなければいけないこと」があることも学んできました。大きなことで言えば日本に住みたいのであれば、税金を収める必要がありますし、法律というルールに従う必要があります。会社でやりたいことがあれば、上司からの了承を得るためにやならければいけないこともあるかもしれません。何かやりたいことがあるのなら、それに付随するやらなければいけないことにも、しっかりと向き合いクリアしていく必要があります。

これまた大好きなザ・クロマニヨンズ甲本ヒロトが上記のことをインタビューで答えているのを見つけたので紹介しておきます。

hazamamakoto.blogspot.com

自分のやりたいことを本気でやっている人はカッコいい

何だか話しが脱線しましたが…

最後に。

価値観が変わり続ける世の中で、自分の価値観を軸にブレずに自分のやりたいことを本気でやっている人はカッコいいと思います。

能力(向き不向きとか)動機(生活のため、お金のためとか)そんなのカンケイない。自分の価値観を信じてやりたいことやって生きていく。そうできると信じていた10代の頃。しかし、失敗や挫折を繰り返し、厳しい現実に直面すると、そんな甘いこと言ってられない、ちゃんとしなきゃと、他人と比較し落ち込んだり、歳を重ねるにつれて価値観がブレブレになる自分がいました。 そんなこんなで葛藤しながらも、ようやく今は自分の価値感を多少は取り戻し、やりたいことをやれてはいますが、そこで何も結果が残せていない以上、これから先の将来に不安が多いのも事実です。

今回のトークは、そんな心情ともマッチしとても胸に響きました。

また最近、目の前の目標(卒業して就職すること)ばかりに執着し、冒頭のキャリアキーノートのように自分の価値観を俯瞰して見ることが少なかったのですが、今回のイベントを通して、自分の価値観について考えるとてもよい機会にもなりました。

長々と思うことを好き勝手に書いてしまいましたが、今回このようなトークイベントに参加できたことを嬉しく思います。

あんちぽさん、また関係者の皆さま、ありがとうございました!!

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第13週目まとめ

第8章8.1.1〜第8章8.10まで

期間:2021年08月16日〜2021年08月20日

モジュールの概要

クラスは、データとメソッドを持ったオブジェクトを扱う機能ですが、モジュールは「処理の部分だけ」をまとめた機能になります。

以下の特徴があります。

  • モジュールは、クラスのようにインスタンスを持つことはできません。
  • 他のモジュールやクラスを継承することはできません。

モジュールの使い方(Mix-in)

モジュールをクラスに混ぜ合わせて使えるようにすることをMix-inといいます。

以下、Mix-inの手法を説明します。

include

モジュールをクラスに取り組むことをincludeといいます。クラスに取り込むことで、モジュールで定義したメソッドがインスタンスメソッドとして呼び出せるようになります。

以下の例では、モジュールでEffectorを作成し、Guitarクラスにincludeしてみました。Guitar.newでインスタンスを生成し、メソッドを呼び出すことができます。Guitarをplayしchalkさせるのですが、引数にジミヘンを渡すことで、ジミヘンにしか出せない強烈なファズサウンドを出力することができます。

module Effector
    def chalk(guitarist)
        puts "#{guitarist}: ギュイーーン!!"
    end
end

class Guitar
    include Effector
    
    def play
        chalk('ジミヘンドリックス')
    end
end

# インスタンスメソッドとして呼び出す。
sound = Guitar.new
sound.play
ジミヘンドリックス: ギュイーーン!!  

extend

モジュールをクラスにMix-inするもうひとつの方法です。モジュール内のメソッドをそのクラスのクラスメソッドにすることができます。

今度は、GuitarクラスへEffectorモジュールをextendすることで、クラスメソッドとして呼び出すことが可能になります。

module Effector
    def chalk(guitarist)
        puts "#{guitarist}: ギュイーーン!!"
    end
end

class Guitar
    extend Effector

    def self.play
        chalk('ジミヘンドリックス')
    end
end

# クラスメソッド経由で呼び出す。
Guitar.play
ジミヘンドリックス: ギュイーーン!!

# Guitarクラスのクラスメソッドとして呼び出す。
Guitar.chalk('ジミヘン')
ジミヘン: ギュイーーン!!

Enumerableモジュール

docs.ruby-lang.org

このモジュールのメソッドは全て each を用いて定義されているので、インクルードするクラスには each が定義されていなければなりません。

上記は、るりまに記載されているの内容ですが、最初読んだとき「全てeachを用いて定義されている」の意味がわかりませんでした。輪読会内で質問をしてみたところ無事に理解できましたが、私と同じく疑問に思われている方の参考になればと思い私の解釈を書いておきます。

Enumerableモジュールに定義されたメソッドは、全て内部的にeachが動いています。Enumerableモジュールは、eachのみで書くことができる繰り返し処理を、より簡潔に書くために提供されている拡張機能のようなものです。

例えば、eachメソッドをundefすると、ブロックを渡したときに内部的に動くはずのeachが定義されていないことになるので動きません。逆に、自前のクラスにeachメソッドを定義すれば、Enumerableモジュールをincludeして50を超える便利なメソッドが手に入ることになります。

# Enumerableモジュールであるmapが使えなくなる。

class Hash
    undef each
end
=> nil

{a:1, b:2}.map
=> #<Enumerator: ...>

{a:1, b:2}.map{|k,v|[k,v*10]}
(irb):5:in `map': undefined method `each' for {:a=>1, :b=>2}:Hash (NoMethodError)

また、each以外のメソッドにもEnumerableの機能を提供するために、ラッパークラスというものがあります。Enumerator を介することで、Enumerableの機能を利用することができます。

docs.ruby-lang.org

以前のブログでも取り上げましたのでリンクを貼っておきます。

チェリー本輪読会 第5週目まとめ - D IT Y

Comparableモジュールと <=>演算子(UFO演算子

docs.ruby-lang.org

比較演算を可能にする(値の大小を識別できるようにする)モジュールです。

Comparableモジュールのメソッドを使うための条件は、このモジュールをインクルードするクラスで、基本的な比較演算子である <=> 演算子を定義している必要があります。

<=> 演算子はその形状から「UFO演算子」とも呼ばれています。

2 <=> 1
#=> 1
# a > b として、aが大きい場合正の整数となる。返り値は1となる。

2 <=> 2
#=> 0
# 等しい数値の場合は、0を返す。

1 <=> 2
#=> -1
# a < b 1が小さい場合、負の整数を返す。

2 <=> 'abc'
#=> nill
# 比較ができない場合は、nill

これは、「a <=> bは、a>bなら1を、a==bなら0を、a<bなら-1を返すようにしよう。そしたら便利なはず!sortとかで使いやすいはず!」ということが目的で使われます。

もしUFO演算子がなかったら、以下のように書かなくてはいけなくなり大変です。UFO演算子は、簡潔に書くために用意された演算子になります。

p ary.sort do |a, b|
  if a.to_i > b.to_i 
    1
  elsif a.to_i == b.to_i
    0
  elsif a.to_i < b.to_i
    -1
  else
    nil
  end
end

また、文字列の大小比較は、バイト配列の大小を元に比較しています。

''.bytes     #=> [227, 129, 130]
'あいう'.bytes #=> [227, 129, 130, 227, 129, 132, 227, 129, 134]

'あいう' <=> ''
=> 1
'' <=> 'あいう'
=> -1

文字列の大小比較については、伊藤さんのこちらの記事が参考になります。

qiita.com

名前空間

大規模なプログラムや外部に公開するgemを作ったりするときに、クラス名の重複が問題になることがあります。そのとき役に立つのが、名前空間(ネームスペース)としてのモジュールです。モジュール構文の中にクラス定義を書くと「そのモジュールに属するクラス」という意味になります。これで、同名のクラスがあったりしても外側のモジュール名さえ異なっていれば、名前の衝突は発生しなくなります。

以下、私の好きなバンドをモジュールにしました。それからクラスは、これまた大好きな二人のJohnを定義してみました。モジュール名::クラス名とすることで、同名のクラス名の衝突を防ぐことができます。

module Thebeatles
    class John
        def initialize(second_name)
            @second_name = second_name
        end
    end
end

module Sexpistols
    class John
        def initialize(second_name)
            @second_name = second_name
        end
    end
end

Thebeatles::John.new('Lennono')
=> #<Thebeatles::John:0x00007fab2a139368 @second_name="Lennono">

Sexpistols::John.new('Lydon')
=> #<Sexpistols::John:0x00007fab2f034cd8 @second_name="Lydon">

モジュールに特異メソッドを定義する

クラスへMix-inせずにモジュール自身に特異メソッドを定義すれば、モジュール名.クラス名という形でそのメソッドを呼び出すことができます。これで、クラスメソッドのような使い方ができます。

モジュールはインスタンス化できない点がクラスと異なります。インスタンスを作ってなにか操作する必要がないものであれば、モジュールにしておいたほうが他の開発者に変な勘違いをさせる心配がありません。

以下、self.chalkとすることで、特異メソッドとしてメソッドを定義しています。特異メソッドは、特定のオブジェクトに紐づくメソッドのことです。チェリー本輪読会 第12週目まとめ - D IT Y

module Guitar
    def self.play(guitarist)
        puts "#{guitarist}: ギュイーーン!!"
    end
end

Guitar.play('ジミヘン')
ジミヘン: ギュイーーン!!

class << self を使って定義することもできます。

module Guitar
    class << self    
        def play(guitarist)
            puts "#{guitarist}: ギュイーーン!!"
        end
    end
end

Guitar.play('ジミヘン')
ジミヘン: ギュイーーン!!

Sinatraメモアプリ

チェリー本とは関係ないのですが、上記のRubyのコードでセックスピストルズを取り上げたこともあり、以前に開発したメモアプリのことを思い出しました。

Image from Gyazo

github.com

歴史に残る大名盤へオマージュを捧げ作成しましたw

open.spotify.com

今思えばそんなに難しくはないプログラムなのですが、当時はホントわからないことだらけで大変でした…。しかし、そう思えるのもチェリー本を学ぶことで、Ruby力が付いたことが大きいです。

参考書籍

🍒 まとめ

久しぶりのブログ更新になります。

ブログとタイムラグがありますが、実は輪読会は9月17日にフィナーレを迎えました!最終日の詳細は後日ブログに書きたいと思います。

今回が13週目になりますので、こちらのブログも残り4週となります。復習も兼ね、完走まで頑張りたいと思います!

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

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第12週目まとめ

第7章7.10.1〜第7章7.11まで

期間:2021年08月09日〜2021年08月13日

第8章にも少し入りましたが、こちらは次週へまとめます。

エイリアスメソッドの定義

alias 新しい名前 元の名前

エイリアスメソッドを定義する場合は、事前に元のメソッドを定義しておく必要があります。

class Numbergirl
    def introduce
        "福岡市博多区から参りましたナンバーガールです”
  end
alias greeting introduce
end

numbergirl = Numbergirl.new

numbergirl.introduce
=> "福岡市博多区から参りましたナンバーガールです"
numbergirl.greeting
=> "福岡市博多区から参りましたナンバーガールです"

クラス/メソッドの定義 (Ruby 3.0.0 リファレンスマニュアル)

メソッドの削除

undef 削除するメソッドの名前

メソッドの定義をあとから削除することができます。

先程の、Numbergirlクラスを継承し、その後定義しておいたintroduceメソッドを削除してみます。

class Zazenboys < Numbergirl
    undef introduce
end

zazenboys = Zazenboys.new
zazenboys.intoroduce
: undefined method `intoroduce' for #<Zazenboys:0x00007fa552839290> (NoMethodError)

クラス/メソッドの定義 (Ruby 3.0.0 リファレンスマニュアル)

ネスト(入れ子)したクラスの定義

class 外側のクラス
    class 内側のクラス
    end
end

クラスの内部に定義したクラスは、::を使って参照できます。

外側のクラス::内側のクラス 

この形を、名前空間(ネームスペース)と呼びます。(通常はクラスよりモジュールが使われることが多いです。)

class Numbergirl
    class Zazenboys
        attr_reader :menber
        
        def initialize(member)
            @member = member
        end
    end
end

leader = Numbergirl::Zazenboys.new('向井秀徳')
leader
=> #<Numbergirl::Zazenboys:0x00007fa556873d88 @member="向井秀徳">

等値を判断するメソッドや演算子4つ

equal?

英語の意味そのままなので分かりやすいです。等しければtrueを返します。

object_idを比較しているので、代入している内容が文字列だとfalseになります。object_idが違うためです。

numbergirl = 'Band'
zazenboys = 'Band'
hakata = numbergirl 

numbergirl.equal?(zazenboys)
=> false
numbergirl.equal?(hakata)
=> true

==

オブジェクトの内容が等しいか判断します。

100 == 100.000
=> true

'a' == 'a'
=> true

'a' == 'A'
=> false

'a' == :a
=> false

foo = 'hoge'
=> "hoge"

bar = 'h'+ 'o' + 'g' + 'e'
=> "hoge"

foo == bar
=> true

eql?

== とは別に、値が同じどうかを判定することができます。

== との違いは、ハッシュ値が等しいかどうかで判断する点です。

hashメソッドで、ハッシュ値を確認し同じであれば、trueになります。

numbergirl = 'Band'
zazenboys = 'Band'

numbergirl.hash
=> 1476835131188092417
zazenboys.hash
=> 1476835131188092417
numbergirl.eql?(zazenboys)
=> true

Hash#hash (Ruby 3.0 リファレンスマニュアル)

===

左辺が単独のStringやIntegerオブジェクトの場合は、==と同じように判断されます。

左辺がClassオブジェクトの場合は、右辺がそのクラスのインスタンスであるかどうかを判断します。

左辺が正規表現の場合は、=~と同じようにマッチしたかどうかを判断します。=~ を使って正規表現を比較する

100 === 100.00
=> true
'a' === 'A'
=> false
'a' === 'a'
=> true

/\w{6}-\d{3}/ === 'MONGOL-800'
=> true

また、case文のwhen節の内部で行われている処理ですが、===で判断されています。

# case文の公式
case 比較したいオブジェクト
when A
  # 処理
when B
  # 処理
else 
    # 処理
end

# 内部で行われている処理
if A === 比較したいオブジェクト
  # 処理
elsif B === 比較したいオブジェクト
  # 処理
else
  # 処理
end
case 'foo'
    when 'foo'
        puts 'true'
    when /w{3}/
        puts 'true'
    else 
        puts 'false'
 end

# true
=> nil

case 100
    when 'foo'
        puts 'true'
    when /w{3}/
        puts 'true'
    else 
        puts 'false'
 end

# false
=> nil

オープンクラス

Rubyはクラスの継承に制限がありません。String や Integer といった基本的なクラス(組み込みクラス)も自由に改変できます。定義済みのクラスへ、メソッドを追加することも上書きすることも可能です。Rubyのクラスは変更に対してオープンなので、「オープンクラス」と呼ばれています。

以下、Arrayクラスを継承させた独自のクラスになります。

class Numbergirl < Array
end

band = Numbergirl.new()
=> []
band << '向井秀徳'
=> ["向井秀徳"]
band << '田渕ひさ子'
=> ["向井秀徳", "田渕ひさ子"]
band
=> ["向井秀徳", "田渕ひさ子"]
band.class
=> Numbergirl

# 継承しないとエラーになる。
class Numbergirl
end

band = Numbergirl.new()
=> #<Numbergirl:0x00007f84d916b420>
band << '向井秀徳'
: undefined method `<<' for #<Numbergirl:0x00007f84d916b420> (NoMethodError)

モンキーパッチ

組み込みクラスやメソッドを追加したり変更したりします。このように自分が期待する挙動へ変更することを、モンキーパッチといいます。

class Numbergirl
    def initialize(name)
            @name = name
    end

    def introduce
        "#{@name}です"
    end
end

band = Numbergirl.new('ナンバーガール')
band.introduce
=> "ナンバーガールです"

# モンキーパッチを当てて、希望する挙動へ変更する。
class Numbergirl
    def introduce
        "福岡市博多区から参りました#{@name}です"
    end
end

band.introduce
=> "福岡市博多区から参りましたナンバーガールです"

原則として、オープンクラス、モンキーパッチには頼らない

オープンクラス、モンキーパッチは非常に強力です。誤って使用してしまうとプログラム全体に不具合を生じさせてしまう原因となってしまいます。

原則として、まずはオープンクラス、モンキーパッチに頼らずにプログラムが書けないか考える必要があります。

大いなる力には、大いなる責任が伴うことを忘れてはいけない。 Matz

特異メソッド (singleton メソッド)

Rubyはすべてがオブジェクトなので、StringクラスやArrayクラスもオブジェクトです。そして、それらのクラスは、Classクラスインスタンスになります。

class String
end

String.class
=> Class

class Foo
end

Foo.class
=> Class

特異メソッドとは、特定のオブジェクトに紐づくメソッドのことです。 オブジェクト固有のメソッドのことで、あるオブジェクトにはあるメソッドが存在しますが、同じクラスの別のオブジェクトには存在しません。

特異メソッドはdef オブジェクト名.メソッド名で定義できます。

class Instance
  def i_method
    puts 'インスタンスメソッドです。'
  end
end

foo = Instance.new
bar = Instance.new

# 特異メソッドを定義します。
def foo.s_method
  puts '特異メソッドです。'
end

foo.i_method
=> インスタンスメソッドです。

bar.i_method
=> インスタンスメソッドです。

# オブジェクトがfooで、特異メソッドとしてs_methodを定義しているので呼び出せる。
# s_methodは、fooのみが持つ特異メソッドとして定義されている。
foo.s_method
=> 特異メソッドです。

# オブジェクトがbarと異なるため呼び出せない。(barオブジェクトにはs_methodは定義していない)
bar.s_method 
: undefined method `s_method' for #<Instance:0x00007f8474a980d0> (NoMethodError)

るりまを参照する際には、クラスメソッドではなく、特異メソッドと書かれているので置き換えて読みます。

Ruby におけるクラスメソッドというのは Class クラスのインスタンスの特異メソッドを便宜上クラスメソッドと呼んでいて、実態は特異メソッドの一種です。

ダックタイピング

もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである

という言葉に由来するプログラミング用語です。

オブジェクトのクラスが何であろうと、そのメソッドが呼び出せればOKとするプログラミングスタイルのことです。オブジェクトを特徴づけるのは、実際のクラスや継承関係ではなく、そのオブジェクトがどのように振る舞うか?(どんなメソッドを持っているか?)です。

class Member
    def play
        puts 'メンバーは楽器を演奏する'
 end
end

class Guitar < Member
    def play
        puts 'ギターを演奏する'
    end
end

class Bass < Member
    def play
        puts 'ベースを演奏する'
    end
end

member = Guitar.new
member.play
# => ギターを演奏する

member = Bass.new
member.play
# => ベースを演奏する

同じ名前のメソッドが複数のオブジェクトに属することをオブジェクト指向の用語で、「ポリモーフィズム」といいます。playメソッドの実行結果はそれぞれ違います。(putsの中身が違う)

ポリモーフィズム

  • 共通メインルーチンを作るための仕組み。
  • 呼び出す側のロジックを一本化する仕組み。
  • 呼び出される側の引数や戻り値の形式を統一する必要がある。

botが導入されました

今週、楽しみにしていたbotが輪読会へ導入されました〜!

botとは?

e-words.jp

choosing-someone-discord-bot(Discordのボイスチャンネルに参加しているメンバーをランダムに指名するbot

Discordの音声チャンネルで開催されている輪読会ですが、毎回、司会やドライバーなどを決める作業がありました。その都度irbを開きsampleメソッドをまわして決めていたのですが、多少の手間と煩わしさがありました。そこで、この作業をbotで代替する目的で、トミーさんが開発されていたのが今回紹介するbotになります。今までの手間がなくなり、かつエンタメ性もプラスされ、楽しく、便利になりました!

botの詳細は、トミーさん(id:eatplaynap329)のブログ、GitHubページに詳しく書かれています。

eatplaynap329.hatenablog.jp

github.com

mitaka.rbへ初参加!

輪読会メンバーの紹介で初めてmitaka.rbへ参加しました。

イベント管理アプリをモブプロで作る会でした。

Webアプリケーションの制作過程を、モブプロ形式で見ることができました。テスト駆動開発(TDD)の流れをベースに、どのように思考しコードを書いてくのか?熟練者のプログラマの手元をLIVEで見ることができ勉強になりました。Vimを使っている画面も初めて見ることができました。他にも、esaを初めて使ってみたり、いろいろ経験できよかったです。

そしてなにより、主催の@tatsuoSakurai さんや、@okuramasafumi さんがとても優しく親切で、とても雰囲気の良いコミュニティでした。ぜひまた参加したいと思います!

mitakarb.doorkeeper.jp

参考書籍

🍒 まとめ

チェリー本、最初の難関であるクラスの章が完了しました!

いや〜、大変でした。しかしその分、同時に参加した『オブジェクト指向でなぜつくるのか』輪読会ともあわせて、クラス、オブジェクト指向の基礎について学ぶことができた章でした。 今までは、「クラス怖い」「継承?クラスメソッド?」と、自信の持てなかった内容ですが、輪読会を通して理解することができ大きな自信となりました。次回は、モジュールに入ります!

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

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第11週目まとめ

第7章7.6.1〜第7章7.9.3まで

期間:2021年08月02日〜2021年08月06日

クラスの継承

オブジェクト指向プログラミングの三大要素のひとつでもある「継承」を学びました。

以下、継承を図で表してみましたが、ひと言で説明すると「継承はクラスの特徴を共有すること」です。動物やゲームのキャラクターなど、いろいろな例があるとは思いますが、生き物ではなくモノでも当てはまるか考えてみました。

is-aの関係

クラスの基本として、スーパークラス、サブクラスという関係があります。

親と子の関係でも表せます。親クラス、子クラス。

以下では、「弦楽器」というクラスの中に「ギター」というクラスが存在します。これを「is-aの関係」と呼び、「サブクラスはスーパークラスの一種である」(サブクラス is a スーパークラス)と当てはめた場合、違和感がないか?継承関係が正しいか確かめることができます。

# 文脈は正しい

ギター is a 弦楽器
ギターは弦楽器の一種である。

サブクラスのギターは、ベースでも、ウクレレでも継承関係は正しいので問題ないですが、弦楽器のスーパークラスに値する「楽器」となると、関係がおかしくなります。

# 文脈的におかしい

楽器 is a 弦楽器
楽器は弦楽器の一種である

クラスの図はサブクラスからスーパークラスへ向かって矢印↑を伸ばすことで、クラスの関係を表します。

f:id:shirotamaki:20210825182458p:plain

上記の話しをベースに、楽器クラスを題材にクラスの継承関係を考えてみました。

  • まず楽器は、弦楽器、管楽器、打楽器などありますが、弦楽器を選択してみました。
    • 弦楽器 is a 楽器
  • それから、弦楽器の中からギターを選び、エレキギターを選択していきます。
  • さらに、メーカーはギブソンを選択します(もちろん、フェンダーもグレッチも同じ関係を持つことができます)
  • 最後に、ギブソンのシリーズ名を選択します(ここでも、フライングV、SGなども同じ関係を持つことができます)

f:id:shirotamaki:20210825182341p:plain

スーパークラス(楽器)に向かうほど、汎化していき、サブクラス(ギブソン)に向かうほど特化していきます。スーパークラスからサブクラスへ、上から下(左から右)へ性質を受け継いでいきます。これを「継承」と呼びます。

サブクラスである「ギブソン」は、スーパクラスの「エレキギター」の特徴を全て継承しています。(鉄製の弦を装備している。ピックアップを内蔵している。弦の振動を電気信号に変換している。など)しかし、スーパークラスの関係性にない「フェンダー」の特徴は継承していません。(製作している工場が違う。ピックアップの種類が違う。音色が違う。など)

オブジェクトのクラスの確認方法

classメソッドを使います。

class Gibson
    puts 'I play guitar'
end

gibson = Gibson.new
=> #<Gibson:0x00007fdcdd045f08>
gibson.class
=> Gibson

instance_of?メソッドを使うと、引数にクラスを渡すことで、true、falseで確認できます。

gibson = Gibson.new
=> #<Gibson:0x00007fdcdd0d7688>
gibson.instance_of?(Gibson)
=> true
gibson.instance_of?(String)
=> false

is_a?メソッドを使うと、継承関係を確認できます。GibsonクラスのスーパークラスであるObjectクラスは、trueになります。

gibson = Gibson.new
=> #<Gibson:0x00007fdcd90d2df8>
gibson.is_a?(Gibson)
=> true
gibson.is_a?(Object)
=> true
gibson.is_a?(String)
=> false

ほかのクラスを継承したクラスを作る

class サブクラス < スーパークラス
end

Guitarクラス(スーパークラス)を作り、その後にElectric_guitarクラス(サブクラス)を作り継承させてみます。

class Guitar 
    attr_reader :brand, :price

    def initialize(brand, price)
        @brand = brand
        @price = price
    end
end

class Electric_guitar < Guitar
    attr_reader :model

    def initialize(brand, price, model)

# @brand、@priceの箇所をsuperを使って呼び出すことができる。
# superを使うと、スーパークラスの同名メソッドを呼び出すことができる。

        super(brand, price)       
        @model = model
    end
end

electric_guitar = Electric_guitar.new('Gibson', 1000000, 'LesPau
l')
=>
#<Electric_guitar:0x00007fc73d1613c0
...
electric_guitar.brand
=> "Gibson"
electric_guitar.price
=> 1000000
electric_guitar.model
=> "LesPaul"

しかし、そもそもスーパークラスとサブクラスで実行する処理が変わらなければ、サブクラスで同名メソッドを定義したり、superで呼んだりする必要はありません。

class Guitar 
    attr_reader :brand, :price

    def initialize(brand, price)
        @brand = brand
        @price = price
    end
end

class Electric_guitar < Guitar
    # スーパークラスに処理を任せる。
end

electric_guitar = Electric_guitar.new('Gibson', 1000000)
=> #<Electric_guitar:0x00007f914618c318 @brand="Gibson", @price=1000000>
electric_guitar.brand
=> "Gibson"
electric_guitar.price
=> 1000000

メソッドの公開レベル

publicメソッド

クラスの外部からでも自由に呼び出せるメソッドのことです。

インスタンスメソッドとして返るように公開することを指します。initializeメソッド以外のインスタンスメソッドは、デフォルトでpublicメソッドが指定されています。

class Guitar 
    def buy
        '私はギターを買いました' 
    end
end

guitar = Guitar.new
=> #<Guitar:0x00007fc82f1873c0>

# buyメソッドを外から呼び出している。
guitar.buy
=> "私はギターを買いました"

privateメソッド

クラスの外からは呼び出せず、クラスの内部でのみ使えるメソッドです。

クラス内でprivateキーワードを書くと、そこから下で定義されたメソッドはprivateになります。

class Guitar 
    private

    def buy
        '私はギターを買いました'
    end
end

guitar = Guitar.new
=> #<Guitar:0x00007fe8a81accf8>
guitar.buy
#=> : private method `buy' called for #<Guitar:0x00007fe8a81accf8> (NoMethodError)

レシーバを指定して呼び出すことができないメソッドになるので、以下のようにself.brandとした場合は、NoMethodErrorとなり呼び出せません。brandメソッドはprivateメソッドになるためです。

class Guitar
  def buy
    "私は、#{self.brand}ギターを買いました"
  end

  private

  def brand
    'Gibson'
  end
end

guitar = Guitar.new
guitar.buy
#=> : private method `brand' called for #<Guitar:0x00007fb3221126f0> (NoMethodError)

protectedメソッド

メソッドを定義したクラス自身とそのサブクラスからは、インスタンスメソッドをレシーバ付きで呼び出すことができます。それ以外の場所からは呼び出せません。

class Guitar
# 一旦publicメソッドとして定義する。
    attr_reader :brand, :price

# priceのみ、protectedメソッドへ変更する。
    protected :price 

    def initialize(brand, price)
        @brand = brand
        @price = price
    end

    def which_is_expensive?(other_brand)
        other_brand.price < @price
    end
end

gibson = Guitar.new('Gibson', '100万円')
=> #<Guitar:0x00007f8fedb68b20 @brand="Gibson", @price="100万円">
fender = Guitar.new('Fender', '80万円')
=> #<Guitar:0x00007f8fedaa8758 @brand="Fender", @price="80万円">

gibson.which_is_expensive?(fender)
=> false
fender.which_is_expensive?(gibson)
=> true

gibson.brand
=> "Gibson"

# クラス外からは、priceは呼び出せない。protectedメソッドのため。
gibson.price
`<main>': protected method `price' called for #<Guitar:0x00007f8fedb68b20 @brand="Gibson", @price="100万円"> (NoMethodError)

定数の再代入を防ぐ

Rubyの定数は再代入が可能です。warningと警告は出ますが、再代入ができてしまいます。

FOO = 'bar'
=> "bar"
FOO = 'hoge'
warning: already initialized constant FOO
warning: previous definition of FOO was here
=> "hoge"

誤って変更されないためには、freezeが必要になります。

しかし、単に定数へfreezeしただけでは、値の変更は防ぐことはできますが、再代入は可能なままです。

# 値の変更は防ぐことはできる。
FOO = 'bar'
=> "bar"
FOO.freeze
=> "bar"
FOO.upcase!
(irb):10:in `upcase!': can't modify frozen String: "bar" (FrozenError)

# しかし、再代入は防ぐことができない。
FOO = 'hoge'
(irb):11: warning: already initialized constant FOO
(irb):1: warning: previous definition of FOO was here
=> "hoge"

再代入を防ぐには、クラスを準備する必要があります。

クラス外部から定数を参照するには、クラス名::定数名 とします。

# クラスを凍結して再代入を阻止する。
class Hoge
    FOO = 'bar'
end

Hoge.freeze
Hoge::FOO = 'piyo'
`<main>': can't modify frozen #<Class:Hoge>: Hoge (FrozenError)

# クラス内でfreezeを呼び再代入を阻止する。
class Hoge
   FOO = 'bar'
  
  freeze
   FOO = 'piyo'
end

`<class:Hoge>': can't modify frozen #<Class:Hoge>: Hoge (FrozenError)

freezeを適用すれば、上記の例のように再代入を防止できますが、通常はこのようにfreezeを呼ぶことはないようです。

マジックコメント # frozen_string_literal: true

# frozen_string_literal: trueというマジックコメントをファイルの先頭に書いておくと、文字列が最初からfreezeされます。freezeされるのは文字列リテラル( ' , " で作成する String オブジェクト) のみであることに注意が必要です。

以下、定数での例ですが、ここでは定数・変数で挙動に違いはありません。

# frozen_string_literal: true

FOO = 'hoge'
FOO.upcase!

`upcase!': can't modify frozen String: "hoge" (FrozenError)

また、文字列リテラルをデフォルトで イミュータブル にする変更は、Ruby 3.0 で導入予定だったようですが、今のところ取りやめになっているようです。

I officially abandon making frozen-string-literals default (for Ruby3).

和訳:Ruby3.0から、frozen-string-literalsをデフォルトにすることは公式に取りやめます。

Matz.

Feature #11473: Immutable String literal in Ruby 3 - Ruby master - Ruby Issue Tracking System

他、こちらの伊藤さんの記事も参考にさせていただきました。

文字列をfreezeさせるいくつかの方法 - Qiita

輪読会「オブジェクト指向でなぜつくるのか第3版」へ参加

www.nikkeibp.co.jp

第2回目の輪読会へ参加しました。オブジェクト指向の三大要素(クラス、ポリモーフィズム、継承)を中心に学びました。

そこで、オブジェクト指向設計においての大事な原則を教えてもらいましたので紹介したいと思います。

Tell, Don't Ask(求めるな、命じよ)

llcc.hatenablog.com

クラスの役割についての原則です。

理想的なオブジェクト指向設計において、クラスを呼び出す側はAsk(求めること)はせずに、Tell(命令)だけするべきという原則です。オブジェクトに対してあれこれと聞いて手元でロジックを組み立てること(Ask)は良い設計とされていません。オブジェクトに対してロジックの結果を聞け(Tell)ということです。

悪い例 Ask 版

ここでは呼び出す側は求めています。

もし、macのosが:mojaveだったら、osを:catalineにして変更してね。と求めています。

class Computer
  attr_accessor :type, :cpu, :memory, :hard_disk, :os

  def initialize(type, cpu, memory, hard_disk, os)
    @type = type
    @cpu = cpu
    @memory = memory
    @hard_disk = hard_disk
    @os = os
  end
end

mac = Computer.new(:mac, :intel, 32, 124, :mojave)
mac.os
=> :mojave

# もし、macのosが:mojave だったら、osを:catalineにしてね。と求めている。
mac.os = :catalina if mac.os == :mojave
mac.os
=> :catalina

良い例 Tell版

ここでは呼び出す側は命令しています。

mojave?メソッドを呼びます。呼ばれたmojave?メソッドは、@os == :mojaveを調べます。その後、mojaveだったら、trueなので、次は、uppgrade!メソッドが呼ばれます。:catalineが代入されます。

class Computer
  attr_accessor :type, :cpu, :memory, :hard_disk, :os

  def initialize(type, cpu, memory, hard_disk, os)
    @type = type
    @cpu = cpu
    @memory = memory
    @hard_disk = hard_disk
    @os = os
  end

  def upgrade!
    @os = :catalina
  end

  def mojave?
    @os == :mojave
  end
end

mac = Computer.new(:mac, :intel, 32, 124, :mojave)
mac.upgrade! if mac.mojave?
=> :catalina

参考書籍

🍒 まとめ

クラスの後半戦に入り、徐々に内容が難しくなってきました。

しかし、同時に参加している『オブジェクト指向でなぜつくるのか』輪読会で学んだことも理解の後押しとなり、オブジェクト指向の需要な要素である「クラス」について、理解ができるようになってきました。

クラスは「まとめて、隠して、たくさん作る」仕組みです。プログラムの無駄を省いて整理整頓するために、このクラスの仕組みが必要不可欠となります。まだまだとっちらかっている自分で書いたプログラム。今後はクラスを用いて整理整頓できるように、試行錯誤していきたいと思います。

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

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第10週目まとめ

第7章7.1.1〜第7章7.5.3まで

期間:2021年07月26日〜2021年07月30日

クラスを使う場合と使わない場合の比較

オブジェクト指向でなぜつくるのかに書かれていたことですが、クラスを用いる理由にも該当するため紹介したいと思います。

オブジェクト指向はソフトウエアの保守や再利用をしやすくすることを重視する技術です。個々の部品により強く着目し、部品の独立性を高め、それらを組み上げてシステム全体の機能を実現することを基本にします。部品の独立性を高めることで、修正が起きた場合の影響範囲を最小限にし、他のシステムで容易に再利用できるようにします。 出典:オブジェクト指向でなぜつくるのか 第3版 22頁

上記のオブジェクト指向を元に、まずはクラスを用いることで堅牢なプログラムの作成の基本を学びます。プログラムが大規模になるほど、データとメソッドを一緒に持ち運べるクラスのメリットは大きくなります。

クラスを使わないプログラムの例

  1. 配列を準備し、欲しいコンピュータのハッシュデータを入れます。
  2. その後、配列にしたcomputers変数のデータをeachで取り出します。
computers = []
computers << {type: 'mac', cpu: 'M1', memory: 32, storage: 'SSD256' }
computers << {type: 'win', cpu: 'intel', memory: 16, storage: 'HDD50' }

computers.each do |c|
    puts "タイプ: #{c[:type]}、CPU: #{c[:cpu]}、メモリ: #{c[:memory]}、ストレージ: #{c[:storage]}"
end

=> タイプ: mac、CPU: M1、メモリ: 32、ストレージ: SSD256
=> タイプ: win、CPU: intel、メモリ: 16、ストレージ: HDD50

クラスを使ったプログラムの例

  1. Computerクラスを準備します。
  2. コンピュータのスペック表示用でメソッドを定義します。

attr_reader initialize は後ほど取り上げます。

class Computer
    attr_reader :type, :cpu, :memory, :storage

    def initialize(type, cpu, memory, storage)
       @type = type
       @cpu = cpu
       @memory = memory
       @storage = storage
    end

    def display_pc_spec
        puts "タイプ: #{@type}、CPU: #{@cpu}、メモリ: #{@memory}、ストレージ: #{@storage}"
  end
end
  1. Computer.newで、インスタンスを生成します。その際、引数に欲しいコンピュータのデータを渡しておきます。
  2. 定義したメソッドを呼び出します。
mac = Computer.new(:mac, :M1, 32, :SSD256)
mac.display_pc_spec
#=> タイプ: mac、 CPU: M1、メモリ: 32、ストレージ: SSD256

win = Computer.new(:win, :intel, 16, :HDD500)
win.display_pc_spec
=> タイプ: win、 CPU: intel、メモリ: 16、ストレージ: HDD500

クラスを使わない場合、使った場合ともに、結果は同じになります。

オブジェクト指向を完全に表現できているわけではないと思いますが、2つの例から、後者がよりオブジェクト指向寄りのプログラムになります。

クラスを定義することで、コンピュータのスペック内容を(属性)を保持することのできる、設計図が出来上がります。class Computer

それから、動作や振る舞いを表すメソッドを定義します。def display_pc_spec 今回は、コンピュータのスペック情報を表示できる機能を持たせました。

ここまで出来たら、後はクラスに命を吹き込みます。インスタンスを作ります。Computer.new引数に欲しいコンピュータの情報を渡してあげることで、希望するインスタンスを生成することができます。

最後に、メソッドを呼び出しますmac.display_pc_spec。レシーバ(先ほど生成したインスタンス)に対して、メソッドを実行することができます。mac変数(レシーバ)に対して、「スペックを表示して」という指示を出しています。

オブジェクト指向プログラミング関連の用語

クラス、インスタンス

上記で取り上げたクラスのことですが、クラスは「オブジェクトの設計図」と表現されることが多いです。

クラスはインスタンス(オブジェクト)と対になる要素です。上記の例では、コンピュータ(クラス)、Macインスタンス)という例で書いてみましたが、コンピュータクラスを使って、いろいろなインスタンスを生成することができます。Winのコンピュータもあれば、Macの超ハイスペックモデルもあるかもしれません。予め容易した属性に、好きなデータを入れ込んで作ることができます。

Compute.new(:好きなOS, :好きなCPU, 希望するメモリ, :希望するストレージ)

クラスは、英語でclass「分類、種類」「同種のものの集まり」を意味します。インスタンスは、英語でinstance「具体的なモノ、実例」を意味します。クラスという種類を元に、そこから具体的なモノを生成するしくみは、プログラムをより堅牢で再利用しやすくすることに繋がります。

オブジェクト、インスタンス、レシーバ

3つとも同じ意味です。

文脈によって使い分けられているようです。

インスタンスという言葉はオブジェクトとほとんど同じ意味で使われています。一方、あるオブジェクトが、あるクラスに属していることを強調する場合には、「インスタンス」のほうがよく使われます。 出典:たのしいRuby 79頁


レシーバは英語で書くと"receiver"で、「受け取る人」や「受信者」という意味です。なので、「レシーバ」は「メソッドを呼び出された側」というニュアンスを出したいときによく使われます。 出典:チェリー本 210頁

メソッド、メッセージ

オブジェクトが持つ「動作、振る舞い」をメソッドと表現します。

ここでは、メソッド名をdisplay_pc_specと定義し、Computerクラスから、インスタンスを生成するときに、コンピュータのスペックを表示する振る舞いを持つメソッドを書きました。

def display_pc_spec
    puts "タイプ: #{@type}、CPU: #{@cpu}、メモリ: #{@memory}、ストレージ: #{@storage}"
end

メッセージは、レシーバと組み合わせて使われます。

変数のmacをレシーバとし、display_pc_specをメッセージとして呼んでいます。「コンピュータのスペックを表示しろ」と、レシーバに対してメッセージを送っているイメージです。

mac = Computer.new(:mac, :M1, 32, :SSD256)
mac.display_pc_spec

状態(ステート)

オブジェクトごとに保持される状態のことを指します。ここでは、引数で渡されたデータによってインスタンスが生成されます。生成されたインスタンスは、「macタイプで、M1のCPUを搭載した、メモリ32でストレージSSD256のコンピュータ」になります。このコンピュータの持つデータが、オブジェクト指向の考え方で言う状態(ステート)です。

mac = Computer.new(:mac, :M1, 32, :SSD256)
mac.display_pc_spec
=> タイプ: mac、 CPU: M1、メモリ: 32、ストレージ: SSD256

属性(アトリビュート、プロパティ)

attr_reader :type, :cpu, :memory, :storage として、オブジェクトに設定することができる値を属性と呼びます。

mac = Computer.new(:mac, :M1, 32, :SSD256) クラス.newをする際に引数を渡していますが、これにより属性:type, :cpu, :memory, :storageへ希望する値を指定してオブジェクトを生成することができます。

属性とは、オブジェクトに属していて、取得、設定が可能な値のことです。

class Computer
    attr_reader :type, :cpu, :memory, :storage

# 省略

mac = Computer.new(:mac, :M1, 32, :SSD256)
mac.display_pc_spec
=> タイプ: mac、 CPU: M1、メモリ: 32、ストレージ: SSD256

initializeメソッド

インスタンスを初期化するために使われます。クラス名.newを実行することにより、オブジェクトが生成されますが、その際、真っ先に実行されるメソッドです。

class Computer
    def initialize(type, cpu, memory, storage)
        puts "タイプ: #{type}、CPU: #{cpu}、メモリ: #{memory}、ストレージ: #{storage}"
    end
end

Computer.new(:mac, :M1, 32, :SSD256)
=> タイプ: mac、CPU: M1、メモリ: 32、ストレージ: SSD256

引数の数が合わないとき、エラーになります。

実引数へ4つ与えられるべきですが、3つですよ。足りないですよ!と、怒られています。

class Computer
    def initialize(type, cpu, memory, storage) # 仮引数
        puts "タイプ: #{type}、CPU: #{cpu}、メモリ: #{memory}、ストレージ: #{storage}"
    end
end

Computer.new(:mac, :M1, 32)  # 実引数
(irb):30:in `initialize': wrong number of arguments (given 3, expected 4) (ArgumentError)

アクセサメソッド

インスタンス変数の値を読み書きするメソッドのことです。

  • attr_reader 読み取り専用(ゲッターメソッド)
  • attr_writer 書き込み専用(セッターメソッド)
  • attr_accessor 読み書き両方に対応

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

ゲッターメソッド(読み取り専用部分)

attr_readerを使わない場合のプログラム

def computer
    @type
end

セッターメソッド(書き込み専用部分)

attr_writerを使わない場合のプログラム

computer=メソッドは、computerとしていますが、fooでもbarでも何でも使えます。このメソッドは、@typeを外部から変更するためのメソッドです。computer=(value) の仮引数としてvalueとしていますが、これもcomputerと同じく何でも大丈夫です。慣例的にvalueにすることが多いそうです。

Rubyは、=で終わるメソッドを定義すると、変数に代入するような形式でそのメソッドを呼び出すことができます。個人的にはこの項目はかなり躓きました。こちらは、『ゼロからわかるRuby超入門』の198頁の説明が分かりやすくてオススメなのでぜひご一読ください。

def computer=(value)
    @type = value
end

実際にプログラムに書き込んでみます。

読み取り、書き込み共に成功します。

class Computer
    def initialize(type)
        @type = type
    end

    def computer
        @type
    end

    def computer=(value)
        @type = value
    end
end

# インスタンスを生成する
mac = Computer.new(:mac)
#=> #<Computer:0x00007ffb288ff400 @type=:mac>

# 読み取り
mac.computer
=> :mac

# 書き込み
# 変数に代入しているように見えるが、メソッドのcomputer=(value)を呼び出している。
mac.computer = :マックブック
=> :マックブック

ゲッターメソッドだけ削除してみます。

読み取りはエラーが出ます。書き込みは成功します。

class Computer
    def initialize(type)
        @type = type
    end

    def computer=(value)
        @type = value
    end
end

win = Computer.new(:win)
=> #<Computer:0x00007fd9520ade48 @type=:win>

# 読み取りはエラーが出る。
win.computer
(irb):18:in `<main>': undefined method `computer' for #<Computer:0x00007fd9520ade48 @type=:ウィンドウズ> (NoMethodError)

# 書き込みは成功。
win.computer = :ウィンドウズ
=> :ウィンドウズ

attr_accessorメソッド(読み取り、書き込み)

attr_accessorメソッドを使ってプログラムを呼び出してみます。

このメソッドは、読み取り、書き込みの両方に対応しています。

class Computer
    attr_accessor :type

    def initialize(type)
        @type = type
    end
end

# インスタンスを生成する。
linux = Computer.new(:linux)
=> #<Computer:0x00007fd955a23ce0 @type=:linux>

# 読み取り
linux.type
=> :linux

# 書き込み
linux.type = :リナックス
=> :リナックス

attr_reader メソッド 読み取り専用(別名:ゲッターメソッド)

読み取り専用にしたい時は、こちらのメソッドでも対応できます。

class Computer
    attr_reader :type

    def initialize(type)
        @type = type
    end
end

# インスタンスを生成する。
linux = Computer.new(:linux)
=> #<Computer:0x00007fd955a23ce0 @type=:linux>

# 読み取り
linux.type
=> :linux

# 書き込みエラー
linux.type = :リナックス
(irb):10:in `<main>': undefined method `type=' for #<Computer:0x00007f8a9610b008 @type=:linux> (NoMethodError)

attr_writer メソッド 書き込み専用(別名:セッターメソッド)

書き込み専用にしたい時は、こちらのメソッドでも対応できます。

class Computer
    attr_writer :type

    def initialize(type)
        @type = type
    end
end

# インスタンスを生成する。
linux = Computer.new(:linux)
=> #<Computer:0x00007f8a9b2133b0 @type=:linux>

# 読み取りエラー
linux.type
(irb):9:in `<main>': undefined method `type' for #<Computer:0x00007fe0640e1108 @type=:linux> (NoMethodError)

# 書き込み
linux.type = :リナックス
=> :リナックス

クラスメソッドの定義

クラスメソッドとは、オブジェクトを作らずに呼び出せるメソッドのことです。レシーバがクラスになるので、クラスに対して呼び出せます。ひとつひとつの「インスタンスに含まれるデータは使わない」メソッドを定義したい場合もあるので、そのような場合はクラスメソッドを定義した方が良いです。

クラスメソッドを定義する方法は2つありますが、ここでは、よく使われるメソッドの前にselfを付ける手法を例に取り上げます。

class クラス名
    def self.クラスメソッド
         # クラスメソッドの処理
    end
end

プログラムの例が無理やり感ありますが、インスタンスを生成しなくてもメソッドを呼び出すことができています。

class Computer
    def initialize(type)
         @type = type
    end

    def self.upgrade(type)
        type.upcase
    end
end

Computer.upgrade(:big_sur)
=> :BIG_SUR

メソッド名の表記法について

メソッドの表記についてです。るりまや、他ドキュメント等では下記のように記載されています。

  • クラス名#メソッド名

#インスタンスメソッドであることを表しています。

  • クラス名.メソッド名 または、クラス名::メソッド名

. ::はクラスメソッドであることを表しています。

このマニュアルのヘルプ (Ruby 3.0.0 リファレンスマニュアル)

定数

一般に定数という言葉は、書き換えが不可能なことを表します。

定数 (プログラミング) - Wikipedia)

しかし、Rubyにおける定数は、書き換えが可能です。

Rubyの定数は「みんなわざわざ変更するなよ」と念押しした変数のようなものです。定数という言葉に惑わされないようにしてください。ミュータブル(変更可能)なオブジェクトの場合、定数の中身を変更できます。(String, Array, Hashなど)

定数を使用する理由は、 マジックナンバー をなくすためというのもあります。

ハードコーディングされた値のことをマジックナンバーと呼びます(本来、別の場所に保存しておくべき値をソースコードの中に直接記述してしまうこと)

また、書き方についてですが、定数は大文字で始める必要があります。最初の一文字が大文字であればOKですが、慣習的には全部大文字で書くことが多いです。

COMPUTER = 'MacBookPro'
=> "MacBookPro"

COMPUTER = 'WindowsMachine'
warning: already initialized constant COMPUTER
warning: previous definition of COMPUTER was here
=> "WindowsMachine"

selfキーワード

Rubyでは、インスタンスメソッドの中で、メソッドのレシーバ自身を参照するために、selfという特別な変数を使います。selfを付けても付けなくても挙動は変わりませんが、ここでのポイントは、selfがレシーバであることを理解することです。ちなみに、selfは省略して書かれることが多いとの事です。

以下、メソッドを呼び出す3パターンを用意しました。

  1. selfなしで、greet_kiyoshiroメソッドを呼び出す
  2. self付きで、greet_hirotoメソッドを呼び出す
  3. インスタンス変数@greetingを直接参照して、greet_yusukeメソッドを呼び出す
class RocknrollHero
    attr_accessor :greeting

    def initialize(greeting)
        @greeting = greeting
    end

    def greet_kiyoshiro
        "#{greeting}!!、愛しあってるかい!?"
    end

    def greet_hiroto
        "#{self.greeting}!!、我々はクロマニヨンズだ!!"
    end

    def greet_yusuke
        "#{@greeting}!!、俺たちが日本のザ・ミッシェルガンエレファントだ!!"
    end
end

hero = RocknrollHero.new('ロックンロール')
=> #<RocknrollHero:0x00007f9cdd97df70 @greeting="ロックンロール">

hero.greet_kiyoshiro
=> "ロックンロール!!、愛しあってるかい!?"

hero.greet_hiroto
=> "ロックンロール!!、我々はクロマニヨンズだ!!"

hero.greet_yusuke
=> "ロックンロール!!、俺たちが日本のザ・ミッシェルガンエレファントだ!!

selfが省略できない場合

name= メソッドのように、=で終わるメソッドを呼び出す場合は、selfの省略ができません。メソッド内で、セッターメソッド(書き込み専用)を呼び出す際には注意が必要です。

class RocknrollHero
    attr_accessor :greeting

    def initialize(greeting)
        @greeting = greeting
    end

    def greet_other_kiyoshiro
        greeting = 'ハローベイベー'
    end

    def greet_other_yusuke
    self.greeting = 'ハローベイベー'
    end
end

hero = RocknrollHero.new('ロックンロール')
=> #<RocknrollHero:0x00007f9cda3e71e0 @greeting="ロックンロール">

hero.greet_other_kiyoshiro
hero.greeting
=> "ロックンロール"    # 書き込み(変更)できない

hero.greet_other_yusuke
hero.greeting
=> "ハローベイベー"

輪読会「オブジェクト指向でなぜつくるのか」へ参加

クラスの章に入るタイミングで、下記の輪読会が開催されると聞き「これは良きタイミングだ!」と思い参加してみました。(7/26に開催)

www.nikkeibp.co.jp

ここ最近よく耳にする「オブジェクト指向

わかるようでわからない。そんな、奥の深いオブジェクト指向を学べる良書ということで、ワクワクした気持ちで本を読みました。週一ペースの開催になるため、まだ第2章までしか読み進めていませんが、こちらで学んだことも一部備忘録として残しておきたいと思います。

オブジェクト指向はソフトウエア開発の総合技術

オブジェクト指向でなぜソフトウエアを作るのですか?」誰かにこんな質問をされたなら、著者はこう答えます。「その理由はソフトウエアを楽に作りたいからです。」 出典:オブジェクト指向でなぜつくるのか 第3版 21頁

冒頭の一文です。

当初、私はオブジェクト指向とは、よく見かけるたい焼きや動物などで表現されている、単にクラスやインスタンスの話しだけだと思っていました。しかし、そんな単純なことではなく、今やオブジェクト指向をカバーする範囲は広く、ソフトウエア開発の総合技術として、プログラマが「ソフトウエアを楽につくるため」に取られる開発手法全般を指していることがわかりました。

オブジェクト指向が難しい理由3つ

  1. プログラミング言語の仕組みが複雑
  2. 比喩を使った説明による混乱
  3. オブジェクト指向というコンセプトが抽象的

また、難しいと思われている理由が3つ述べられており、特に3つ目の理由にも当てはまるであろう「全てがオブジェクト」という概念。このあたりの、難しいと思っている理由を少しでも紐解き、今後の章を読み進めるなかで理解を進めていきたと思います。

参考書籍

🍒 まとめ

今週からついにクラスの章に入りました!

輪読会で一番読みたかった章です。一度、通読してはいますが、クラスの章は「手強い...」という印象で、前回は完敗とまではいきませんが、負けを認めざるえない散々な結果でした...。輪読会メンバーの多くもこの章で苦労している方が多かったです。今回はそんな強敵クラスへリベンジすべく!疑問点は輪読会内で積極的に質問したり、復習にも時間をかけたりと気合を入れて取り組みました。

また、『オブジェクト指向でなぜつくるのか第3版』の輪読会も始まり、クラスの章を学ぶ上で、オブジェクト指向の考えが助けになっています。

学習を進めれば進めるほど、色々な壁が立ちふさがりますが、ひとつひとつ積み重ねて乗り越えていきたいと思います。

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

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第9週目まとめ

第6章6.3〜第6章6.4まで

期間:2021年07月19日〜2021年07月23日

第8週目で紹介したQiita記事の続きから進めました。

今週は、週の最後で第7章(クラス)にも入りましたが、こちらは第10週目の記事で取り上げたいと思います。

正規表現Regexpクラスのオブジェクト

Rubyでは扱える全ての値はオブジェクトです。

正規表現は、Regexpクラスになります。

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

正規表現は、/スラッシュで囲んだ形式で記述します。

\w は、[a-zA-Z0-9_](半角英数字とアンダースコア1文字) と、同等の意味になります。

wは小文字です。大文字のWになると、意味が変わってきますので注意です。[^a-zA-Z_0-9](半角英数字とアンダースコア以外の1文字)になります。

regexp = /\w{6}-\d{3}/
=> /\w{6}-\d{3}/
regexp.class
=> Regexp

=~ を使って正規表現を比較する

=~を使い、マッチした場合はマッチした最初の文字列の位置が返り値として戻ってきます。

'MONGOL-800' =~ /\w{6}-\d{3}/
=> 0

マッチしなかった場合は、nilが返ってきます。

'GO!GO!-7188' =~ /\w{6}-\d{3}/
=> nil

マッチした場合は真、マッチしなかった場合はnil(偽)を返す性質を利用し、if文などの条件分岐でよく使われます。

 if 'MONGOL-800' =~ /\w{6}-\d{3}/
   puts 'マッチしたさ〜'
 else
   puts 'マッチしないさ〜'
 end
マッチしたさ〜

!~ を使って正規表現を比較する

!~ は、=~の逆の結果が返ります。マッチしなかったときに真を、マッチしたときに偽を返せます。

 if 'MONGOL-800' !~ /\w{6}-\d{3}/
   puts 'マッチしたさ〜'
 else
   puts 'マッチしないさ〜'
 end
マッチしないさ〜

キャプチャ機能

英語のcaptureは「捕まえる」「取る」の意味になります。そこから、コンピュータにデータを取り込むことを指す用語として使われています。正規表現のキャプチャ機能は、マッチした部分の一部を取り出すものです。()を使い、抜き出したい部分を指定します。

キャプチャ機能でマッチした部分は、$数字の形式で取り出すことができます。

 /(\w{6})(-)(\d{3})/ =~ 'MONGOL-800'
=> 0
 $1
=> "MONGOL"
 $2
=> "-"
 $3
=> "800"

MatchDataオブジェクト

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

matchメソッドを使い、MatchDataオブジェクトとして扱うことで、マッチした箇所を指定して取り出すこともできます。0は、マッチ全体を返します。

 name = 'MONGOL-800'
 m = /(\w{6})(-)(\d{3})/.match(name)
=> #<MatchData "MONGOL-800" 1:"MONGOL" 2:"-" 3:"800">
 m[1]
=> "MONGOL"
 m[2]
=> "-"
 m[3]
=> "800"
 m[4]
=> nil
 m[0]
=> "MONGOL-800"

キャプチャの結果に名前を付ける

上記の方法でキャプチャした結果に名前を付けることができます。(?<foo>)というメタ文字を使います。キャプチャの結果に名前を付けることで、取り出すときに指定した名前で呼び出せます。名前があることで、何の値を取得しているのかが分かりやすくなります。

 name = 'MONGOL800のメンバーはkiyosaku、takashi、satoshiです'
 m =  /(?<band_name>\w{9})のメンバーは(?<bass>\w+)(?<guitar>\w+)(?<drums>\w+)です/.match(name)
=> #<MatchData...
 m[:band_name]
=> "MONGOL800"
 m[:bass]
=> "kiyosaku"
 m[:guitar]
=> "takashi"
 m[:drums]
=> "satoshi"

正規表現と組み合わせると便利なメソッド

scanメソッド

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

matchメソッドと似ていますが、scanメソッドは引数で渡した正規表現にマッチする部分を「配列」に入れて返します。(matchメソッドは、マッチした場合には結果を MatchData オブジェクトで返します。マッチしなかった場合 nil を返します。)

以下は、マッチした数字\dを配列で返しています。

'MONGOL800 GO!GO!7188 10-FEET'.scan(/\d+/)
=> ["800", "7188", "10"]

() を使い、キャプチャした部分を配列の配列で返すこともできます。

'MONGOL800 GO!GO!7188 10-FEET'.scan(/(\d+)/)
=> [["800"], ["7188"], ["10"]]

[ ] メソッド

[] は、スクエアブラケット、角カッコと読みます。

エイリアスメソッド(slice)

[]メソッドを使うと、文字列から正規表現にマッチした部分を抜き出すことができます。

band_name = 'MONGOL800 GO!GO!7188 10-FEET'
band_name[/\w{6}\d{3}/]
=> "MONGOL800"

複数ある場合は、最初にマッチした文字列が返ります。

band_name = 'MONGOL800 LONDON700'
band_name[/\w{6}\d{3}/]
=> "MONGOL800"

slice! メソッド

マッチした部分が文字列から破壊的に取り除かれます。

band_name = 'MONGOL800 GO!GO!7188 10-FEET'
=> "MONGOL800 GO!GO!7188 10-FEET"
band_name.slice!(/\w{6}\d{3}/)
=> "MONGOL800"
band_name
=> " GO!GO!7188 10-FEET"

gsubメソッド、gsub!メソッド

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

第1引数の正規表現にマッチした文字列を、第2引数の文字列へ置き換えるメソッドです。

band_name = 'MONGOL800'
band_name.gsub(/\d{3}/, '八百')
=> "MONGOL八百"

置き換え後に、変数band_nameを確認すると元に戻っています。

band_name
=> "MONGOL800"

破壊的に変更するには、gsub! メソッドを使います。

band_name = 'MONGOL800'
band_name.gsub!(/\d{3}/, '八百')
=> "MONGOL八百"
band_name
=> "MONGOL八百"

正規表現オブジェクトを作成する

Regexp.new

エイリアスメソッド(Regexp.compile)

Regexp.compile (Ruby 3.0.0 リファレンスマニュアル)

Regexp.newの引数へ正規表現の文字列を渡すと、正規表現オブジェクトが作成できます。

正規表現オブジェクトを変数に代入して使いたいときは、以下のようにRegexp.newが必要です。

regexp = Regexp.new('\d+')
=> /\d+/
'MONGOL800 GO!GO!7188 10-FEET'.scan(regexp)
=> ["800", "7188", "10"]

%r (%記法)

//の代わりに%r記法を使うこともできます。

以下、4つとも同じ意味です。

 /foo/
=> /foo/
 %r{foo}
=> /foo/
 %r!foo!
=> /foo/
 %r?foo?
=> /foo/

//を使う場合は、スラッシュをエスケープする必要があります。

/http:\/\/www\.google\.com/
=> /http:\/\/www\.google\.com/

%rを使うときは、スラッシュのエスケープ処理は不要です。ただし、.についてはメタ文字のため、エスケープ処理が必要になります。

%r{http://www\.google\.com}
=> /http:\/\/www\.google\.com/

case文で正規表現を使う

case文は、whenで指定した値に一致するかどうかを===演算子を使って判定しています。==ではありません。(===演算子 についての詳細は、チェリー本269頁)

=== 演算子を使うことで、オブジェクトのデータ型を識別することができます。

配列の場合

array = [1,2,3]

case array
when String
    puts '文字列'
when Array
    puts '配列'
when Regexp
    puts '正規表現'
end
配列

正規表現の場合

regexp = Regexp.new('\d+')

case regexp
when String
    puts '文字列'
when Array
    puts '配列'
when Regexp
    puts '正規表現'
end
正規表現

when節に正規表現を指定して、以下のようにマッチする文字列を判定するときに使うこともできます。

band_name = 'MONGOL800'

case band_name
when /\w{6}\d{3}/
    puts 'モンパチ'
when /\d{3}/
    puts '八百'
end
モンパチ

輪読会メンバーがLT会へ登壇!

今回のブログを書き始めた8月7日のこの日、フィヨルドブートキャンプ内で開催されるLT会へ参加しました。輪読会メンバーのぺろさん(id:mosshu0703)と、いっしーさん(id:isshi-hasegawa)が登壇されたのですが、とても良かったので当ブログでも感想を書きたいと思います。

隣の芝生はいつも青い

フィヨルドブートキャンプは、メンターさん、アドバイザーさんをはじめ、同じ受講生でも凄い方が多く、隣どころか360度青くしか見えない私にとって、ぺろさんの発表はとても勇気がもらえました。

自分の庭に集中しよう

ほんとコレですね。

分からないことやできないこと、詰まって思い悩むことは今でも頻繁にありますが、そんなときこそ、ぺろさんのLTを思い出し、目の前のことに集中し人と比べず取り組んでいきたいと思います。

エモくて元気になれる素敵なLTでした!サンクス!

eminoprogramming.hatenablog.com

2021年フィヨルドブートキャンプの旅

映画が好きないっしーさん。物語のような発表構成で、スライドの冒頭からその世界観に引き込まれました。普段から輪読会やもくもく会などで積極的にコミュニケーションを取られている姿や、何でも器用にこなしている姿しか知らなかったので、今までの独学期間で思い悩まれていた話しなどは意外でした。当時の苦労があったからこその今に繋がっているんですね。それにしてもまだ3ヶ月ってのは本当に凄い!

技術や言語が好きになる = マジ最強💪

もちろん、ほとんどの受講生が転職やスキルアップなど、何らかしらの現実的な目標を持ち学習に取り組んでいるとは思いますが、シンプルに「好き」だからやる。これが一番最強なんだと思えました。あれやこれやと考え、上手くいかないことも多かったりするのですが、もっとシンプルに考えていきたと思いました。私もまだまだ旅の途中ですが、楽しんで経験値を積み、まずはフィヨルドブートキャンプというゲームのクリアを目指し、頑張っていきたいと思います!

isshi-hasegawa.hatenablog.com

「好き」に繋がりますが、我らが輪読会のオーガナイザー、トミーさん(id:eatplaynap329)のアツいツイートもご紹介🔥

参考書籍

🍒 まとめ

正規表現の章を終え、基礎を学んだことで、「意味不明の恐ろしい呪文」がそうではなくなりました。他の場所で見かける正規表現に対しても、「ひとつひとつ調べればわかる」と、うろたえることなく扱えるようになったことは、またひとつ自信になりました。

いよいよ次は、最初の関門「クラス」の章に入ります。

オブジェクト指向の知識も必要となってくる章となり、難易度がグッと上がります。

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

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

f:id:shirotamaki:20210619091936p:plain

🍒 はじめに

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

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

🍒 輪読会 第8週目まとめ

第6章6.1.1〜第6章6.2.2章まで

期間:2021年07月12日〜2021年07月16日

正規表現とは?

「パターンを指定して、文字列を効率よく検索・置換するためのミニ言語」

チェリー本ではこのように表現されています。

正規表現Rubyだけのものではなく、他プログラミング言語でも使える汎用的な表現方法です。

いくつかの文字列を一つの形式で表現するための表現方法とも言えます。

英語で書くと、Regular Expression となり、Regex, Regexpと略されます。

正規表現をゼロから学習するための参考資料(Qiita記事)

チェリー本でも紹介されていますが、まずはこちらの伊藤さんのQiita記事を元に学習を進めていきました。

輪読会では、その1からその3までを取り上げました。その4については、Qiita記事にもあるように「中級者向け」の内容となり、一気に難易度が上がっています。今回はチェリー本の本筋から外れてしまうため、一旦こちらは各自で目を通すだけにし、深追いするのはやめました。

メタ文字

正規表現では、さまざまなメタ文字が使われています。

メタ文字とは「コンピュータープログラムに与える文字列データの中で、特別な意味や役割を持つ文字のこと」です。\[ ]- などのことを指します。

例えば、\dは半角数字1文字を表します。[AB] は、AまたはBが1文字であることを表しています。

正規表現とは、このようなメタ文字の組み合わせで表現する、検索・置換するためのミニ言語です。

RubyMineで正規表現の置換モードを使う

ショートカットコマンド command + R にて、入力できる項目が表示されます。

その後、矢印の箇所をクリックにしONにすることで、正規表現の検索・置換モードへ切り替えられます。

f:id:shirotamaki:20210804172838p:plain

このような形で、正規表現がマッチする箇所がハイライトされます。

f:id:shirotamaki:20210804172913p:plain

タブ文字は何文字分か?

タブ文字は \t で表現できます。

タブ文字の見た目が「スペース何文字分か」は、出力するエディタなりコンソールなりの設定によって異なるようです。Rubyではタブ文字は半角スペース2文字分が推奨されていることが多いため、私もRubyMineでは、文字数「2」へ設定しています。

Rubyでは、内部に含まれるものを記述する時、常に2文字のインデントが推奨されています。 参照:独習Ruby on Rails(40頁)

環境設定command + ,から、エディター > コードスタイル > Ruby で変更できます。

f:id:shirotamaki:20210804172941p:plain

\t の代わりに\sを使うこともできる

\s は、空白文字を表します。\s には、タブ文字\t改行文字\nや復帰文字\rなども含まれています。ただし、\s にマッチする文字は、言語や環境によって違うので注意が必要です。

Rubyの場合は、こうなります。\s = [ \t\r\n\f]

正規表現との出会い

余談ですが、初めて正規表現と出会った時のことを思い出したため書きたいと思います。

数ヶ月も前の話しですが、Railsチュートリアルへ取り組んでいるとき、講師の@yasulab さんが、解説動画内でこのようなことを仰っていました。

  • 正規表現は奥が深い。
  • 正規表現にテーマを絞ったカンファレンスも開催されており、好きな人にはたまらない。
  • 正規表現をツマミに酒が飲めるww
  • 正規表現は実際に試しながら学んだ。

そこでは、正規表現の教科書とも言える、O'Reillyから出版されている本も紹介されていたのですが、こちらの本はなんと正規表現だけで528頁もあるそうです。(チェリー本の正規表現の章は25頁)

www.oreilly.co.jp

@yasulab さんも、こちらの本を全部は読んでいないそうで、正規表現は実際使っていくうちに覚えていったそうです。

やはり「手を動かす」ことが大事なのですね。

今回改めてチェリー本で正規表現を学び直すなかで、正規表現をツマミにお酒が飲める気はまったくしませんがww、コーヒーくらいなら楽しめるようになってきたと思います。

いつかは、フクロウ本にもチャレンジしてみたいです!

るりま勉強会(Kyoto.rbへ潜入!)

輪読会でお世話になっている(id:sanfrecce-osaka) さんの紹介で、この日(2021年7月11日)るりま勉強会が開催されることを知りました。Kyoto.rbで開催されるとのことで、初めて参加してみました。

以前、ブログや、LTの題材としても取り上げたり、日々慣れ親しんではいるものの、まだまだ知らないことも多く苦手意識がある「るりま」。今回、少しでも学び、気づきが得れればと思い参加しましたが、結果、とても良かったです。特にRuby熟練者からのるりまに関する話しは貴重で、とても学びの多い勉強会でした。

  • 10年近くRubyを使っているが、それでも知らないメソッドはたくさんある。など、現役エンジニアからの声。
  • るりまの読み方
  • permalink, rdoc とは?
  • ターミナルでsayとすると、音声で読み上げてもらえる!(小ネタ的なことも!)

などなど。

同じく参加された(id:fuga__ch) さんのブログに詳しい勉強会の内容がまとまっています。 勉強会の詳細が知りたい方はぜひ!

fuga-ch85.hatenablog.com

また、今回の勉強会には多くのフィヨルド生や卒業生が参加されていました。普段より多くの参加者に対し、主催者の方も驚かれていましたww 有益な勉強会を開催してくださったKyoto.rbの皆さま、ありがとうございました!ぜひまた第二弾!楽しみにしております!

参考書籍

🍒 まとめ

今週から入った正規表現。Qiitaの記事を中心に、その1からその3まで進めました。

こちらの記事は「正規表現初心者」を対象としている内容なので、とてもわかり易かったです。

謎の呪文が\d{2,5}[-(]\d{1,4}[-)]\d{4} 謎ではなくなり、基本的な正規表現についてはポイントを抑えることができました。

「手と目で覚える正規表現

伊藤さんも仰っているように、手を動かして使い慣れていくことが、正規表現を身につける一番の近道だと思いました。今回学んだRubyMineの検索・置換モードや、Rubular を活用し身につけていきたいと思います。

次週も引き続き正規表現へ取り組みます。

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