ユユユユユ

webエンジニアです

背伸びして『Effective C++ 第3版』を読んだ

 ある Googler の投稿を読んでいて、見かけた本である。題字のフォント、表紙のデザインなど、飾り気はないのに力強いオーラを持つ装丁がただちに印象に残った。名著という評判も高いおかげで安心して読むことができたし、翻訳も丁寧な印象で、自然に読める心地よさはありがたかった。

Effective C++ 第3版 (ADDISON-WESLEY PROFESSIONAL COMPUTI)

Effective C++ 第3版 (ADDISON-WESLEY PROFESSIONAL COMPUTI)

 どのページでも実践的な開発の勘所を完結に提示してくれて、知識として興味深買った。例えば「メンバ関数カプセル化を弱める」という観点。メンバ関数は private なデータにアクセス可能であるから、カプセル化の観点からは非メンバ関数の方が安定するのだという。自分ではたいしてこだわりも持っていなかったが、言われてみれば理にかなっていて、「明日から使える知識」として印象に残った。

 ただ全体としてはあくまで、C++という言語を主題としているわけだから、そもそも入門したての僕にとってはいくぶん背伸びした内容だったことは否めない。仕事としてC++のプロジェクトに携わっているわけではないから、読んで得た知識を実践にフィードバックする機会もそう多くない。つまり知識の定着という意味でも、短期的に成果を得るということはあまり期待できないということで、いくらか残念に思う。

 現状の僕にとってのC++の用途は、多くとも数百行程度のコードで計算をさせるくらいの使い方にとどまっている。使い捨てのクラスは書いても、長く使われるクラスを設計したことはない。そういった経験不足のせいで、この本を十分に味わいつくすことはまだできないようだ。

 一方で、これからより多くを学んでいくにつれて、わかることがどんどん増えるのだろうなとは期待してしまう。そのためにはますます意識的にテクニックを身につけていけないけれど、そのさきでこそ再読すべき本がこれなのだと思う。そう思って、その機会がやってくるまでひとまずこの本はしまっておく。

『江添亮のC++入門』でC++の基本を学び直す

 競技プログラミングに触れるようになって、自己流ながら少しずつC++が使えるようになってきた。しかしきちんとした参考書を読んだことがなく、体系立てた学習もしたことがないのが気にかかっていた。学習といえば、AOJの教育コンテンツを少しやったばかりで、そのあとは実践問題を解きながら必要に応じて学んできた程度にすぎなく、それでは物足りないと思い始めて、入門の参考書をやってみることにした。

江添亮のC++入門

