プログラミング漫遊記

思ったことや、勉強したことをつらつらと。

【Ruby】等差数列の配列をつくる

たとえば、3から23までの範囲で、4ずつ増えていく数列の配列を作りたいとする。 数学的にいうと初項3、公差4の数列を23になるまで作りたい こんな感じ。(最後のやつがいいたいくて書いたブログです)

[3, 7, 11, 15, 19, 23]

やり方を色々考えてみる。 環境はRuby3.1です。

Enumerable#selectで絞る

パッと思いつくものはselectメソッドを使って絞り込むというもの。3, 7, 11, ... は4で割って3余る数の集合とみなせるので以下のようにできる。

(3..23).select { _1 % 4 == 3 }

Enumerable#mapで頑張る

(0..5).map { 4 * _1 + 3 }

一般項を求めてmapで変換するけど、これは少し苦しいなー。なぜ0から5とわかるんだと言われたら逆算してるので、、、と答えるしかない。 selectで絞るという方法もあるけど、、、微妙ですね。ボツ。

Enumerator::ArithmeticSequence クラスを使う

僕も初めて知ったんですが、Enumerator::ArithmeticSequenceなる等差数列を扱うクラスがあるみたい。 クラス名長すぎやしませんかね?笑

るりま:https://docs.ruby-lang.org/ja/latest/class/Enumerator=3a=3aArithmeticSequence.html

等差数列オブジェクトを作成するにはstepメソッドを使う

等差数列オブジェクトの生成
as = 3.step(by: 4, to: 23)
=> (3.step(by: 4, to: 23))

as.class
=> Enumerator::ArithmeticSequence

as.to_a
=> [3, 7, 11, 15, 19, 23]

# こうも書ける
as = 3.step(23, by: 4)

Enumerableモジュールを継承(インクルード)しているのでループ処理をするだけならto_aは不要です。

%を使って短く記述することも可能。(これを紹介したかった)

as = (3..23) % 4
=> ((3..23).%(4))

as.class
=> Enumerator::ArithmeticSequence

公差が0の時はエラー

as = (3..23) % 0
=>  `%': step can't be 0 (ArgumentError)

特に%の方は短くかけて便利そうじゃないですか? AtCoderとかで使えそうだなと思いました。

まとめ

(3..23) % 4がかっこいいから紹介したかっただけ。

https://docs.ruby-lang.org/ja/latest/class/Range.html#I_--25

【Ruby】2重配列から特定の位置にある要素の配列を作る

先日、ド忘れして困ったので、忘備録として。

たとえば1つ目の要素だけを集めた配列を作りたいとする。 こんな感じ。

# 1つ目の要素だけを集めた配列を作るイメージ
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
=> [1, 4, 7]

Enumerable#mapを使うと簡単

ary = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

ary.map(&:first)
=> [1, 4, 7]

もう少し一般化する。 n番目の要素だけを集めた配列を作りたい場合

ary = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

ary.map { |a| a[n - 1] }

番外編としてArray#transposeを使う方法もある

ary = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ary.transpose[n - 1]

Enumerableモジュールのメソッドツアー - max_byメソッド

Enumerableモジュールのメソッドを1記事1メソッドで雑に紹介していくコーナー第3弾です。

前回はmaxメソッドを紹介しました。

haruguchi-yuma.hatenablog.com

今回はmaxメソッドにとても似ているmax_byメソッドについて紹介します。

max_byメソッド

まずはるりま*1の確認から。

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

各要素を順番にブロックに渡して実行し、その評価結果を <=> で比較して、最大であった値に対応する元の要素、もしくは最大の n 要素が降順で入った配列を返します。 引数を指定しない形式では要素が存在しなければ nil を返します。引数を指定する形式では、空の配列を返します。該当する要素が複数存在する場合、どの要素を返すかは不定です。 Enumerable#max と Enumerable#max_by の違いは Enumerable#sort と Enumerable#sort_by の違いと同じです。

「各要素をブロックに渡して実行し、〜」とあるようにブロック付きで呼ぶのが基本となるメソッドみたいです。 maxメソッドと同じく、なんらかの観点で比較したときの、最大の要素を引数の数だけ取得するのが基本の使い方になります。

ちなみに、ブロックなしで呼び出した場合はEnumeratorオブジェクトが返ってきます。

では挙動を確認していきます。

irbで挙動の確認

Ruby 3.1.2で実行しています。

ブロックありでの呼び出し

# 一の位が最大である要素を取得
[8, 32,4].max_by{ |n| n % 10}
# => 8

# イメージ
8 % 10 <=> 32 % 10で比較
8が大きい
8 % 10 <=> 4 % 10 で比較
8が大きい
8を返す

# 最大の要素がない場合はnil
[].max_by{ |n| n }
=> nil

引数を指定すると最大の要素を引数の数だけ取得することができます。 また、返り値は配列になります。

# 文字数で比較
%w(panda monkey dog cat).max_by(2) { |str| str.length }
=> ["monkey", "panda"]

# 引数に負数を渡すとArgumentError
%w(panda monkey dog cat).max_by(-1) { |str| str.length }
`max_by': negative size (-1) (ArgumentError)

