give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

挑戦者求む!Rubyで点字メーカープログラムを作ってみよう 〜Qiita Advent Calendar 2021〜

お知らせ

QiitaのAdvent Calendar 2021の特別企画として、「Rubyプログラミング問題にチャレンジ! -改訂版・チェリー本発売記念-」というアドベントカレンダーをオープンしています。

qiita.com

これは僕が出したRubyプログラミングの「お題」をみなさんに解いてもらい、そのコード解説を記事として書いてもらおう、という企画です。
LGTMが一番多かった人1名と、僕が「このコードは良い!」と思った人1名に、それぞれプレゼントが渡されます🎁

「プロを目指す人のためのRuby入門(通称チェリー本)」の知識があれば十分解ける問題なので、初心者の方も上級者の方もぜひチャレンジしてみてください💪

お題は「点字メーカープログラム」です

僕から出したRubyプログラミング問題はズバリ、「点字メーカープログラム」です!

このプログラムでは以下のように、ひらがなをローマ字で入力すると、それに対応する点字が"o"と"-"で出力されます。

f:id:JunichiIto:20211104084925p:plain

「点字の仕組みなんて知らないよ!」という人でも大丈夫です。以下のサイトを読めば「へ〜、こんな仕組みになってるのね」というのがわかるはずです。

全視情協:点字とは - 点字のしくみ

f:id:JunichiIto:20211104085201p:plain

点字の仕組みはある程度法則が決まっているので、結構プログラミング向きなんじゃないかなーと思って、この問題を作ってみました。

参加方法は動画で詳しく解説してます

「やってみようかな」と思った人は、参加方法を解説したYouTube動画があるのでこちらをチェックしてください👇


www.youtube.com

参考情報:僕は1時間半ぐらいでできました

僕は点字の仕様を理解して、プログラムの実装方針を考えて、実際にコードを書いて完成させるまでにかかった時間は1時間半ぐらいでした。
プログラミング初心者の人はもうちょっと時間がかかるかもしれませんが、チャレンジする人は参考にしてみてください(もちろん、完成までにかかった時間はコードの評価に関係ありません)。

まとめ:チャレンジできるのは先着25名。早めの参加登録を!

というわけで、このエントリではQiita Advent Calendar 2021で開催中の「Rubyプログラミング問題にチャレンジ! -改訂版・チェリー本発売記念-」というアドベントカレンダーを紹介してみました。

参加枠は最大25名でそれ以上増えないので、興味がある人は早めに参加登録することをオススメします。
みなさんからカッコいいコードがたくさん届くのを楽しみにしています〜😄

qiita.com

挑戦者の強い味方は「改訂版・プロを目指す人のためのRuby入門」!?

アドベントカレンダーのタイトルにもチラッと書いていますが、拙著「プロを目指す人のためのRuby入門」の改訂版が2021年12月2日に発売されます。

ズバリそのものの回答が載っているわけではありませんが、今回の「点字メーカープログラム」は本書の例題と同じようなノリで作っているので、プログラムを作っていく流れなどは参考になるんじゃないかな〜と思います。

また、問題の難易度も本書の内容をフル活用すれば十分対応可能なレベルにしてあるので、Ruby歴が浅い人は本書が心強い味方になってくれるはずです。

改訂版の内容については以下のエントリに書いているので、こちらもぜひご覧ください💁‍♂️

blog.jnito.com

PRS McCarty SC (Singlecut) 594というギターを買いました

またまた、新しいギターを買ってしまいました……。PRS McCarty SC (Singlecut) 594というギターです。

f:id:JunichiIto:20211019090321j:plain

最近はめっきり「ギブソン党」になってまして、ギブソン以外のメーカーにはほとんど興味がなかったんですが、YouTubeとかを見てるとよく「PRSはいいぞ」とか「McCarty 594はレスポールに近い」みたいな動画を見かけるので、「じゃあ試しに一回試奏してみるか」と思ったのが運の尽きでした😅

「えっ、何これ?めちゃくちゃ弾きやすいやん!」

「音もめっちゃいい・・・ヤバい、これ欲しい」

と思って、1週間悩んだ末に買ってしまいました 💸