江添亮のC++入門

  • 作者:江添 亮
  • 発売日: 2019/09/20
  • メディア: 単行本

 数ある参考書のなかでこれを選んだ経緯について、初めて書影を目にしたときは、いくぶんギョッとさせられた。よくも悪くも潔い印象はある。正直にいって、はじめ手にとったときはこの装丁に圧倒されて、すぐに書棚に戻してしまった。しかし多くの入門書があるなかで、どれを選ぶかと判断しなければならないときに、実名と肖像を大きくフィーチャーしているこの本は、著者としての自信と覚悟がもっともあらわれているという点で強いフックになった。

 文章について、教科書や参考書というよりはエッセイに近い文体で書かれている。手取り足取りというような親切さはないが、付かず離れずの距離感で読者を先導してくれる印象はあった。リズムに慣れる必要はあるものの、慣れさえすればそう気にはならない。

 内容の方に目を向けると、C++についての記述に先立って、 GCCMakefile の説明を一通りカバーしてくれているのは親切に感じた。C++について自己流で学んできたのと同じように、ビルドについても見よう見まねで覚えてきたため、これらのトピックを付録ではなく冒頭で解説してくれるのは、僕にとってピッタリの進行であった。欲をかいていうと、 cmake がなんであって、どう使うのか、なんかもついでに教わりたい気持ちはあった。とはいえひとまずは make を覚えて手に馴染ませなさい、ということなのだろう。

 中盤の「アルゴリズム」の章で、それまで学んだ知識を動員して、標準ライブラリに含まれるような典型アルゴリズムを実装するのもいい教材だった。先行する章で学んだばかりのイテレータの知識を利用して、 for_each, find, count といったアルゴリズムが実装可能であると、手を動かしながら知ることができる。その過程では、これら標準ライブラリに含まれた関数が決してブラックボックスではなく、基本の知識だけでも十分実装可能なものである、という大事な認識も与えられた。「繰り返し処理には for_each を使いましょう」というような、すでに御膳立てされた道具の使い方を教える方式ではなく、その実装まで付き添って教えてくれるのは、非常に善意のある教え方だと印象に残った。

 ポインタの解説についても、「意味としてのポインタ」と「文法としてのポインタ」をよく区別しないまま捉えてしまうことが学習者の混乱の原因であると看破して、よく整理して解説してくれている。先に学んだリファレンスとの比較において、ポインタは機能としては大きくかけ離れたものではないとした上で、複雑に見えるのは文法が難しいに他ならない、としてくれるのは、安心感も納得感もある。要するに、ポインタがわからないと嘆くのは、ポインタの概念がわからないのではなく、文法を覚え切れていないということに他ならないのだろう。自戒も込めつつ、これには実感がある。

 と、ここまでは実践も含めてたいへん満足いくものであった。他方で、その先のトピックではちょっと置いてきぼりにされてしまったきらいがある。例えば、それまで C++17 を前提に進めてきた演習に、突然 C++20 の機能を使う、と宣言される場面があった。このため手元のコンパイラapple clang 11.0.3)でコンパイルできなくなってしまった。コンパイル手段を検討したり、C++17 で実装し直すのは、つまらない作業ではなかったが、学習スピードとしては著しく低下してしまった。

 ほかにもコンパイラの相性のせいなのか、サンプルコードをうまく動かせない場面はちらほらあった。ごまかしながら進めていたが、なんとなく動かして満足するのでは学習として意味がないなと思って、手を動かしながらじっくり読むのはやめにした。逐一検証していくことはせず、気になる部分だけを書き写して実行してみるほかは、ザッと読んで参考にするだけに留めておいた。

 というわけで、教材としてはおおむね満足しているが、後半、解説が駆け足になるにつれて、置き去りにされてしまった気分は否めない。入門というには高度に思われる話題も少なくない気がしたが、それ自体はそう悪いことではないと思う。それは言うなれば、大学の講義なんかで教授の話を一生懸命聞いているものの、だんだんと内容が発展していき、気づくとついていけなくなってしまっている、というようなものだと思う。そして経験上、こういったケースではあとになって教授の含意に気づく瞬間がふと訪れるものだから、その時までじっくり待てばいい。

 要するに、わからなかった箇所もちらほらあるが、わかる部分が皆無だったわけでは決してない。そして事実、新しく学べたことはたくさんあるのだから、まずはその新しい知識を応用できるようになることを目指すのが筋と思う。そのうち、わからなかったこともだんだんわかるようになってくるはずだし、そのようなときに、ここになにが書かれていたかを思い出して、適当に読み返せられればそれでいいだろう。

『キタミ式イラストIT塾 基本情報技術者』を読んだ

 大学生であったおり、就職活動に合わせてこの参考書を手に取ったことがある。そしてほとんど2割も読まずに匙を投げた。資格の存在を知るより先にプログラミングのアルバイトを始めていたから、資格試験の勉強では死んだ知識しか身に付けられない、と粋ぶっていたのだと思う。

 インターン先としてもぐり込んだある企業では、2つ年下のインターン生が応用情報技術者を持っているということがあった。すごいな、とは思ったけれど、そんな彼と同じ待遇で実務をさせてもらえるに足るだけのアピールを自分は持てているのだという自尊心を育てるだけで、相変わらず資格勉強は低くみていた。

 まあ、そのときにはそのときの視野の狭さがあったということで、それについてくどくどと書いても仕方がない。それよりも、かつてはひどく退屈に思われたのと同じテキストが、いかに体系づけられ、よく整理され、網羅的な参考書であったかということに新鮮な思いで気づけたということ、そのことを喜びたい。

 実に、計算機の基本をいかに知らずにいたかを繰り返し教えられた。半加算器と全加算器は言葉すら知らなかったし、補数を利用して減算を実現するというアイデアは、自分でそれを発想することはまず不可能だが、言われてみれば単純明快でたいへん気持ちがいい。クイックソートヒープソートまでカバーされていることには驚いたが、世間的にはこれらさえ基本の範疇なのだろうと襟を正す気分にもさせられる。

 要するに、情報技術者としての僕はまだまだ基本未満なのである。そしてそれを知って、かえって晴れやかな気分になれているところに僕の成長がある。

 最初の仕事に就くまでは、「実務経験」のような言葉を振り回して自分を大きく見せるのはひとつの戦略であった。資格試験を軽んじていたことは否めないが、それは自信のなさを隠す手段としての行為であったような気がしている。基数変換になんの意味があろうか? と息巻く様子は、数学嫌いの中学生のような態度だったろうと思う。しかしまさにイニシエーション的という意味合いで、それは僕が学生から社会人に足を踏み入れるための儀式として必要だったのではないかなともいまでは思っている。

 いま、「応用から基礎へ」と自分のテーマを定めてようやく、この資格のありがたみを知った。情報科での教育のない僕のような者にとって、これだけ網羅的に「基本」を学べるチャンスが、これだけアクセスしやすく提供されていることは幸せなことである。ネットをみわたすと python だとか aws の講義が無料で受講できたりして、それはそれで豊かな学びにはなるだろうが、電気信号レベルでの話から丁寧に、かつ体系的に学ぶ機会というものはなかなかない。それはキャッチーでなかったり、フックがないから、ということにはなるが、そうした技術をこそ「枯れた」と形容すべきなのだろう。はっきりいって、基本情報のテキストは「枯れた知識」のオンパレードである。墓場とみなすか甘味箱とみなすかは受け手次第である。

 この一冊をザッと眺めただけで学んだとするのも傲慢であるし、そんなことをいう気はない。しかしコンピューターを語るための基礎のボキャブラリーをひととおりインプットして、これでようやくスタートラインに立てた、というような実感はおおいにある。たとえば少し前まではちっとも使おうとすら思わなかったビット演算を、いまや当たり前のように使いこなせるようになっているところに、ほかならない自分の変化を見出すなどしている。

 仕事としてプログラムを書くうえで、それがどう便利にはたらくかはわからない。実際、低レベルの知識が要求される仕事をしているわけではないし、ここで得た基礎知識が仕事に直結するとは言わない。しかしそれでいいとも思っている。かつて退屈していた知識にあらためて向き直して、そのおもしろさに気づくことができたこと、そのことがなによりも自分の成長を感じさせてくれるし、この「おもしろい」をさらに掘り下げていくと、いったいそこにはどんな世界があるのだろうとわくわくさせられる。

 ひとまず出題範囲をひと巡りすることができたことになる。すでに持ち合わせている知識も多いから、わざわざ受験するまでのことはないようにも思うが、それでも僕は秋の試験を受けようと考えている。基本情報なんていまさらとっても履歴書に書いてどうなるわけでもないし、投資対効果という意味ではたいしたものにはなるまい。はっきりいって、そういった世俗の利益計算とは関係なく、試験そのものを目的として勉強したいという気になっている。誰に命じられるでもなく、またなんの利益があるわけでもなく、ただそれ自体を目的として学ぶ。ストイックなようでもいて、富豪的なようでもあるが、いずれにしても、そのプロセスを通り抜けた先に、また新しい自分を発見できるような予感がある。予感があるから、それに従おうという、それまでの話となる。