ちょっとだけ実用的な例を見てみましょう。 数学のテストの点数が一番高いユーザーのテスト結果を調べます。

students = [
  {name: 'taro', japanese: 30, math: 50, english: 90},
  {name: 'jiro', japanese: 80, math: 23, english: 20},
  {name: 'saburo', japanese: 88, math: 92, english: 79},
]

students.max_by { |student| student[:math] }
=> {:name=>"saburo", :japanese=>88, :math=>92, :english=>79}

ブロックなしで呼び出した場合

ブロックなしで呼び出した場合はEnumeratorオブジェクトを返します。

('a'..'c').max_by
=> <Enumerator: ...>


('a'..'c').max_by(2)
=> <Enumerator: ...>

maxメソッドとmax_byメソッドの違い

言葉で説明するのは難しいのですが、

maxメソッドは要素を2つずつ取り、明示的に<=>メソッドで比較を行います。(正確には左辺が大きいときは正の数、右辺が大きいときは負の数、等しいときは0を返せば<=>メソッドでなくても良いです)

一方、max_byメソッドは比較基準だけ示しておけば、内部的に<=>メソッドで比較を行ってくれます。

パフォーマンスの観点からどちらが優れているというのは未検証のため、言及は控えておきます。

# maxメソッドの場合
%w(panda monkey dog cat).max { |animal1, animal2| animal1.length <=> animal2.length }
=> "monkey"


# max_byメソッドの場合
%w(panda monkey dog cat).max_by { |animal| animal.length }
=> "monkey"

何かしらの観点で最大の要素を取得したい場合はmax_byを使った方が記述量は少なくすみそうです。

まとめ

  • max_byは最大の要素を取得する
  • ブロックを評価した後<=>で比較処理が行われる
  • 引数に正整数を渡すとその数だけ最大の要素を取得できる
  • ブロックなしで呼び出した場合はEnumeratorオブジェクトが返ってくる

以上です!

*1:Rubyリファレンスマニュアルの略

Enumerableモジュールのメソッドツアー - maxメソッド

Enumerableモジュールのメソッドを1記事1メソッドで雑に紹介していくコーナー第2弾です。

前回はall?メソッドを紹介しました。

haruguchi-yuma.hatenablog.com

今回はmaxメソッドについて紹介していきます。

maxメソッド

まずはるりま*1の確認から。

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

ブロックの有無で説明が分かれていました。

詳しい説明はるりまに譲るとして、雑に紹介するなら、最大の要素を引数の数だけ返すメソッドということになります。

最大の要素がなければ、nilを返します。

早速irbなどで試してみましょう。

irbで挙動の確認。

Ruby3.1.2で確認しています。

Enumerable#maxの説明ではありますが、ArrayやRangeクラスなどオーバーライドしているmaxメソッドも併せて紹介します。

ブロックなしの場合

ArrayやRangeオブジェクトに対して引数ありと引数なしでmaxメソッドを呼び出しています。

[1, 2, 3, 4, 5].max
=> 5

[1, 2, 3, 4, 5].max(2)
=> [5, 4]

(1..10).max
=> 10

(1..10).max(3)
=> [10, 9, 8]

('a'..'z').max
=> "z"

('a'..'a').max
=> 'a'

[].max
=> nil

引数なしの場合は返り値は最大の要素そのものが返ってきており、最大が見つからない(空配列など)場合はnilを返していることがわかります。 引数ありの場合は返り値は配列です。引数で指定した要素数だけ最大値を選び、降順に並べて返します。