PRSは1年前にも試奏してて、そのときはいまいちピンと来なかったんですけどねえ。あれはアンプが悪かったのか、ダブルカッタウェイだったからなのか、はたまたモデルチェンジ前の初期モデルだったからなのか🤔

今回は楽器屋さんにあった「2016年モデルのDC(ダブルカッタウェイ)」「2020年モデルのDC」「2020年モデルのSC(シングルカッタウェイ)」と、3種類のMcCarty 594を弾き比べました。その中で一番自分の好みの音だったのが、2020年モデルのSCでした(値段も一番高かったんですが💧)。

というわけで、今回のエントリでは僕が買ったPRS McCarty SC594というギターを紹介してみます。

気に入ってるところ

めちゃくちゃ弾きやすい

冒頭にも書いたとおり、めちゃくちゃ弾きやすいです。ネックの握り心地が太すぎず細すぎず、厚すぎず薄すぎず、絶妙なバランス!コードを押さえても良し、ギターソロを弾いても良し、僕にとってジャストフィットでした。McCarty 594はPRSの中でも一番太いネックらしいんですが、僕が持ってる1956 Les Paul Reissueに比べると全然薄いです。

今まで弾いてきたギターは「コードは押さえやすいけど、ソロが弾きにくい(=ネックが太い)」とか「ソロは弾きやすいけど、コードが押さえにくい(=ネックが細い)」みたいな一長一短パターンが多かったんですが、PRSはどっちも完璧です。

あと、シングルカットモデルですが、ボディ裏のヒールが斜めにカットされていて、ハイポジションも楽に指が届くようになってます(僕はそんなにハイポジションで弾きまくる人ではないですが)。

すごく弾きやすいと、練習のモチベーションも爆上がりします。実際、PRSを買ってからギターを手に取る時間がすごく増えました。

チューニングが全然狂わない

レスポールは「ぐいーん」とチョーキングしたりすると、「あれ、ちょっとチューニング狂った?」と思うときがあるのですが、PRSはチョーキングしてもチューニングが全然狂いません。テクニックはないけど、音程の狂いには結構敏感なタイプなので、今までは「チューニングが狂うからチョーキングしたくないなあ」と思うことがあったのですが、PRSだと思い切って「そりゃー!!」とチョーキングできるのがいいですね。

あと、たいていのギターは次の日弾くと微妙にチューニングがズレてることが多い、というかそれが当たり前なのですが、PRSはほとんどズレません。チューニングしようと思ってチューナーで測っても、ほとんどペグを触らずにチューニング終了、ということが多いです。いったいどういうネック構造になってるんでしょうか??

新品で買ったから、という理由もあると思いますが、オクターブ調整とかも完璧です。どのポジションでコードを押さえてもコードがきれいに響くので、非常に気持ちいいです。

音が良い

ギターそのものの音も良いです。ただ、McCarty 594はレスポールを意識したモデルだと思いますが、僕の持ってるレスポールと似ているかというとそこまで似てないかもしれません。

PRSの方が高音の抜けが良いです。低音もしっかり出て、なおかつスッキリしています。それでいて中音域にも厚みがあります。すなわち、上から下まできれいにバランス良く鳴るイメージです。すごくハイファイで「なんかピアノっぽい」と思うこともあります。でもビンテージ寄りの音を目指しているらしいので、冷たい感じではなくウォームさも十分あります。あと、多少雑にストロークしても、ギター側がある程度音量差を整えて前に出してくれるような印象があります。

生音も「シャリーン」と高音がきれいに抜ける感じです。PRSの試奏動画で生音を鳴らしているものがありますが、だいたいどれも「そうそう、僕のPRSもこんな音がするわー」と感じるので、個体差は少ないのかもしれません(このクオリティで大量生産できるのって、いったいどういう技術力なんだ?)。

とはいえ、音だけでいうと、レスポールの図太くて雑味の残った感じの方が僕は好きかもしれません。でもどうなんだろう?PRSのPaul氏は「ビンテージギターの音を目指した」みたいなことを語っているので、ビンテージのレスポールはもしかするとこんな音がするんでしょうか?(そんなわけないか……)

座って弾いたときのバランスが良い