Project Euler で最初の50問を解いた

f:id:jnsato:20200715215813p:plain

 最初の25問を解いたのはちょうど一週間前であった。

jnsato.hateblo.jp

 ペースが速くなったのは、おもしろくて手が止まらなくなってしまっていたため。絶妙な難易度のおかげで、あと1問だけ解いたら寝る...解けた...寝る前に次の問題もみてみよう...やっぱりもう1問やってから寝よう...という方式に病みつきになっていた。

 最初の25問のほうが難儀だった印象があるのは、巨大な整数を扱うのに苦しめられたからだろう。例えばこの問題Python ないし Ruby を使えばワンライナーで答えを求められる。しかし C++ にこだわって解法を求めたせいで、必要以上にことを難しくしていた。

 最初の25問を過ぎると、正答者の数はどんどん減っていく一方で、難易度としてはむしろ易しくなったように感じた。ほとんどの問題は愚直な全探索で解けたように思う。それでも一般的にはそれなりの難易度ということになるのだろう。個人的には、簡単すぎず、難し過ぎないあたりの、絶妙に取り組みがいのある難易度であった。

 おもしろいので、無理なく続けられるのも嬉しい。そろそろ挫折しそうな気配はあるが、そのときこそ新しい学びのチャンスと思ってのびのびと継続したい。

C++の整数リテラルに桁区切りを加える

 Ruby であればこういう書きかたが許されている。ゼロを列挙するよりもはるかに視認性が高く、お気に入りの記法である。

a = 1_000_000_000
a
# => 1000000000

 C++ を書くとき、ここにほのかなもどかしさを感じることはあった。1000000000と書くのは読みづらいし、かといって std::pow(10, 9) と書くのもスマートとは思えない。

 試行錯誤の結果、桁数をわかりやすくするためだけに、 1001001001 のように必要な数値よりちょっと大きめに定義したりしていた。それでもなお不格好な感じは残り、喉に刺さった小骨のように気になっていた。そこへその小骨を取り除く方法を発見した。こう書けばいい。

int a = 1'000'000'000;

 要するに、 Ruby で整数リテラル内にアンダースコアを使えたように、 C++ ではシングルクオーテーションを使うことができるわけだ。

 もちろん3桁ごとに区切らずとも、任意の箇所に挿入することができる。たとえばこうすれば、二進数の先頭が符号ビットであることを示す、という意図にも使える。

int b = 0b1'1111101;

 まあ、要は伝え方の問題でしかなく、読んでくれる人に配慮がなければただの自己満足でしかない。そうであってもやっぱりこう書くほうがおさまりがいい、というのはあるから、こう書いていきたいなとは思う。