# 引数なし
[].max
=> nil

(2..1).max
=> nil

# 引数あり
[].max(2)
=> []

(2..1).max(2)
=> []

上のコードを試して個人的にはびっくりしたポイントです。引数有りの場合は最大が見つからなくても配列を返すというのがわかりやすいです。

難しい話ですが、maxメソッドで最大の要素とは何かというと<=>で比較できるものみたいですね。 module Comparable (Ruby 3.1 リファレンスマニュアル)

100 <=> 1 # 比較できる
=> 1

1.2 <=> 1 # 比較できる
=> 1

'a' <=> 'aa' # 比較できる
=> -1

true <=> false # 比較できない
=> nil

1 <=> 'a' # 比較できない
=> nil

上の例でいくとbooleanは比較できないのでmaxメソッドで最大の要素は取得できません。(true <=> trueは0なので等価性は比較できる)

また、Integerとstringなど型が違う場合(Integerとfloatは除く)<=>で比較できないのでmaxメソッドは例外を発生させます。

 [true, false, true, false].max
=> comparison of TrueClass with false failed (ArgumentError)

[1, 'a', 3,5, false].max
=> comparison of String with 1 failed (ArgumentError)

Hashはmaxメソッドをオーバーライドしていません。

{a: 3, b: 2, c: 1}.max
=> [:c, 1]

{a: 3, b: 2, c: 1}.invert.max # invertはkey valueをひっくり返すメソッド
=> [3, :a]

{a: 3, b: 2, c: 1}.max(2)
=> [[:c, 1], [:b, 2]]

おそらくですが、keyで大小比較をして、[key, value]の配列(つまり要素)で返しているみたいです。

ブロック有りの場合

るりまの説明です。

ブロックの評価結果で各要素の大小判定を行い、最大の要素、もしくは最大の n 要素が入った降順の配列を返します。引数を指定しない形式では要素が存在しなければ nil を返します。引数を指定する形式では、空の配列を返します。ブロックの値は、a > b のとき正、 a == b のとき 0、a < b のとき負の整数を、期待しています。該当する要素が複数存在する場合、どの要素を返すかは不定です。

なんだか難しいですが、ブロックパラメータには要素が2つずつ入ってきます。この2つの要素をブロック内で比較して左辺が大きい場合は正の数を返し、左辺が小さい場合は負の数を返し、左辺も右辺も等しい場合は0を返すようにすればOKです。そうすることで「どの観点で最大値を取るか」を柔軟に指定することができます。

文字列なら通常は辞書順ですが、ここでは文字の長さで最大値を取りたいとします。つまり、文字列が長い方が最大であると定義します。

['monkey' 'Gorilla gorilla', 'cat'].max
=> "monkey" # この場合は辞書順

['monkey', 'Gorilla gorilla', 'cat'].max do |a, b|
  if  a.length > b.length
    1000 # 左辺が大きい場合 正の数を返す
  elsif a.length < b.length
    -1000 # 左辺が小さい場合 負の数を返す
  else
    0 # 等しい場合 0を返す
  end
end
=> "Gorilla gorilla"

今回は文字数で比較したいのでlengthメソッドを利用しています。

ブロックの値は、a > b のとき正、 a == b のとき 0、a < b のとき負の整数を、期待しています。

ブロックの評価で数値を返す部分は適当な数を返しましたが、本来<=>を使うともっと簡単に記述することができます。

['monkey', 'Gorilla gorilla', 'cat'].max { |a, b| a.length <=> b.length }
=> "Gorilla gorilla"

ブロックを渡すことで最大とは何か?自分で定義できるのがいいところですね。 下の例は文字列の最後の文字を辞書順に比較する例です。 (そんなことしたい場合はなさそう😅)

['monkey', 'Gorilla gorilla', 'cat'].max { |a, b| a[-1] <=> b[-1] }
=> 'monkey'

monkeyの最後の文字が'y'なのでうまくいってるようです。

まとめ

雑にまとめます。

  • maxメソッドは最大の要素を返す
  • 引数を指定するとその指定した引数の要素数だけ配列にして返す(降順)
  • ブロックを渡すことができる -ブロックでは2つの要素を比較し、左辺が大きければ正の数、左辺が小さければ負の数、左辺と右辺が等しければ0を返す
  • そういう決まりを守ることで最大とは何かを柔軟に定義できる