あと、座って弾いたときのバランスがいいのもPRSの良いところです。僕の持ってるギターは重心がややボディ寄りというか、気を抜くとじわ〜っとギターのお尻が下がっていくものが多いのですが、PRSは両手を離しても膝の上でバランスを取ってくれます。これがすごく楽!!バランスが良いと宙に浮いてるギターを弾いてるような感覚になって、弾くときのストレスがひとつ減りますね。

見た目がきれい

PRSといえば見た目が美しいと言う人も多いですね。妻は「すごく高級感がある」と言って結構気に入ってるみたいです。ちなみに僕が持っているのは「10 top」という少し木目が綺麗なモデルです。

f:id:JunichiIto:20211019184237j:plain

ただ、見た目だけでいうと僕はレスポールの方が好きかもしれません。PRSのシングルカットモデルはどうしても「レスポールっぽいレスポールもどき」になってしまうのが、「うーん」という感じですね。それなら本家本元のレスポールの方が貫禄があってカッコいいなあと思ってしまいます。あとボディに傷が付くのが嫌なのでピックガードも付けてほしかったな〜と思います(このデザインに似合うかどうかはわかりませんが)。

イマイチなところ(?)

どれも致命的な問題ではないのですが、あえてイマイチな点を挙げるならこんな感じでしょうか。

  • ボリュームやトーンのノブが軽すぎる
    • ノブが軽いのは一気に0や10に持って行けるようにするためかもしれませんが、僕はそういう使い方をせず、微調整したいときに使うことが多いので、少し重いぐらいの方が調整しやすいです。
  • ボリュームやトーンの目盛り位置を示す「ぽっち」が欲しい
    • 名前がよくわからないのですが、ギブソン系のボリュームやトーンには現在の目盛りを指し示す「ぽっち」が付いてます。僕はこの「ぽっち」を頼りに「ボリュームが8で、トーンが6」みたいな確認をしていました。PRSにはこれが付いてないので、ちょっと不便です。

f:id:JunichiIto:20211020192027j:plain

  • 普通に弾いてるだけでドーピング状態になってしまう
    • PRSは「めちゃくちゃ弾きやすい」&「音のバランスが異様に良い」ギターなので、「自分の実力以上に弾けてしまってるのでは?」と心配になるときが時々あります。いや、全然悪いことではないんですが、他のギターに持ち替えたときに「全然弾けない&出音がバラバラ」みたいなことが起きてしまうかもしれない、と少し心配してます😅

まとめ

というわけで、このエントリでは最近僕が購入したPRS McCarty SC (Singlecut) 594を紹介してみました。

かなり高価なギターですが、「これはさすがに高いだけあるわ」と納得させるだけのクオリティですね。本文にも書きましたが、このクオリティで大量生産できるのがすごいと思います。

「レスポールに近いフィーリングを持ったモダンなギター」を探している人は、ぜひ楽器屋さんでPRS McCarty 594を弾いてみてください〜。

f:id:JunichiIto:20211020193432j:plain

おまけ:594という名前の本当の由来!?

この594というモデル名はギターのスケール(弦長)が24.594インチ(約62.5㎝)であることに由来しているそうです。

24.5インチ・スケールよりもわずかに長い24.594インチ・スケールのネック。これが名前の由来に。

McCarty Singlecut 594

でも、0.001インチって、約0.025㎜なんですよね。

f:id:JunichiIto:20211021064745p:plain


0.01インチ単位なら約0.25㎜なので、まだ「ありえるかな」と思うんですが、0.001インチ=0.025㎜単位で弦長を測っているというのは「さすがにそれはないんじゃないの!?」と思ってしまいます。「日本人女性の髪の太さは、平均約0.08ミリ」という情報があるので*1、PRS社は髪の毛の太さ以下の単位で弦長を測っていることになります(ええ〜っ!?)。

そこでネットを検索してみたらちょっと面白い情報を見つけました。

It takes its name, primarily, from its scale length of 24.594 inches or, as Paul Reed Smith told us earlier this year, "It's a '59 spec guitar with four knobs."

(DeepL翻訳)
このギターの名前の由来は、スケール長が24.594インチであること、そしてポール・リード・スミスが今年の初めに語ったように、「4つのノブを持つ59年仕様のギター」であることです。

PRS McCarty 594 review | MusicRadar