rmagick をビルドする

 入社初週の恒例行事として既存プロジェクトの環境構築をしていた。

 幸いにしてほとんど障害なく進められたのはありがたい。このエントリに書くことさえ、大したことではない。しかし安易に検索エンジンに頼ったりせずに、自分の頭で考えてビルドを通せたことに、自分自身の成長を感じられて感慨があった。それがずいぶん嬉しかったので、記念に書き残しておく。

 さて、問題としてあったのは bundle install 時の rmagick のビルド失敗である。具体的にはこう。

% bundle install
Fetching rmagick 2.16.0
Installing rmagick 2.16.0 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
Package MagickCore was not found in the pkg-config search path.
Perhaps you should add the directory containing `MagickCore.pc'
to the PKG_CONFIG_PATH environment variable

 思えば数年前にも見たような覚えがある。で、そのときは思考停止して StackOverflow を漁っては出てくる回答をめくらめっぽうに実行して、うまくいくのを祈っていた。これも妙にクリアに覚えている。

 しかしわざわざ検索エンジンに頼らなくても、ここで必要な情報はきちんとフィードバックしてくれている。それはこの部分。

Package MagickCore was not found in the pkg-config search path.
Perhaps you should add the directory containing `MagickCore.pc'
to the PKG_CONFIG_PATH environment variable

 pkg-config は imagemagick の依存先としてインストールされるライブラリであるはずなので、それを調べてみる。するとこちらにも親切で明快なインストラクションが与えられていた。

% brew info imagemagick@6
...
For pkg-config to find imagemagick@6 you may need to set:
export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig"

 つまりこう環境変数を設定してあげればいいだけのようなので、そうする。その上で bundle install をリトライすると、万事オーライであった。

% export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig"
% bundle install

 正直にいって、 rmagick のコードを読んだことはないし、 imagemagickAPI もろくに知らない。 pkg-config についてはなにをやっているライブラリなのかすら知らない。それでいてビルドができたというだけで喜ぶのもおかしな話かもしれない。

 しかしこのあたりの些細なことがらに足を取られないということは、それだけスピード感を持って「利益を生み出す開発」にシフトできるということでもあるから、そこは自己評価してあげてもいいのかなとも思う。

ローカルの postgres を 12.3 に移行する

 今週から新しいプロジェクトに参加することになった。 Rails + postgres の構成で動くアプリケーションの環境構築をした。

 ミドルウェアを使うときはコンテナで実行するのが手癖になっていた。しかし今回は「無理して Docker を使わないこと」のポリシーに沿うことにした。実際、プロジェクトの依存関係の知識を持たないまま docker で環境構築をしようというのは、どれだけ時間がかかるか見積もれない。

 こうして久しぶりに postgres を macOS で動かして使うことにした。そのときの記録を書く。

TL;DR

% brew postgresql-upgrade-database

 みると brew でインストール済みのようだったので、まずはバージョンを確認する。

% postgres --version
# => postgres (PostgreSQL) 12.3

 これをそのまま起動してみようとすると、以下のようになる。どうやら postgresql@11 で作成したファイルが残っており、 postgresql@12 では起動できない様子。

% postgres
2020-07-06 05:39:58.529 CEST [91445] FATAL:  database files are incompatible with server
2020-07-06 05:39:58.529 CEST [91445] DETAIL:  The data directory was initialized by PostgreSQL version 11, which is not compatible with this version 12.3.

 おそらくどこかのタイミングで、考えなしにメジャーアップデートを受け入れてしまっていたよう。使っていなかったものだからここまで気がつかなかった。

 何か情報がないかと brew info に尋ねてみる。と、幸いにもすぐに見つかった。

% brew info postgres
...
==> Caveats
To migrate existing data from a previous major version of PostgreSQL run:
brew postgresql-upgrade-database
...

 ドンピシャだから、これを実行して、ふたたび起動してみる。

% brew postgresql-upgrade-database
% postgres
2020-07-06 12:49:00.620 JST [93259] LOG:  starting PostgreSQL 12.3 on x86_64-apple-darwin19.4.0, compiled by Apple clang version 11.0.3 (clang-1103.0.32.59), 64-bit
2020-07-06 12:49:00.621 JST [93259] LOG:  listening on IPv6 address "::1", port 5432
2020-07-06 12:49:00.621 JST [93259] LOG:  listening on IPv4 address "127.0.0.1", port 5432
2020-07-06 12:49:00.622 JST [93259] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2020-07-06 12:49:00.634 JST [93260] LOG:  database system was shut down at 2020-07-06 12:48:31 JST
2020-07-06 12:49:00.638 JST [93259] LOG:  database system is ready to accept connections

 こうして無事にローカルの postgres が利用可能になった。やや面倒ではあったが、それでも docker でセコセコ定義していくよりは圧倒的に早く済んだ印象がある。これはこれで気持ちがいい。