個人的にmax_byとよく混同してしまうので今回整理できてよかったです。

*1:Rubyリファレンスマニュアルの略

【Ruby】Enumerableモジュールのメソッドツアー - all?メソッド

こんにちは。はるぐちです。

Enumerableモジュールってご存知でしょうか。そうArrayとかHashとかにインクルードされている便利なやつ。

これのおかげで反復処理がスムーズにできると言っても過言ではないEnumerableモジュールですが、メソッドを全部知っているわけではないなーと思って1記事1メソッドでツアーをすることにしました。

ちゃんとした仕様の説明はRubyリファレンスマニュアル(以下るりま)をみてください。僕は雑に紹介していきます。

all?メソッド

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

all?メソッドは各要素を調べて、真偽値を返すメソッドです。

すべての要素が真である場合はtrue、1つでも偽である場合はfalseを返します。

言葉だけではイメージがつかないこともあると思うので、早速irbで遊んでみます。

irbで挙動の確認

Ruby3.1で確認しています。

引数なしの場合

引数なしの場合は各要素を真偽値に変換します。 Rubyの場合はnilfalseの場合はfalseになり、それ以外はtrueに変換されます。

[true, false, true].all?
=> false
[0, 'foo', nil].all?
=> false

基本的にrangeオブジェクトやhashオブジェクトに対して引数なしのall?メソッドを呼び出すのはあまり意味がなさそうです。

# なんでもtrueになる
(1..3).all? # 1も2も3もtrueなので返り値はtrue
=> true

{a: 1, b: nil}.all?
=> true

余談ですが、ハッシュのvalueに対してすべての値がtrueか確認したい時はHash#valuesメソッドを使って配列に変換するのが良さそうだと思いました。

# 配列に変換 valueだけ取り出す
{a: 1, b: false}.values
=> [1, false]


{a: 1, b: false}.values.all?
=> false

引数ありの場合

引数あり(ブロックなし)の場合は引数に対して===メソッドを呼び出し各要素と比較します。 ===メソッドの説明は大変なので割愛したい。(挙動がたくさんあって説明しきれないので、、、😅) class Object (Ruby 3.1 リファレンスマニュアル)

[1, 2, 3].all?(Integer)
=> true
[1.2, 2, 3].all?(Integer)
=> true

['foo', 'bar', 'baz'].all?(/\w+/)
=> true
['foo', 'bar', ''].all?(/\w+/)
=> false

これだけだとイメージしにくい方は以下にイメージ図を参考にしてください。

# イメージ図
[1.2, 2, 3].all?(Integer) の場合

# 各要素に対して順番に 引数 === 要素 を実行していく
Integer === 1.2  false
Integer === 2 true
Integer === 3 true

[false, true, true].all?
=> false

実際には1つでもfalseな要素があった場合はそれ以降の要素は確認されずにfalseが返されるみたいなのでこれはあくまでもイメージと思ってください。

ブロックありの場合

もう少し詳細に条件を絞り込みたい場合はブロック付きでメソッドを呼び出します。

ブロックパラメータは各要素で、ブロックの中でbooleanを返すような条件式を記述します。

# すべて3以上かどうかを調べる
(2..5).all? { |v| v >= 3}
=> false

# すべての要素が偶数かどうか調べる
[2, 4, 6, 8].all(&:even?)


# 全員成人かどうか調べる
users = [
  {name: 'taro', age: 40},
  {name: 'jiro', age: 30},
  {name: 'saburo', age: 20}
]

users.all? { |hash| hash[:age] >= 20 }
=> true

ブロックを用いる場合はHashでも使い道がありそうです。

{a: 1, b: 2}.all? { |k, v| Integer === v }
=> true
{a: 1, b: 2.0}.all? { |k, v| Integer === v }
=> false

まぁ、この例だったら配列に変換したほうがよさそうではありますね😅

引数とブロックを与えた場合

引数とブロックを与えた場合はどちらが優先されるのでしょうか?るりまには記述が見当たらなかったので自分で調べていきます。

[1, 2, 3].all?(Integer) { |v| v >= 2 }
warning: given block not used
true