ここでいう「59年仕様のギター」というのは、Gibson レスポールの1959年モデルのことを指しているんだと思います。「59年のレスポールをイメージした4つのノブ(ボリュームx2、トーンx2)を持つギター」だから、594。公式の情報ではないので真偽のほどは不明ですが、僕はこっちの由来の方がしっくり来るなと思いました(苦笑)。

ちなみに僕の持ってる594の弦長を測ったところ、62.2㎝=約24.49インチでした*2。公式のスペックより0.1インチ=約2.5㎜ほど短いですが、これは誤差の範囲でしょう。なので、「McCarty 594のスケールは24.594インチ」というのはまったくのデタラメではないと思いますが、僕が想像するに、最初は「4つのノブを持つ59年仕様のギター」だから「594」という名前を付けて、後付けで「24.594インチだから594です」という由来を考えたんじゃないかな〜と思っています(あくまで個人の推測です)。

*1:人種による違い|髪の知識|花王株式会社 ヘアケアサイト

*2:計測方法はナットから12フレットまでの長さを測って2倍したものです。

僕がRSpecでsubjectを使わない理由

はじめに

僕は折に触れて「RSpecではなるべくsubjectを使わない方がいい」という発言をしています。

ただ、その理由をあまり明確に明文化していなかったので、ここらでちょっとブログにまとめてみたいと思います。

RSpecをよく知らない方へ

そもそもRSpecって何?subjectって何?という方は以下の記事を読んでから戻ってくることをお勧めします。 qiita.com

subjectの使いどころ = メソッドの戻り値をテストするケース

subjectの使いどころについては、以前僕が書いたQiita記事に投稿された、こちらのコメントが的を射ていると思います。

副作用が目的の(procedural)メソッドと、返り値が目的の(functional)メソッド。 返り値が目的のメソッドは、subjectがうまくハマる。

by @akihitofujiwara 2017-09-15 14:36

上のコメントにあるとおりなのですが、メソッドの戻り値をテスト対象にするときはまだ収まりが良いです。たとえば、イメージとしてはこんな感じです。

def greet
  'Hello!'
end

describe '#greet' do
  subject { greet }
  it { is_expected.to eq 'Hello' }
end

上のテストコードはgreetメソッドをテストしています。このメソッドはHello!という文字列を返すメソッドです。このように戻り値を返すメソッドをsubjectにすると、あまり大きな問題はありません。

subjectが向いていないケース = メソッドの副作用をテストするケース

一方で、次のようなメソッドはsubjectには向いていません。

class Counter
  attr_reader :count

  def initialize
    @count = 0
  end

  def increment
    @count += 1
  end
end

describe 'Counter#increment' do
  let(:counter) { Counter.new }
  subject { counter.increment }
  it do
    subject
    expect(counter.count).to eq 1
  end
end

上のテストコードはCounterクラスのincrementメソッドをテストしています。そのため、subjectにはcounter.incrementをセットしました。しかし、incrementメソッドはあくまでインスタンス内で保持しているカウント値を増やすことが目的です(注:Rubyの言語仕様上、加算後の値がメソッドの戻り値になりますが、説明の都合上、戻り値はvoid、つまり戻り値無しとします)。つまりこのメソッドは副作用を起こすことを目的としています。subjectは副作用を起こすメソッドのテストには向いていないため、次のような不自然なテストコードが生まれます。

it do
  # 副作用を発生させる
  subject
  # 副作用の結果を検証する
  expect(counter.count).to eq 1
end

もちろん、ひとひねりすれば次のようなコードを書くこともできます。

describe 'Counter#increment' do
  let(:counter) { Counter.new }
  subject { ->{ counter.increment } }
  it { is_expected.to change(counter, :count).by(1) }
end

これを「美しい」と思う人もいるかもしれませんが、僕からすると「無理矢理がんばってるなあ」感が強いです。あくまで主観の問題かもしれませんが、こういったテストコードは「自己満足」の要素が強い気がするんですよね。で、その自己満足と引き換えに、以下のようなデメリットが発生します。

  • subjectをがんばって使うための、試行錯誤の工数が発生する
  • そのがんばりの結果、摩訶不思議なテストコードが生まれる(RSpec初心者に優しくないし、熟練者が見ても一瞬考えてしまう)

