ユユユユユ

webエンジニアです

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 でセコセコ定義していくよりは圧倒的に早く済んだ印象がある。これはこれで気持ちがいい。

任意の累乗数をみちびくアルゴリズム

 例えば Ruby はユーザーが特に意識せずとも無限に大きな整数値を扱うことができる。

jnsato.hateblo.jp

 C++では long long 型を利用してもたかだか 64-bit までしか扱うことができない。ビルトイン型として提供されているのはこれが最大となるから、それより大きな値を扱うには工夫が必要である。

 Project Euler にて 21000 を求める問いを与えられた。Ruby なり Python なりを使えばたやすい問題である。しかしこういった便利な言語に甘えずにアルゴリズムを組むならどうするのがよいか、考えてみた。

 で、書いたのが次のコード。std::deque を利用して、下の桁から各桁の積と繰り上がりを順繰りに計算するアルゴリズムである。

#include <cstdio>
#include <deque>

int main()
{
  int c = 2;
  int e = 1000;
  std::deque<int> p; p.push_front(c);

  for (int i = 1; i < e; ++i)
  {
    int carry = 0;
    std::deque<int> n;
    for (int j = p.size() - 1; 0 <= j; --j)
    {
      int s = p[j] * c + carry;
      n.push_front(s % 10);
      carry = s / 10;
    }
    if (carry != 0) n.push_front(carry);
    p = n;
  }

  std::printf("%d^%d = ", c, e);
  for (auto i : p) std::printf("%d", i);
  std::printf("\n");

  return 0;
}
# 実行結果
2^1000 = 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376

 nm (n < 10) を想定して書いたコードであって、 10 <= n のケースについてはよく考えていない。 n = 10 においては偶然にしてうまく動いてしまうが、それ以上の場合、指数を十分に大きく取るとところどころの桁でオーバーフローが起きるのか、負に転じる桁が発生する。

# 実行結果
11^178 = -196205743-935628574771553105603298049722910150779438188710688145549549083518975740480663061638639633476100897013485981010563604515686537966613714558926634949087465684780460966676028753081

 不完全なコードを掲載するのは気が引ける反面、いまの時点での限界として自分自身のために記録することにする。実際、 21000 を求めるという当初の目標は達成できているので、そのことにまずは満足しておく。

循環小数を検出して表記する

 1/3 や 1/7 のような循環小数を数値型を使って表現しようとすると、普通は有効桁数の範囲でしか表記できない。そうではなく、次のように循環小数であることが一目瞭然となってほしい。

1 / 3 = 0.(3)
1 / 7 = 0.(142857)

 そこで単純に、分子と分母を引数に受け取り、上のような文字列を返すアルゴリズムを書いた。それが以下。

 筆算で除算を解くのと同じ要領で、桁ごとに除算の余りを10倍してはさらに除数で割っていく、というループを作っている。ループの最中に余りが0となればその数は明らかに循環小数ではない。他方で循環小数については、各桁の除算で得られた余りを記録しておいて、同じ余りが現れたらその時点で循環とみなしている。数値型ではなく文字列だから、有効桁数に制限もない。

to_rational(1, 3) // => 0.(3)
to_rational(1, 7) // => 0.(142857)
to_rational(1, 23) // => 0.(0434782608695652173913)
to_rational(727, 53) // => 13.(7169811320754)

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

f:id:jnsato:20200708212711p:plain

 以前のエントリで掲げた、最初の25問を解く目標を達成できた。最初の問題を解いてからちょうど10日での達成だった。これで全登録者のうち上位11.48%の進捗だという。まだまだ先は長いと知りつつも、十分すぎる達成感と自己肯定感を得られている。コラッツ数列友愛数過剰数など、今まで持ち合わせなかった数学の知識を得られたのも満足している。

 全問、C++で解いた。まだ自由に使いこなせるとまではいかないが、手に負えないと思わされる場面はずいぶん減った。RubyPythonワンライナーを書くのも気持ちがいいけれど、ほどよく難しいアルゴリズムをひとりで考えて実装していくのもずいぶん気持ちがいいものだと知るようになり始めている。

 一方で、運よくここまでは挫折せずにこられたとはいえ、運がよかっただけというような気もする。この先にしても、数学についての素養がそもそも足りないだろうから、遅かれ早かれ壁にぶつかるだろうとは思う。ただ、少なくともここはやってこれたし、なにより大いに楽しみながら毎日取り組んでこられた。才能があるとはいえないけれど、楽しみを見出せるだけの素養はどうやらありそうだということを知ることができたのは大きい。

 いつどこでつまずくにしても、この無垢な好奇心は忘れないようにしたい。

7月に取り組むテキストを紹介する

 月が改まり、仕事も入った。時間の余裕は減る。しかし今までが暇すぎたのである。これからは気持ちを入れ替えて、メリハリをつけて勉強に取り組もうと思いを新たにしている。

 で、そのためのテキストをいくつか用意したので、紹介する。

f:id:jnsato:20200703163831j:plain

応用から基礎へ」というスローガンを掲げたうえで、まずはここから学び直すことにした。学生時代にいちどテキストを購入したが、その時は匙を投げてしまったのだった。あらためて取り組んでみて、ひととおりの理解はできているものの、曖昧な知識しか持たない部分はおおいにある。

 これだけの情報量がよく整理された参考書が、どの書店でも一画を割いてプロモートされていること、それだけ間口の広い資格として流通していることは、なかなかすごいことと思う。資格試験で得られる知識なんて実務では使わない! などと強がっていた過去の浅はかな自分を反省しつつ読んでいる。

 春の試験はウイルス対策で中止となったようだが、まもなく秋の試験の申し込みが始まるということなので、それも視野に入れながらまずは通読することを目標に読んでいく。

江添亮のC++入門

江添亮のC++入門

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

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

 入門書はそれこそ山ほど出版されているなかで、著者名とポートレート写真を大きく目立たせて掲げる本書の潔いスタンスはよくも悪くも目についた。著者プロフィールを確認したうえで、信頼がおけると判断してこれを選んだ。

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

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

 これはインターネットで見かけた本で、内容というよりも佇まいの美しさに引かれて購入した。題字のフォントは洗練されているし、表紙のデザインもシンプルながら目に快い。丸善出版から出ていて、しかも第3版だというから、まず品質に間違いはないだろうと思い、安心して購入した。

 55の断章形式で書かれているというので、少しずつ読み進めるにもちょうどいい。いまの実力でどの程度理解することができるかが心配ではあるけれど、長く読める本だと思って付き合っていってみる。

 大判本が多いので、大きさの比較対象として文庫本を紛れ込ませた。とはいえこれも今月の読書リストのひとつである。寝る前に少しずつ読み進めている。

 SFはあまり読んでこなかったけれど、このごろ妙に興味が湧いてあれこれ読んでみている。ウイルスをめぐる科学と人間社会の関わりという意味で、いくぶん身近なリアリティを持って読めているような気がする。

 ソラリスはとりわけおもしろくて困っている。読み進めるのが恐ろしいと思いながら、怖いものみたさでページをめくる手が止まらない。こんな気持ちで読書ができるのはそうないことだと思うし、丁寧に読み終えたいと思っている。