ご覧の通り引数が優先されるみたいです。 warningが出てるので、ひょっとしたらall?メソッドの話ではなく引数とブロックが両立しないブロック付きメソッドの共通の仕様なのか?(未検証)

まとめ

雑にまとめます。

  • all?メソッドは各要素がすべて真ならtrue、そうでないならfalseを返すメソッド
  • 引数ありの場合は引数 === 要素で検証
  • ブロック付きの場合
    • ブロックパラメータ: 各要素
    • ブロックの中にはbooleanが返るような式を記述

【JS】イテレータ(iterator) / 反復可能オブジェクト(iterable object) ってなんだ?

こんにちは。はるぐちです。

最近JavaScriptイテレータについて学習したのでまとめておきたいと思います。 勘違いや誤り等ありましたら、コメントで指摘していただけると助かります🙏

イテレータとは

反復処理などでよく見かけるイテレータ。 わかってるようでわかってないのでまずは言葉の意味を理解するところから。

まずはMDNを見てみます。 イテレーターとジェネレーター - JavaScript | MDN

JavaScript では、イテレーターはシーケンスおよび潜在的には終了時の戻り値を定義するオブジェクトです。

なるほど、よくわからん😅となりますね。

めちゃくちゃざっくりいうと反復処理において、次の値を示すvalueプロパティと,反復処理の終了を表すdoneプロパティの2つのプロパティを返すnextメソッドを実装しているオブジェクトをイテレータと呼びます。

  • イテレータ
    • nextメソッドを持つ
      • 返り値:以下の2つのプロパティを持つオブジェクトを返す
        • value: 反復処理における次の値
        • dole: 反復処理における最後の値が消費されたかどうかを表す。反復処理が終わっていたらtrueになっている

nextメソッドの戻り値を確認してみる

Array.prototype.values()メソッドはイテレータを返すのでnext()メソッドがどのようになっているか、nodeで確かめてみます。

Array.prototype.values() - JavaScript | MDN

> const arr = ['a', 'b', 'c', 'd'];
undefined
> arr.values(); // イテレータが返ってることがわかる
Object [Array Iterator] {}
> const it = arr.values() // イテレータを変数に代入
undefined
> it.next(); // nextメソッドを呼び出すとオブジェクトが返ってくる
{ value: 'a', done: false }
> it.next(); 
{ value: 'b', done: false }
> it.next();
{ value: 'c', done: false }
> it.next();
{ value: 'd', done: false }
> it.next();
{ value: undefined, done: true } // 反復処理が終わったらdone: trueになる
> it.next();
{ value: undefined, done: true } // 反復処理が終わったら何回呼び出しても結果は同じ

イテレータfor ... of文で反復処理を行うことができる

配列からイテレータを取り出してfor ... of文で反復処理を行なってみます。

> const arr = ['foo', 'bar', 'baz'];
> const it = arr.values() // イテレータを代入

> for (const i of it) { // イテレータを反復処理する
// 内部的にイテレータからvalueが取り出される
    console.log(i.toUpperCase()); 
    }
//→FOO
//→BAR
//→BAZ

> it.next()
{ value: undefined, done: true } // 反復処理が終わったのでdone: true