そのため、もしチーム内で「必ずsubjectを使うこと」というコーディング規約があったりすると、上のような問題が頻発しそうな気がします(個人の推測です)。

僕の主張:そもそもsubjectなしで書けばいいじゃない

上で示したテストコードはどちらもsubjectなしで書いても全然問題ないと思います。実際にsubjectなしのテストコードに書き直してみましょう。

最初はメソッドの戻り値をテストする場合です。

describe '#greet' do
  it 'returns "Hello!"' do
    expect(greet).to eq 'Hello!'
  end
end

次に、メソッドの副作用をテストする場合です。

describe 'Counter#increment' do
  let(:counter) { Counter.new }
  it 'increments count' do
    counter.increment
    expect(counter.count).to eq 1
  end
end

別解としてこういう書き方もあります。

describe 'Counter#increment' do
  let(:counter) { Counter.new }
  it 'increments count' do
    expect { counter.increment }.to change(counter, :count).by(1)
  end
end

僕はこういうテストコードでも何も問題を感じません。むしろシンプルかつ素直なので、subjectを使うときよりもコードが読みやすいと感じます。

subjectを好む人の主張に、「テスト対象のメソッドがわかりやすくなる」というものがありますが、describe '#greet'describe 'Counter#increment'でテスト対象を明示しておけば、それで十分明確になるはずです。よって、「テスト対象をわかりやすくする」という目的において、subjectは必ずしも必要ないと考えています。

もうひとつの問題:subjectを使うと視線を上下させなければならない

subjectを使うもう一つの問題点は、subjectを使うタイミングで視線を上下させなければならないところです。上で示したような単純なサンプルコードとは異なり、実務で書くテストコードはどうしても大きくなりがちです。そうすると、いちいち「このsubjectっていったい何だっけ?」というのを確認しに行く作業が発生します。

イメージとしてはこんな感じです。

describe 'Foo#bar' do
  let(:foo) { Foo.new }
  subject { foo.bar }
  context 'when A' do
    before do
      # 何行にも渡ってセットアップが続く
      # .
      # .
      # .
    end
    it { is_expected.to eq 'abc' }
  end
  context 'when B' do
    before do
      # 何行にも渡ってセットアップが続く
      # .
      # .
      # .
    end
    it { is_expected.to eq 'def' }
  end
  context 'when C' do
    before do
      # 何行にも渡ってセットアップが続く
      # .
      # .
      # .
    end
    it { is_expected.to eq 'xyz' }
  end
end

こんなテストコードになっていると、context 'when C'を読む頃には「あれ?subjectって何だっけ??」ということになってしまいます。

テストコードは人間が読むドキュメントのように上から下に読めるようにすべきです。subjectをなくせば、視線を上下させる必要がなくなります。

context 'when C' do
  before do
    # 何行にも渡ってセットアップが続く
    # .
    # .
    # .
  end
  it 'returns "xyz"' do
    # 視線を上に戻さなくてもテストコードが理解できる
    expect(foo.bar).to eq 'xyz'
  end
end

プルリクエストをレビューするときもsubjectの実体がdiffに出てこない(ことが起こりえる)

プルリクエストをコードレビューするときはこんなふうに新規に追加したテストケースだけがdiffに上がってきて、subjectの実体はdiffに出てこないこともあるかもしれません。

   end
+  context 'when C' do
+    before do
+      # 何行にも渡ってセットアップが続く
+      # .
+      # .
+      # .
+    end
+    it { is_expected.to eq 'xyz' }
+  end
 end

subjectを使わなければそのテストで何の結果を検証しているのか、diffを見るだけで理解できます。

   end
+  context 'when C' do
+    before do
+      # 何行にも渡ってセットアップが続く
+      # .
+      # .
+      # .
+    end
+    it 'returns "xyz"' do
+      expect(foo.bar).to eq 'xyz'
+    end
+  end
 end

応用:subjectの代わりにrspec-parameterizedを使う

かつては僕もたまにsubjectを使っていました。それは次のようなテストコードを書く場合です。

describe 'Ticket.calc_fee' do
  subject { Ticket.calc_fee(age) }
  context '子どもの場合' do
    let(:age) { 10 }
    it { is_expected.to eq 500 } # 半額
  end
  context '大人の場合' do
    let(:age) { 20 }
    it { is_expected.to eq 1000 } # 通常料金
  end
  context '老人の場合' do
    let(:age) { 65 }
    it { is_expected.to eq 0 } # 無料
  end
end

これはメソッドに渡す引数をcontextごとにいろいろ変えながら、メソッドの戻り値を検証するテストコードです(「同値分割・境界値分析」みたいなテスト技法の話はこのエントリの本題ではないため、ここでは無視します)。

ですが、こういったテストコードはrspec-parameterized gemを使った方がシンプルに書けます(余談:gemなしで使えるようにRSpecの標準機能に取り込んでほしい〜)。

describe 'Ticket.calc_fee' do
  where(:age, :fee) do
    [
      [10, 500],
      [20, 1000],
      [65, 0]
    ]
  end

  with_them do
    it 'returns fee by age' do
      expect(Ticket.calc_fee(age)).to eq fee
    end
  end
end

rspec-parameterizedすら使わず雑に書く

上のような例であればそもそもsubjectもrspec-parameterizedもいらないかもしれません。

「デグレを防止できる」「APIドキュメント代わりになる」という要件を満たすのであれば次のようなテストコードでも必要十分だったりします。

describe 'Ticket.calc_fee' do
  it 'returns fee by age' do
    # 子どもの場合
    expect(Ticket.calc_fee(10)).to eq 500
    # 大人の場合
    expect(Ticket.calc_fee(20)).to eq 1000
    # 老人の場合
    expect(Ticket.calc_fee(65)).to eq 0
  end
end

最近はこういうスタイルで書くことが多いので、僕が書くRSpecのコードではsubjectが登場する機会がほとんどなくなりました。

参考:RSpecのメンテナから見たsubject

日本語には翻訳されていませんが、RSpecの元メンテナ・Myron Marston氏が執筆した"Effective Testing with RSpec 3"という本があります。

本書を読むとsubjectについて次のような注意書きが書いてあります。

We recommend you use this one-liner syntax sparingly. It’s possible to over-emphasize brevity and rely too much on one-liners.

(僕の日本語訳)
ワンライナー構文(訳注 it { is_expected.to eq 'Hello!' }のような構文)は控えめに使用することをお勧めします。この構文を使うと簡潔さを過剰に求めてしまい、大量のワンライナーが量産されてしまう恐れがあります。

本書に書かれているsubjectに関する注意点は、以下の翻訳記事でもほぼ同じ内容が語られています。

qiita.com

私はsubjectやワンライナー構文(例: it { is_expected...})を使うことはほとんどありません。特定の分野のプロジェクト(例:mustermann)ではワンライナー構文はうまく機能しますが、大半のプロジェクトではあまり有益なテクニックではないと考えています。

【翻訳】RSpecのリードメンテナだけど何か質問ある? - Qiita

まとめ

というわけで、このエントリでは僕がRSpecでsubjectを使わない理由をつらつらと書いてみました。

このあたりは個人の好みで大きく分かれるというか、もはや宗教に近いものを感じることもよくあります。「絶対subject使うマン」から見ると、僕の主張はどれも眉をひそめる話ばかりだったんじゃないかと思います。なので、「subjectを使った方がいい!」と思っている人はそのまま使い続けても構いません。

「subjectを使いたくない」と思っていても、既存のプロジェクトにあとから参加した場合は悩ましいですね。既存のテストコードが「subject使いまくり」だと、テストコードの一貫性の面でsubjectを無くすのは難しいかもしれません。その場合はチーム内でテストコードの方針を話し合ってみてください。

あわせて読みたい

テストコードをテクニカルに書くために時間をかけすぎるのは不毛だと僕は考えています。 qiita.com

テクニカルなテストコードを避け、上から下に読めるテストコードを書きましょう、という話をここに書いています。 qiita.com

本文でも引用した"Effective Testing with RSpec 3"という書籍の書評です。 blog.jnito.com

[PR] RailsですらすらとRSpecのコードが書けるようになりたい!という方はこちらの電子書籍をどうぞ💁‍♂️(僕が翻訳しています) leanpub.com