このようにイテレータfor ... of文で反復処理を行うことができます。 (というか`for ... of文がイテレータを処理する実装になっていると言った方が正しいかもしれません。)

反復可能オブジェクト

次に反復可能オブジェクト(iterable object)についてみていきます。

安定のMDN反復処理プロトコル - JavaScript | MDN

反復可能プロトコルによって、 JavaScript のオブジェクトは反復動作を定義またはカスタマイズすることができます。例えば、 for...of 構造の中でどの値がループに使われるかです。一部の組み込み型は既定の反復動作を持つ組み込み反復可能オブジェクトで、これには Array や Map がありますが、他の型 (Object など) はそうではありません。

イテレータの説明よりはわかりやすいかな。😅

これもざっくり説明すると反復可能オブジェクトとはArrayMapStringのようなオブジェクトのことで、イテレータを持っているオブジェクトのことを指します。

(さっきも配列からイテレータを取り出しましたよね)

もう少し厳密にいうと@@iteratorメソッドを実装しているオブジェクトのことで、このメソッドは返り値にイテレータを返します。

@@iteratorメソッドはSymbol.iteratorというプロパティに定義されているので、自作クラスで反復可能オブジェクトを作りたい場合は[Symbol.iterator]メソッドを定義することになります。

  • 反復可能オブジェクト
    • [Symbol.iterator]メソッドを実装しているオブジェクト
      • 返り値はイテレータ(nextメソッドを実装してるオブジェクト)

反復可能オブジェクトはfor ... of文で反復処理ができる

for ... of文は反復可能オブジェクト(iterable object)に対して反復処理ができます。 (イテレータは反復可能オブジェクトでもあるので先ほどのイテレータの例も反復処理が可能だったと考えることができます。)

for...of - JavaScript | MDN

// 以下はStringの反復処理
const str = 'hello world';

for (const c of str) {
  console.log(c.toUpperCase());
}

H
E
L
L
O
 
W
O
R
L
D

自作クラスを反復可能にしてみる

最後に自作クラスを反復可能にしてみます。

やること

Setオブジェクトを再発明したGroupクラス(およびGroupIteratorクラス)を作ります。*1 Groupにはadd, delete, hasメソッドとfromという静的メソッドを用意します。

Setオブジェクトと挙動を合わせるため以下のような要件になっています。

  • Groupクラス
    • add : 引数を1つとり、その値を追加する。重複している値は無視する。
    • delete: 引数を1つとり、その値を削除する
    • has: 引数を1つとり、その値が存在するかどうか確かめる。Booleanを返す。
    • static from: 反復可能なオブジェクトを1つ受け取り、それを反復して生成されたすべての値を含むGroupオブジェクトを作成する。
    • ]symbol.iterator]: イテレータオブジェクトを返すメソッド -GroupIteratorクラス => イテレータオブジェクトを作成するクラス
    • next: {value: ... , done: ...}というオブジェクトを返すメソッド

作成してみた

class Group {
  constructor () {
    this.values = [];
  }

  static from(obj) {
    const group = new Group();
    for (let element of obj) {
      group.add(element);
    }
    return group;
  }

  add(value) {
    if (!this.has(value)) {
      this.values.push(value);
    }
  }

  has(value) {
    return this.values.includes(value);
  }

  delete(value) {
    this.values = this.values.filter(function(ele){
      return ele != value;
    })
  }

  [Symbol.iterator]() {
    return new GroupIterator(this);
  }
}

class GroupIterator {
  constructor(group) {
    this.index = 0;
    this.group = group;
  }

  next() {
    if (this.index === this.group.values.length) return {done: true};

    let result = {value: this.group.values[this.index], done: false};
    this.index++;
    return result;
  }
}

できました。

反復処理してみる

作ったGroupクラスからオブジェクトを作成し、反復処理してみます。

for (let value of Group.from(["a", "b", "c"])) {
  console.log(value);
}
// → a
// → b
// → c

できていますね。 今回で言うとGroupクラスは配列で内部データを表しているのでイテレータを取り出すのはもっと簡単なんですが、わざわざ自分で作ってみて理解が深まりました。

参考

*1:『流麗なJAVASCRIPT』という書籍を参考にしています

「勝手にモブプロ」というのを勝手にやってみた

2月からフィヨルドブートキャンプで「勝手にモブプロ」という会をはじめてみて、ちょうど?17回目を終えたので感想をば。

勝手にモブプロ会とは?

僕(@haruguchi)がフィヨルドブートキャンプで勝手にやっているモブプロ会になります。 実際、参加募集している様子を見てもらった方が早いかもしれないです。

こんな感じ。

フィヨルドブートキャンプ内のDiscord ペアプロ・モブプロ相手募集チャンネルにて

普通のモブプロとは違い、前もってメンバーを集めず、勝手にやってるから勝手に参加してね!という非常に怠惰で傲慢なスタイルになります。なので、参加人数によってはペアプロになったり、最悪1人でやることになります。(おかげさまで今の所、ぼっちは回避)

モブプロのお題はAtCoder Problemsにある過去問のA,B問題を利用しています。

厳密には、不定期開催を謳っているのですが、実質毎週木曜日の16:00~17:00に固定されつつあります。

参加者は平均4〜5名ほどで結構流動的に変わります。(レギュラーもいます) Rubyラクティスを始めたばかりの方からメンターの方まで幅広く参加してもらっていて、初めましての方とも交流できて嬉しいです!

なんではじめたの?

きっかけはざっくり3点

  1. 気軽にワイワイできる環境が欲しい(遊び友達が欲しい)
  2. モブプロの楽しさを広めたい
  3. 何か企画してみたかった

気軽にワイワイできる環境が欲しい(遊び友達が欲しい)

フィヨルドブートキャンプのメンバーにトミー(id:eatplaynap329)さんという方がいるのですが、チーム開発最初のIssueをモブプロ形式でワイワイやってるのが羨ましかったというのがきっかけです。 当時のモブプロ会の感想です。

余談ですが、トミーさんはいい意味でクレイジーなので結局ほとんどモブプロでIssueをやってしまいました。もはや恐怖すら覚えます😱笑

eatplaynap329.hatenablog.jp

モブプロの楽しさを広めたい

現在フィヨルドブートキャンプでは、気軽にモブプロ・ペアプロができる環境にあります。

傾向としては分からないところをメンターの方と一緒にペアプロ(モブプロ)して解決する「問題解決型」のスタイルが主流になっています。(勝手に名付けた)

僕としては、問題解決のためのモブプロだけでなく、何かお題をみんなで一緒に解いていく遊びメインのモブプロも流行って欲しいなーと思って始めました。

これは、昨年にはじめて参加したikumaさん(id:ikmbaer)のモブプロ会が楽しかったという経験からきています。 その会の詳細は以下のブログの10月にまとめてあります。

haruguchi-yuma.hatenablog.com

何か企画してみたかった

フィヨルドブートキャンプでは日々いろんな人がいろんなことを企画しています。 僕は参加することが多かったのですが、自分も何か企画してみたいという思いがありました。

で、何を企画しようかな🤔ということになるわけです。

輪読会は開催してから終わるまでの期間が長く、気軽さの面では少し難しい。 就職してからも続けられる自信がないなどの理由から手軽さを取ってモブプロにしました。

工夫したこと

工夫というほどでもないですが、以下のことにこだわって開催しています。

  • 再現性があること

再現性があること

モブプロの楽しさを広めたいので、誰でも真似できるということにこだわっています。

まず何かを企画した時に、一番気になるのが参加者がいるのか?という問題ですが「勝手にやる」スタンスを取ることで解決しています。

元々自分がやりたかったAtCoderを解くと言うのをテーマにしているので、最悪1人でも当初の目的通りAtCoderの問題を解く練習をすればいいようになっています。

他には、主催者の負担をできるだけ減らすと言うことにこだわっています。 実際ホストの僕がやることは、ほとんどありません。

開催の3日前くらいに「勝手にモブプロやります〜」と声をかけたり、問題を解き終わったら「次誰かドライバーやりたい人いますか〜?」と呼びかけるだけです。

一度始まればみんなと遊ぶだけなんでとても気楽です。

また、お題を考えるのが大変なのでAtCoderというのは良い案だったなと思います。1問あたり15分から30分くらいなのでちょうどいい感じですし、A,Bくらいの難易度だと数学的な知識もほとんどなしで解けるのもポイントです。一度お題を作ってモブプロ会をしたことがあるんですが、作問に3、4時間くらいかかってコンスタントにするのは不可能だなと感じました。

良かったこと

やってから気づいた良かったことがいくつかあります。

  • いろんな人と接点ができた
  • 人前でコードを書くのが恥ずかしくなくなった
  • 人と話す練習になった
  • 自分の考えを言語化する癖がついた
  • コードリーディングする機会が増えた
  • るりまを見る練習になる

あと、本当にはじめての場合は画面共有の練習になったりしていいかもです。

あと、これをきっかけにかは分からないですが、sugieさんはAtCoderの問題を解いたブログをまめに更新していてすごいです。みんなに見てほしい。

sugie.co

最後に

勝手にやってるとはいえ参加者がいて楽しくなっている会ですので、いつも参加してくださる人には感謝です🙇‍♂️

今後の方針としては、AtCoderにこだわらず何かお題を決めて大きめのプログラムを作る回が臨時的にあってもいいかなぁなんて思っている次第です。

「勝手にモブプロ」が気になると言う方は見学だけでも大丈夫なので是非勝手に見学してみてください。