下記のごとく毎月定例の江添ボドゲ会を開催します。
2018-05-31
2018-05-29
世の中にはプログラミングを理解できない人間が存在する
現在、C++によるプログラミングの入門書を書いているので、初心者のプログラミングの学習過程にとても興味がある。私自身も初心者の気持ちを取り戻すためにHaskellを学んでみた。最初の数日は頭が痛くなるほど難しかったが、そこを過ぎてみれば後は楽になってしまった。結局、初心者の気持ちはあまりわからなかった。結局、プログラミングの基礎はすでに学んでしまっているので、
先日、FizzBuzzがわからないから教えてくれという知人がいたので、これは初心者の気持ちを知るいい機会と話を聞いてみたところ、想像を絶する世界が見えてきた。
まずこれが動かないと悩んでいたコードだ。
for ( int i = 0 ; i <= 100 ; i++ )
{
}
else if ( i % 15 == 0 )
{
Debug.log("FizzBuzz") ;
}
else if ( i % 3 == 0 )
{
Debug.log("Fizz") ;
}
else if ( i % 5 == 0 )
{
Debug.log("Buzz") ;
}
else
{
Debug.log(i) ;
}
一見するとそれらしいソースコードのようにみえるが、そもそも文法が間違っている。文法が間違っている箇所を指差しても納得しない。for文の文法を説明しようとするが聞く耳を持たない。「これが間違っているなら参考にしている教科書も間違っている」との一点張りで誤りが存在していることを頑なに認めない。参考にしているらしいスライド資料にのっているコード例と比較させても間違いの箇所を判断できない。for文の文法の説明をもう一度試みるがやはり聞く耳を持たない。
そしてソースコードの見た目を適当に書き換えてなんとか問題を「修正」しようと試みる。私はいつシェイクスピアの名作が出来上がるだろうかと考えながらその様子を見守っていた。
ややあって、奇跡的にソースコードが適切な形に「修正」された。しかし出力は期待通りにならない。当然だ。なぜならこれはUnityで、使っているのはデバッグログ用の機能であって、重複は省かれるからだ。出力は、1, 2, "Fizz"というメッセージが何件, 4, "Buzz"というメッセージが何件, 7, 8...と続く
そもそも基礎的なプログラミングの知識が十分ではないのだから、Unityはやめてもっと罠の少ないCLI環境でプログラミングの基礎を学ぶべきだと諭しても、「Unityは私がやりたいことを実現できる環境であり私のモチベ維持のために重要であるからUnityでやる」といって改めようとしない。
これは一体どうすればいいのだろう。こういう人間は教育不可能だ。この人物の経歴を見るに、どうもそれらしく見えるモックアップをでっち上げてきたアート系の人間のようだ。本人は今の時代は科学とアートは同じものでありアートを融合することでユーザービリティを考慮したUIが云々などと、まるでピタゴラス派のようなことを言う。世界は人間にとって美しい数字とか法則で定義されるべきであり、定義が観測結果に従わないとしても定義は正しいと主張したのがピタゴラス派だ。アートに引きこもっているならそれでいいのだが、それは科学ではない。コンパイラーのソースコードの解釈はこの自然界には珍しいことに冪等性を持っているのだから、見た目をでっち上げるのではなくて本質を理解すべきなのだ。たとえその本質が自分の美学に合わなかったとしてもだ。私は本棚にあったアラン・ソーカルの著書、知の欺瞞を手に取らせてみたが、あまり興味は示さなかったようだ。
こういう人間によって書かれたソースコードは一見もっともらしく見えるが、文法違反の間違ったコードとなる。どこかからか発見してきたコードをコピペしてツギハギしてそれらしい見た目のソースコードをでっち上げる。人間相手であれば、いかにも主題について理解したかのようにもっともらしくでっち上げた小論文を書いて読ませれば筆者は主題を理解していると読者を騙すことは可能だが、ことコンパイラーが相手では文法違反のソースコードを騙すことなどできない。
思うに、先天的に、あるいは幼少期の教育の結果、プログラミングに向かない学習方法に最適化されてしまった人間がいるのではないだろうか。物事の本質を完全に理解するにはコストがかかる。しかし、不完全な理解だがそれらしいものをでっち上げるにはコストがかからない。そして、人間には、それらしくでっち上げられた偽物と、本質を理解した上で作られた本物を判別するのが難しい。一方コンピューターは違う。コンパイラーはソースコードがいかに本物のソースコードらしい見た目をしていようとも、文法違反のソースコードを判別できる。コストを書けずに結果を出すためにそれらしくでっち上げる学習方法に最適化されてしまった人間は、コンパイラーに対して、それらしくでっち上げられたソースコードを食わせてコンパイラーが騙されてくれることを期待する。しかしコンピューターは騙されない。そもそも騙しようがない。コンパイラーは定義済みの規則に則って判断するだけで騙すという概念すらないのだから。
2018-05-17
ポルノ本意制の通貨が登場したことにより今後予想される問題について
ポルノ動画サイトの最大手であるポルノハブが仮想通貨Vergeによる支払いを受け付けているそうだ。
世界最大のポルノサイトが仮想通貨「verge」に対応、アクセスしてみるとびっくり! ポルノ業界が急激に進化中!
これはとても面白い事象である。というのも、この通貨はポルノと交換できることがポルノハブによって保証されているわけだ。すなわち、これは金本位制ならぬポルノ本位制である。しかもこの話は単なる空想に終わらない。ポルノハブは実際にこの通貨とポルノの交換を行っているわけで実態がある。ここにポルノ本位制の通貨が登場した。
ポルノ本位制の通貨が存在する社会はどのように変革していくのか、ポルノ本意制の通貨を使うときには何に気をつければよいのか。我々人類は通貨と経済学について長い歴史があるので、歴史から学ぶことで今後の動向は予測できる。本記事ではポルノ本位制の通貨がもたらすシュールな世界を紹介しよう。
ある通貨はポルノと交換できることが保証されているとして、一体どのようなポルノと交換できるのだろうか。ポルノはどのようなものであってもポルノであり、したがってポルノとしての価値を持つはずだ。質の高いポルノを生産するのにはコストがかかる。すると、世の中には質の悪い製造が容易なポルノが量産されるだろう。通貨と交換品の価値に差が生じた場合、人々は通貨と交換するポルノには質の悪いポルノを使い、質の良いポルノは貯蓄するようになる。これを「悪ポルノは良ポルノを駆逐する」といい、グレシャムの法則とも呼ばれている。
経済学的にポルノの価値を考えると興味深い事情がある。ポルノの価値はまだ誰も見たことのない市場に出回っていない新作ほど高い。みんな見たような市場に出回りすぎたポルノは価値が低い。これを考えると、ポルノは金のように価値が安定しておらず、消費財である。しかし金の生産量は簡単に増やせないが、ポルノの生産量は裸の人間とカメラさえあればいくらでも生産できる。しかも一人の人間に複数のカメラを使うことで並列生産も可能だ。中には車とドラゴンをポルノだとみなす人もいるが、ドラゴンの用意はやや難しい。最近は地図上にもドラゴンの住まう地と書かれる土地が少なくなったからだ。
ポルノ本意制の社会では、実経済に出回っている通貨がいつポルノとの交換を要求されるかわからない。そこで、銀行は十分な量の価値の高い未公開ポルノを戦略的に備蓄しておく。これを準備ポルノという。
なぜ実経済が存在するポルノより多い通貨を動かすようになるのだろうか。それは借金の存在のためだ。まだポルノを所有していない人間が、将来ポルノを所有する信用によって通貨を借りる。その通貨でポルノを生産し、借金を返す。このとき、生産したポルノによって借金以上の価値が生まれれば得をする。ところで、借金をしたときまだポルノは世の中に存在していなかったわけだ。しかし借金をしなければポルノは生産されなかった。したがって、実経済では常に準備ポルノ以上の通貨が動いているのだ。
現実には、市場に出回る通貨すべてを一斉に換ポルノしたときの需要を満たすほどの準備ポルノを用意することはできないだろう。皆が一斉に通貨とポルノ交換、すなわち換ポルノを要求したとき、取り付け騒ぎが起き市場が混乱する。このような取り付け騒ぎはめったに起こらないが、たまに些細な噂が拡大解釈されて発生することがある。例えば女子高生が何気なくつぶやいた、「最近ポルノハブ危ないんだって」という言葉からポルノの取り付け騒ぎが発生し、準備ポルノの少なかった弱小ポルノサイトが閉鎖に追い込まれることは十分にあるだろう。そのような自体に陥った場合、まず自殺したと噂される理事長自ら生存を主張し、巨大スクリーンでポルノを映すことにより準備ポルノは十分にあることをアピールすべきだ。
不況時には、ポルノが十分に国内に供給されない。このとき、国内にすらポルノ供給が足りていないのに海外にポルノを輸出するのはけしからんという極めて素朴で単純な考え方が国民に芽生え、そのような狭い思想の政治家が当選する。結果として、国家はお互いにポルノ輸出を禁ずるようになり、各国が経済的に鎖国し始める。
こうしたことから、ポルノ本意制はいずれ破綻し、廃止しなければならなくなるだろう。どうやって廃止するのかを歴史に学んで今から対策しておこう。国家は通貨とポルノの法定為替を一方的に定め、国民の所有するポルノ財産をすべて法定為替で強制的に買い上げるかもしれない。
そして、今と同じようにポルノと通貨は変動為替に移行し、取引所に直結したアルゴリズムトレード勢がマイクロ秒単位でポルノ価値を判断して先物取引をするようになるだろう。
他にも面白い思考実験として、フィクションの世界では水本位制がある。これはFalloutというゲームの世界の設定だ。全面核戦争の後に清潔な飲料水が貴重になり水が価値になった。しかし水を持ち運ぶのは手間なので、ある有力な水商人が水とボトルキャップを交換するようになった。なぜボトルキャップなのかと言うと、もはやボトルキャップを製造する技術や設備は存在しないからだ。つまり、ボトルキャップは偽造が実質不可能で、全面核戦争前に製造されたものしか残っていない。偽造が不可能であれば貨幣として使うには十分だ。
2018-05-14
OpenBSD、1985年に追加されたIntelの最新の誇大広告された機能を使わないことにより脆弱性を華麗に回避
“We didn't chase the fad of using every Intel CPU feature” | Hacker News
前回の記事であるIntelの古いマニュアルを誤読したために生じた脆弱性では、IntelのCPUがスタック切り替えるためにss/spレジスターをアトミックに更新する汚いハックとして、ssレジスターが変更された直後の1命令は割り込みが遅延される古い仕様があるが、多くのOSはこの古い仕様を把握していなかったため、ssレジスターを変更した直後の1命令でカーネルモードに入り、かつハードウェアブレイクポイントが設定されたことにより割り込みを起こせば、カーネルモードに入った直後にカーネルのコードを1命令たりとも実行していない状況でカーネルモードとしてユーザーの割り込みが実行される脆弱性を引き起こしていた。
さて、各OSが対応に追われるなか、セキュリティに万全の体制を取ることで定評のあるOpenBSDでは特に何事もなくのほほんとしているので、MLにこのことについて質問をするものがいた。
OpenBSDが影響を受けないという理由について教えてほしいんですけど?
バカな質問ですみませんが、このFreeBSDもこの問題に引っかかっているのに、なぜOpenBSDは平気なのか不思議です。
事前に把握してたんですか?
昨日、一面記事になったような大ニュースになぜOpenBSDは引っかかっていなかったのか気になります。
これに対するTheo De Raadtの返事。
Intelの誇大広告まみれのCPU機能を全部追いかけるようなマネはしていないんでな。
We didn't chase the fad of using every Intel cpu feature.
強すぎる。
この機能というのはi386から追加されたハードウェアブレイクポイント機能のことだ。なんとOpenBSDではユーザースペースからx86のハードウェアブレイクポイント機能の使用を許可していない。ではgdbのようなユーザースペースのデバッガーはどうやってブレイクポイントを実装しているのかと言うと、古き良きソフトウェア実装を用いている。ブレイクポイントを仕掛けたい部分のコードを割り込み命令(int 3とか)とか無効命令(ゼロ除算)で置き換えておき、例外割り込みをブレイクポイントを仕掛けたい場所で発生させることによる移植性の高いソフトウェア実装だ。
ちなみに、IntelのCPUがハードウェアブレイクポイント機能を実装したのは1985年のi386にまで遡ることができる。
結果として、OpenBSDは今回の主要なOSが軒並み影響を受けた問題に対して何の影響も受けていないので、結果的に正しかったと言えるが、いやしかし凄まじい。OpenBSDのセキュリティにかける情熱を過小評価していた。
2018-05-11
Intelの古いマニュアルを誤読したために生じた脆弱性
Multiple OS Vendors Release Security Patches After Misinterpreting Intel Docs
Multiple OS Vendors Release Security Patches After Misinterpreting Intel Docs | Hacker News
8086でスタックを切り替えるには、ssレジスターとspレジスターを両方変更する必要がある。しかし、ssレジスターだけを変更してまだspレジスターを変更していないときに割り込みがかかると問題だ。そこで、8086は粋なはからいによって例外的にこの問題に対処した。ssレジスターを変更した直後の1命令では割り込みが発生しない。仮に割り込みが起きたとしても1命令を実行するまでは遅延される。
もし、ssレジスターを書き換えた直後の1命令でカーネルモードに入った場合、この粋なはからいが問題になる。カーネルモードに入ったあと、カーネルコードを1命令たりとも実行していない段階で割り込みが発生する可能性があるからだ。しかも実行はカーネルモードだ。
2018-05-08
glibcのabortマニュアルの中絶方針ジョークについて
「glibcのabortのマニュアルにはabort(終了)とabortion(中絶)をかけた中絶ジョークがあり、これはマニュアルとして有益ではなくて混乱の元なので削除するというパッチが提出され、受け入れられたが、RMSの反対により差し戻された」
これだけ読むとくだらない出来事のように思えるし、人によってはこのジョークの存在が好ましくないとか下品だと思うかもしれない。しかし、これは単に下品なジョークで片付けてよい問題ではない。実は音はもっと深いのだ。
まず、ジョークの内容は中絶ジョークではなく、中絶方針ジョークなのだ。
glibcのabortのマニュアルには以下の記載がある。
将来の変更警告:連邦検閲規制委員会に提案された方針によれば、この関数を呼び出すことができる可能性についての情報を我々が与えることが禁止されるかもしれない。我々はこれがプログラムを終了する適切な方法ではないと言わざるを得なくなるかもしれない。
問題の方針とは、メキシコシティポリシーと呼ばれているアメリカ合衆国では歴史の深いものだ。
Mexico City policy - Wikipedia
メキシコシティポリシーとはアメリカ合衆国政府の方針で外国籍のNGOがアメリカ政府から出資、健康補助、HIV補助、妊婦及び子供の補助を受ける場合「中絶の実施、並びに中絶を家族計画の方法として積極的に宣伝する」ことをしてはならない、というものだ。
これはアメリカ合衆国で中絶の違憲判決が出ていなかった時代から続く方針だ。歴史的に右翼である共和党はこの方針を肯定し、左翼である民主党は否定している。アメリカ合衆国における右翼とはキリスト教原理主義者を指し中絶は違法であるとの立場を取る。左翼はリベラルであり中絶は合法であるとの立場を取る。
メキシコシティポリシーの名前は1984年に共和党のロナルド・レーガン政権が名付けたものだ。これは1993年に民主党のビル・クリントン政権で撤回されたが、2001年に共和党のジョージ・W・ブッシュ政権で復活し、2009年に民主党のバラク・オバマ政権で撤回され、そして当然、2017年の共和党のドナルド・トランプ政権では復活した。
ちなみに、ブッシュ政権のときにメキシコシティポリシーの違憲性を問う訴訟で、アメリカ政府が外国籍のNGOを補助するときに中絶への立場を考慮することは違憲ではないという判決が出ている。
さて、話をglibcに戻そう。glibcというのはGNUであり自由ソフトウェア財団である。RMSが始めたGNUはその当初から左翼主義が全面的に出ており、ソフトウェア開発は政治活動だと考えている。その思想に基づけば、この20年前からマニュアルに存在する中絶方針ジョークは必要不可欠でありたやすく取り除いてはならないものである。
今回はRMSが強権を発動し、ジョークは残った。
この問題の背景事情を考えるに、私はこの中絶方針ジョークは残すべきであると思う。
2018-05-03
LLVMで5番目に貢献の多い開発者、LLVMの最近のSJW運動に反対して開発をやめると表明
One Of LLVM's Top Contributors Quits Development Over CoC, Outreach Program - Phoronix
Rafael Avila de Espindolaは2006年からLLVMに対して4300以上もコミットした開発者で、現在LLVMの全Authorの中で第5位のコミット数を保有する開発者である。Rafaelは最近のLLVM Code of Conductと今年のアウトリーチプログラムへの参加を、「社会不正義」(Social Injustice)だと吐き捨ててLLVMの開発をやめる声明を出した。
LLVMのCode of Conductは以下の通り。
LLVM Community Code of Conduct — LLVM 7 documentation
- 仲良く辛抱強くやれよ
- 新参は歓迎しろよ
- 迷惑かけんなよ
- 尊敬しろよ
- 言葉遣いには気をつけるのと、あと親切にしてやれよ
Code of Conductは、過去に様々な自由ソフトウェアコミュニティが取り入れて、その結果政治的な問題を引き起こしてきた歴史がある規約だ。そもそも存在自体が政治であるものを入れると純粋な技術から遠ざかり、些細な言葉遣いのような揚げ足取りの政治に終始するのは当然であると言えよう。
アウトリーチプログラムというのは現在コミュニティでマイノリティである属性を持った開発者を新規に確保するための優遇措置だ。属性というのは主に性別で、具体的には女性のことだ。しかしこれは純粋な技術力ではない性別のような条件で人を優遇するということであり、これはまさに性差別そのものである。これをやりだすと人物が純粋な技術力で評価されず、たまたまマイノリティの性や宗教や門地のような属性を持っていたというだけで優遇される。
私がさる理由はコミュニティの変化のためだ。現在のライセンス変更の議論は私がGCCの開発をしていた頃の自由ソフトウェア財団の政治を彷彿とさせるものがある。これだけではまだ去るほどの理由ではない。コードと同じように、LLVMはまだ最適なライセンスを選択しているし、コミュニティの変化というのがライセンスの変更だけであれば、まだ続けることもできた。
私が受け入れられないコミュニティの変化は社会不正義運動がはびこっていることだ。私がLLVMに参加したとき、誰も私の宗教や政治信条について疑問を呈するものはいなかった。我々は皆、良きコンパイラーフレームワークを作ることに注力していた。
ここ最近、Code of Conductやらが採用された。これによれば、コミュニティはすべての「政治信条」を歓迎するよう務めるとある。しかし、Code of Conductの存在に反対する政治信条を持つ者は歓迎できない。そして、カンファレンスに参加するにはCode of Conductへの同意が必要であるからして、私はもはや参加することができなくなった。
トドメの一撃は、LLVMがある団体と手を組んだことで、この団体は公に性別や門地による差別を行っている。これは私の倫理と真っ向から対立するものであり、私はこの手の輩と一緒くたにされないために、プロジェクトを去る必要がある。
RafaelのMLへの投稿は、最後に"So long, and thanks for all the bugs,"(さよなら、いままでバグをたくさんありがとう)と締めくくっている。銀河ヒッチハイクガイドへのリファレンスのようだ。
2018-04-23
C++入門書で再帰について解説しようとしたら思わぬ最適化できないコードに出くわした
C++入門書を書き始めて早数カ月、すでに文章量が「江添亮の詳説C++17」の半分近くに達しているが、まだようやくループを説明したところだ。
ループの章を一通り書き終えて、ついでに再帰によってループを実現する方法について軽く触れて章を閉じようと、以下のようなコードを書いた。
void hello()
{
std::cout << "hello\n"s ;
hello() ;
}
すると何故かsegmentation faultを起こすではないか。GCCでもClangでも同じ挙動になる。なぜC++コンパイラーはこの程度の末尾再帰を最適化できないのだろうか。
不思議に思って以下のコードも試すと、こちらは問題なく末尾再帰の最適化が行われる。
void hello()
{
std::cout << "hello\n" ;
hello() ;
}
違いは文字列だ。今回の入門書では、初心者に簡単にするために、文字列はすべてstd::stringを返すユーザー定義リテラルのoperator "" sを使っているのだ。これにより初心者は、"hello\n"sとかけばstd::stringとして使える。つまり、
auto s = "hello"s ;
s.size() ;
などと書ける。難しいことを一切考えなくてもよい。わたしの本が書き上がって出版されるまでにまだ1年はかかるだろうし、その頃にはC++17が普及している。C++17を使って初心者でも学びやすい記述をすることで、私のC++入門書は初心者にも優しい本になるだろう。そう考えていた矢先に何ということだ。
どうもstd::stringがあると末尾再帰の最適化が行われないらしい。しかしおかしい。一時オブジェクトの寿命はリファレンスにより寿命延長されない場合はfull-expressionの中までのはずだ。したがってこのコードはまだ末尾再帰のはずだ。再帰呼び出し後に何かする必要は何もないはずだ。
不思議に思って以下のコードを試してみたが、やはり末尾再帰の最適化はされていない。
void hello()
{
{
std::string s = "hello\n" ;
std::cout << s ;
}
hello() ;
}
これなら一時オブジェクトでもないしブロックスコープを明示的に使っているし問題はないだろうと思ったが、なぜかコンパイラーは末尾再帰の最適化を諦めてしまう。
では関数で包んでしまうというのはどうか。
void hello()
{
[]{ std::cout << "hello\n"s ; }() ;
hello() ;
}
これはうまくいった。関数で包んだ場合、コンパイラーは末尾再帰を最適化する。
コンパイラーの気持ちはよくわからない。
2018-04-18
2018-04でC++のドラフトに入った変更
C++のドラフトが更新されている。最新版はN4741だ。
変更点はEditor's Reportに書いてある。
N4740: N4740 Editors' Report -- Programming Languages -- C++
今回入った主要な変更は以下の通り。
P0840R2: Language support for empty objects
[[no_unique_address]]属性の追加。
この属性は、クラスが状態を持たずクラスを表現するためにストレージを割り当てなくてもよいことを意味する。
クラスには、存在すること自体に意味があり、そのオブジェクトに特に意味はないクラスがある。
template<typename Key, typename Value,
typename Hash, typename Pred, typename Allocator>
class hash_map {
Hash hasher;
Pred pred;
Allocator alloc;
// ...
public:
// ...
};
このようなコードで、HashやPredやAllocatorといったクラスが状態を持たないもののみを使用可能だと取り決めた場合、ストレージを割り当てる必要がない。しかし、このような基本クラスや非staticデータメンバーであっても、アドレス可能にするために最低でも1バイトはストレージを割り当てる必要がある。
[[no_unique_address]]はこのようなストレージの確保を必要としない、アドレスを取る必要がない型に使うことで、不必要なストレージの割当を回避することができる。
template<typename Key, typename Value,
typename Hash, typename Pred, typename Allocator>
class hash_map {
[[no_unique_address]] Hash hasher;
[[no_unique_address]] Pred pred;
[[no_unique_address]] Allocator alloc;
// ...
public:
// ...
};
P0962R1: Relaxing the range-for loop customization point finding rules
range-based forはメンバー関数begin/endのいずれか片方を持っている場合は、ADLによるbegin/endの検索を行わない。しかし、たまたまクラスがbegin/endという名前の片方のメンバー関数だけを持っていた場合も、ADLによる検索が行われない。
range-base forはbegin/end両方のメンバー関数を持つ場合のみADLによる検索を行わないようにする変更。
当然の話だ。
[PDF] P0969R0: Allow structured bindings to accessible members
クラスを構造化束縛する場合、メンバーはpublicでなければならないとされている。
class Three
{
int a, b, c ;
} ;
void f()
{
Three t{1,2,3} ;
auto [a,b,c] = t ; // エラー
}
しかしこれはおかしな話だ。というのも、アクセス指定というのは使った文脈が重要だからだ。
class Three
{
int a, b, c ;
friend void f() ;
public :
Three( int a, int b, int c )
: a(a), b(b), c(c) { }
} ;
void f()
{
Three t{1,2,3} ;
// エラー、friendなのに
auto [a,b,c] = t ;
}
構造化束縛でpublicなメンバーに限らず、アクセスできる場所ではアクセスできるようにする変更。
当然すぎる。
P0961R1: Relaxing the structured bindings customization point finding rules
構造化束縛でメンバー関数getが見つかったときに、テンプレートではない場合は、ADLで検索する機能。
これも当然だ。
文脈上型であることが明らかな場所では依存名にtypenameを付けなくても良くする機能。
template < typename T >
using type = typename T::type ;
と書くかわりに、
template < typename T >
using type = T::type ;
と書ける。他にも様々な文脈上型しか書けない場所でtypenameを省略できる。
P0780R2: Pack expansion in lambda init-capture
ラムダ式の初期化キャプチャーの中でパック展開できる機能。
たとえば、パラメーターパックをstd::moveしつつパック展開して初期化キャプチャーするには以下のように書ける。
template < typename ... Types >
void f( Types & ... args )
{
[ ... args = std::move(args) ]
(){ } ;
}
P0479R5: Proposed wording for likely and unlikely attributes (Revision 5)
分岐先の実行されやすさを指示できる[[likely]], [[unlikely]]属性の追加。
ある分岐先が実行されやすい時、あるいは実行されにくいときがコンパイル時にわかっているときは、その情報をコンパイラーにヒントとして伝えることで、コンパイラーは実行されやすいコードをよりキャッシュに乗りやすいホットな領域に配置したり、コード生成を工夫したりといった最適化ができる。
// めったに起こらないエラーの確認
if ( check_uncommon_error() )
{
[[unlikely]] ;
// エラー時の処理
}
// 必ず成功するはずの初歩的なチェック
if ( sanity_check() )
{
[[likely] ;
// 通常の処理
}
P0905R1: Symmetry for spaceship
比較演算子としてoperator <=>が追加されたが、"a <=> b"が合法であるならば、"b <=> a"も合法であり、かつその結果も自然なものになるように規定する変更。
何もしないヘッダーファイル<version>の追加。このヘッダーファイルはコンパイラーによって定義される実装依存の定義済みマクロなどを定義ささせるのに使われる。そのような定義済みマクロを使うには、何らかの標準ヘッダーを#includeしなければならず、そのような軽量ヘッダーとして追加された。
P0355R7: Extending
<chrono>にカレンダー機能を追加する。ユーザー定義リテラルによりコード中でも読みやすくなる。
int main()
{
auto date = 2018y/April/18 ;
// 2018-04-18
std::cout << date << "\n" ;
// 2018-04-19
date += 1 ;
std::cout << date << "\n" ;
// 2018-03-25
date -= 25 ;
std::cout << date << "\n" ;
}
日付処理が簡単にできるようになる。
[PDF] P0122R7: span : bounds - safe views for sequences of objects
spanライブラリの追加。これは連続したストレージ上の配列を扱うストレージを所有しないライブラリだ。
ロシア、Telegramをブロックするために180万ものIPアドレスをブロック
Russia Bans 1.8 Million Amazon and Google IPs in Attempt to Block Telegram
ロシアはインスタントメッセージングサービスのTelegramをブロックするため、AmazonやGoogleの所有する約180万ものIPアドレスをブロックした。
以下がそのIPアドレスの範囲で、1835008個のIPアドレスになる。
52.58.0.0/15
18.196.0.0/15
18.194.0.0/15
18.184.0.0/15
35.156.0.0/14
35.192.0.0/12
Telegramの創始者は以下のような声明を出したそうだ。
過去24時間でTelegramはロシアのISPによってBANされた。理由は我々が暗号鍵をロシアの諜報機関に提出するのを拒んだためだ。我々にとってこれは容易い選択であった。我々は顧客に対し100%のプライバシーを約束しており、約束を守れぬのであればむしろ消えるべきであるからだ。」
BANにもかかわらず、ユーザーの利用率は極端に落ちていない。これはロシア人は政府の検閲に対抗するために日常的にVPNやプロクシーを使っているためである。また我々はサードパーティのクラウドプロクシーサービスを使うことで部分的にユーザーにサービスを提供し続けている。
ロシアのTelegramユーザーの皆さんの協力に感謝する。また政治的検閲に与しなかったApple, Google, Amazon, Microsoftにも感謝する。
ロシアはTelegramユーザーの7%を占めている。ロシア市場を失ったとしても、他の地域でのユーザーの増加がこの穴を数ヶ月以内に埋めるであろう。しかし、ロシアのユーザーにサービスを提供し続けることは個人的にも重要だ。
ロシアとその他の地域におけるインターネットの自由を守るために、私はsocks5プロクシーとVPNを提供する個人や団体にbitcoinを寄付することにした。私はこの運動のために今年数百万ドルを喜んで寄付するし、私に続く者が出てきてほしい。私はこれをデジタル抵抗軍を呼ぶ。電磁的自由とその進展に対する草の根活動だ。
一方、日本では出版社が両手を上げて、緊急避難によるIPアドレスのブロックに賛同している。日本もロシアや中国と同じ道を辿りつつある。隷属への道は近い。
2018-04-07
2018-04-05
入門書の文章量が増える問題
C++17の参考書を書き終えた今、この世に必要なのはC++の入門書だ。そこでC++の入門書を苦しみながら書いているのだが、一つ問題がある。文章量が多いということだ。
今書いているC++の入門書は、まだ本の序盤までしか書いていない。C++のソースコードのコンパイルと実行方法、ビルドシステム、そしてようやく条件分岐を半分ぐらい書いた。後は付録としてプリプロセッサーを解説した。その程度だ。
しかし、wcで現行を単一のHTMLファイルに変換した結果を大雑把に計算すると、今書きかけのC++入門書が"2335 7876 178578"に対し、完成したC++17本が"8280 31208 639671"だ。まだ条件分岐までしか書いてないというのに少し分量が多くないだろうか。
考えてみると、文章量が多くなるのも無理はない話だ。初心者の気持ちに寄り添って書いている結果、「整数と浮動小数点数を演算すると結果は浮動小数点数になる」と書けば済む程度のところを、
- 整数と浮動小数点数を演算するとどうなるのだろうか?
- 確かめてみよう。
- ソースコード
- 実行結果
- なるほど、整数と浮動小数点数を演算すると浮動小数点数になることがわかる
などという冗長な書き方をしているためだ。これでは文章量が増えるのも当然だ。
しかし、冗長ではない書き方をするとなると、結果的にリファレンスマニュアルのような無味乾燥とした記述が続く。それで入門できる人間は、規格書とか私が昔書いたC++11/14コア言語を読めばC++を学べるはずだ。
とはいえ、文章量が多いとそれだけ読むのに労力がかかり、ただでさえ人間の脳に負担を強いる全く新しいプログラミング言語の習得に、長文を読むという追加の負担を強いることになる。
この問題をどうすればいいのだろう。Bjarne StroustrupのC++入門書があまりにも長大で鈍重で悪書の見本のような参考書であるとかつてあざ笑ったが、これは笑えない由々しき問題だ。このペースで書き続けると鈍器として凶器転用可能な重量を持つ入門書ができあがってしまう。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
2018-03-24
vectordash: 暗号通貨採掘している奴らに暗号通貨採掘よりは歩合のよい報酬を払ってGPGPU計算を購入するGPU版AirBnBみたいなサービス
Deep Learningを研究しているある苦学生は、AWSやGoogle CloudのようなクラウドでGPGPU計算を提供している高い利用料に頭を悩ませていた。そこで、GeForce GTX 1080Tiを複数枚使ってEthereumを採掘している友人の存在に気が付き、交渉して、Ethereumを採掘するよりも歩合のよい報酬を支払い、機械学習に必要なGPGPU計算を請け負ってもらうことにした。その対価はEthereumを掘るよりは高いが、AWSやGoogle Cloudよりはよっぽど安い。
望みのGPGPU計算を終えてめでたしめでたしの苦学生はふと思う。「まてよ、世の中の暗号通貨採掘しているやつは、AI研究者に計算力を売ったほうが歩合がいいんじゃないのか」と。
そのような小規模なGPGPU計算力を売買するインフラは存在していなかったので、学生は自分で作り上げた。Vectordashだ。
Vectordash: GPU instances for deep learning
仕組みとしてはGPGPU計算力の売り主のコンピューターで実行されるコンテナーに買い主がsshしてGPGPU計算をして対価を支払うというものだ。
要するに、GPGPUのAirBnBみたいなものだ。
面白い仕組みだ。暗号通貨の採掘に計算力を注ぎ込んでも暗号通貨を維持する以上の価値はない。しかしこの方法では、計算力を直接販売できるし、暗号通貨採掘の利益より高いのであれば、売り手も買い手もWin-Winではないか。AWSやGoogle Cloudより安いとあればなおのことよい。
もちろん、すでに大勢の人間が指摘しているように、信頼という問題点がある。
売り主は本当に本物の計算力を提供しているのだろうか。特にGPGPU計算を売るというのが難しい。売り主に悪意があれば、カーネルに手を加え、GeForce GTX 1080を複数枚用意していると見せかけ、実際には何も計算せず、ランダムな結果を返すことができてしまう。結果は信頼できない。
たとえ売り主に悪意がないとしても、ハードウェアは故障する。コモディティハードウェアの信頼性は高くない。もしGPUが故障して間違った計算を行うが、見かけ上正常に動作しているように見える場合、結果は信頼できない。
計算力の売り主が信頼できない状態で計算結果の信頼性を担保したい場合、複数の売り主に同一の決定的(deterministic)な計算を依頼し、結果が同一であるかどうかを比較することで信頼度を上げることができる。しかし、そうすると利用料は数倍になり、AWSやGoogle Cloudに対して安いという魅力がなくなる。それに、完全に決定的(deterministic)な計算だけを扱うのも難しい。
買い主の悪意はどうだろうか。GPUはDMAできる。すると、買い主はDMAにより仮想化の檻を脱獄し、あまつさえルート権限を取得し、売り主のコンピューターをBOTネットワークの一部とするだろう。GPUに対するセキュアな仮想化技術はまだ発展途上だ。そして、セキュリティを担保するとパフォーマンスに影響を与えるだろう。
個人が計算力を提供する分散コンピューティングというのは前例のない話ではない。たとえばSETI@homeは宇宙からの観測データを有志が計算力を提供することにより知的な宇宙人の存在を発見しようという目的をもった分散コンピューティングだ。Folding@homeはタンパク質の折りたたみ構造を有志が計算力を提供することによって様々な疾患の治療に役立てようというものだ。
これらの既存の分散コンピューティングは、計算内容の提供者は固定で、参加はボランティアによるもので、しかも参加者に短期的な金銭による対価が支払われることはない。そのため、悪意ある計算力の提供者の存在や、計算内容に悪意があるリスクは低いと考えられる。
しかし、今回のvectordashとやらは、汎用的な計算力の売買を行うので、悪意の可能性は大いに考慮しなければならない。
問題はあるものの、暗号通貨に計算力と電力を注ぎ込むよりは、よほど人類にとって有益な計算力市場が出来上がるのではないか。
2018-03-19
2018 Jacksonville会議でC++のドラフト入りが決定した機能
2018 Jacksonville ISO C++ Committee Reddit Trip Report : cpp
なぜこのような情報をRedditで見なければならないのかという疑問はあるが、2018 Jacksonville会議の結果がRedditでまとめられている。
P0840R1: Language support for empty objects
[[no_unique_address]]属性が追加された。この属性はクラスの非staticサブオブジェクトがユニークなアドレスを必要としないとコンパイラーに支持するものだ。これによって、コンパイラーはそのサブオブジェクトにメモリレイアウト上でスペースを割り当てる必要がなくなるので、Empty Base Optimizationができる。
struct A { } ;
struct B { } ;
struct C { } ;
struct D
{
[[no_unique_address]] A a ;
[[no_unique_address]] B b ;
[[no_unique_address]] C c ;
} ;
int main()
{
// 1
std::cout << sizeof(D) ;
}
例えばtraitsのようなクラスのサブオブジェクトとして持っておくのに使える。
template<typename Key, typename Value,
typename Hash, typename Pred, typename Allocator>
class hash_map {
[[no_unique_address]] Hash hasher;
[[no_unique_address]] Pred pred;
[[no_unique_address]] Allocator alloc;
Bucket *buckets;
// ...
public:
// ...
};
文脈上、型しか書けない場所に書かれたものは型であるとみなすことにより、依存名にtypenameを記述しなくてもすむように制限緩和する提案。
依存名が型である場合はtypenameキーワードによって型であることを明示しなければならない。
// Tはこんな感じの型を想定
struct example
{
using type = int ;
static constexpr type value = 0 ;
} ;
//
template < typename T >
typename T::type f()
{
typename T::type x = T::value ;
using type = typename T::type ;
std::vector< typename T::type > v
return x ;
}
このコードが以下の様に書けるようになる。
template < typename T >
// typenameはいらない
T::type f()
{
// typenameが必要
typename T::type x = T::value ;
// typenameはいらない
using type = T::type ;
// typenameはいらない
std::vector< T::type > v
return x ;
}
書きやすくなった。
P0479R4: Proposed wording for likely and unlikely attributes (Revision 4)
[[likely]]と[[unlikely]]の追加。この属性は文に書くことができる。この属性が書かれた文を含む実行パスの実行の期待度を示す。
例えば以下のようなコードがあるとして、
while ( true )
{
// まれにしか起こらないエラーのチェック
if ( check_rare_error_condition() ) {
// 分岐1
// エラーへの対処
} else {
// 分岐2
// 通常の処理
}
}
このコードでは、エラーが起こることはまれであり、したがって分岐1が実行される可能性は低い。コンパイラーがその情報を事前に知っていれば、コード生成の際に役立てることができる。
if ( check_rare_error_condition() )
{
[[unlikely]] ;
// エラーへの対処
}
この機能は大抵のコンパイラーが独自拡張として様々な文法ですでに実装している。
ヘッダーファイル<version>を追加。このヘッダーファイルは標準規格的には何も入っていない。実装依存のバージョン番号やリリース番号を示す定義済みマクロを提供するためのヘッダーファイルだ。
多くのC++コンパイラーが独自の定義済みマクロでコンパイラーのバージョンその他の情報を提供している。問題は、そのような定義済みマクロは、コンパイラーマジックによって定義されるのでもなければ、なんらかの標準ライブラリのヘッダーファイルを読み込まないと定義されない。
そこで、標準ライブラリの中でも特に軽量なヘッダーファイルである<ciso646>が慣習的に使われてきたが、今回、ciso646は実用的な機能を提供していないので廃止しようという議論が持ち上がった。そこで、このヘッダーファイル自体に意味はないが、コンパイラー独自の定義済みマクロのためだけに#includeしている既存のコードがたくさんあるという指摘が上がった。
そのため、その用法をサポートするための最も軽量なヘッダーファイルである<version>が提案され、追加された。
P0355R5: Extending <chrono> to Calendars and Time Zones
<chrono>にカレンダーライブラリの追加。
int main()
{
auto date = 2017y/mar/18d ;
// "2018-03-18"
std::cout << date ;
}
カレンダー操作が型安全に行える。
osyncstream用のバッファーフラッシュの動作を制御するマニピュレーターの追加。
連続したストレージを所有しないまま配列として扱うspanライブラリの追加。提案段階ではarray_refとかarray_viewなども提案されていたが、最終的にspanになった。
P0780R1: Pack expansion in lambda init-capture
ラムダ式の初期化キャプチャーでパック展開ができるようになった。これにより可変長テンプレートを使った関数テンプレートでラムダ式にパラメーターパックをそれぞれムーブキャプチャーすることができるようになった。
template < typename ... Types >
void f( Types ... args )
{
[ args = std::move(args)... ](){} ;
}
ドラフトの変更以外としては、simd<T>がParallerism TS v2に入ったり、Reflection TS v2, Library Fundamentals TS v3が発行されたりした。
現在の観測を見てみると、モジュールがC++20に入るかは疑わしい。コンセプトとレンジのコアは入りそうだ。
colony(歯抜けを許すvector)やflat_map(ソート済みvectorのバイナリサーチ)はC++20に入ってほしい。他には、fixed_capacity_vector(サイズ固定vector)やring_span(リングバッファー)
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
2018-03-16
自宅のメインPCのストレージが故障した
江添亮の詳説C++17本の発売の余韻に浸る暇もなくC++入門書を書いているが、これがなかなか面倒だ。というのも、C++を学ぶ前にまず、C++のソースファイルをコンパイルして実行する方法を学ばなければならないと考えたので、環境構築から書いている。
具体的にはmkdirでディレクトリを作りcdで移動してそこにvimでテキストファイルを作成しGCCでコンパイルして実行するがカレントディレクトリはPATHに含まれていないので./を忘れずにつけるようになどといった、誰でも書けるような本当に初歩的な内容を書いている。
はたしてこんな初歩的な内容が必要なのかと疑問に思うこともあるが、しかし初心者がどこでつまづくかわからない以上、すべて書くしかない。すでにプログラミングを学んでいて実用的なソフトウェアを書いている人間であっても、今やプログラミング環境は多種多様になっており、CLIでの操作経験がまったくないままプログラマーとしてやっている人間がいても不思議ではない。したがって、カレントディレクトリはPATHに含まれないとか、コンパイルやリンクといった概念の説明が必要になる。
話は変わるが、今朝自宅のメイン環境として使っているラップトップの調子がとても悪かった。まずブートせずにbusyboxのシェルを表示する。fsckを書けたところブートするようにはなったが、何やら極端に遅い。dmesgするとストレージへのアクセスでエラーが大量に出ている。どうやらストレージが壊れたようだ。幸い、データはサルベージできたし、プログラマーの当然の嗜みとして参考書はgitで管理しており、常に複数のストレージに入れるようにしてあるので問題はなかったが、メインPCが使えないのは困ったものだ。自宅にはゲーム専用の不自由な日本語IMEすら無効化しているWindows PCとゴミのようなタブレットしかない。このタブレットはAMDの超省電力の非力なCPUを使っており、かつGPUがLinuxカーネルで使えないのでこともあろうかLLVMpipeでグラフィック表示している最悪の環境だ。
メインで使っていたラップトップは15インチで4Kディスプレイを内蔵していて、かつnVidiaのGPUを積んでいないという稀有な存在だ。AMDのGPUを積んでいるらしいのだがなぜかUbuntuでは認識されず、Intel HD Graphicsだけで動いている。訳あり中古で買った格安品で、訳ありの理由は、内蔵ディスプレイの中央あたりに白い常時点灯の線があるというものだ。地味につらいが値段は安く、4Kディスプレイを内蔵しているnVidiaのGPUを積んでいないコンピューターだったのでだましだまし使っていたのであった。
ストレージはHDDにキャッシュ用のそれなりの容量のフラッシュメモリがついているという、少し前に流行ったハイブリットHDDだ。ストレージさえ交換すれば再び使えるようになるのではないかとも思うが、残念ながらNVMe M.2 SSDとは違い、おそらくはラップトップ用のSATA接続の2.5インチHDDであろうし、調べたところ分解手順が一般人の分解を想定していないほど面倒だ。
早く自宅のメインPCを調達して自宅で参考書を執筆できる環境を整えなければならない。しかし思ったのだが、これはC++17本の印税の突っ込みどころとして最適ではないか。雀の涙のような印税ではあるが、高スペックなラップトップを一台買えるぐらいの額はある。それに経費と相殺すれば雑所得が確定申告が必要な額である20万円を下回るのではないか。ただ、調べると10万円を超え、かつ1年以上使うコンピューターについては減価償却という概念が出てきて、耐用年数が4年だそうだ。ただ、どうせ印税の額は少ないので4分の1にしても20万円を下回るのは容易いように思われる。ただ、それが執筆専用に使われない場合は比率を考えないといけないのでそれも難しい。
経費と税金のややこしい話はともかく、自宅に執筆用のPCが必要なので、さっそく久しぶりにラップトップを物色したが、どうにも難しい。私がほしいのは、15インチで4Kディスプレイ内蔵でnVidiaのGPUを積んでいないラップトップだ。私の本の執筆には高性能なGPUはいらないし、nVidiaのGPUは不自由なバイナリブロブのカーネルモジュールを必要とする。しかし、世の中には12インチで他の条件を満たすものか、15インチでnVidiaのGPUを積んでいるものしかない。
そして、ここ1,2年ぐらい顕著なのが、3K解像度のディスプレイだ。3200x1800程度の解像度のラップトップが増えている。これはけしからんことだ。作業効率というのは眼球にどれだけ多くのピクセルを叩き込めるかで決まるものだ。1920x1200が1920x1080に駆逐されたときも失望したのだが、4Kディスプレイの劣化版がHiDPIを名乗っているのもけしからん。3Kは劣化。4Kディスプレイは最低限の基準だ。
まあ、実際の理由は、4Kディスプレイ内蔵のラップトップはバッテリー駆動時間が短いので、HiDPIを謳いつつ電力消費を下げるために3K内蔵ディスプレイが出てきたのだろうし、15インチ4KディスプレイにnVidiaのGPUを積んだラップトップしかないというのも、15インチで4Kディスプレイを内蔵したラップトップにはGPU性能の需要もあるからそういう製品ばかりになっているのだろう。15インチで4Kディスプレイ内蔵でGPU性能はいらないという私の需要は少数派なのだろう。
というわけで、候補はSystem76かPurismか、あるいはDellのXPSあたりになる。System76は3K解像度、Purismは解像度も1920x1080しかない。Dellは無難だが、やはり15インチにはnVidiaのGPUがあり、13インチは3Kディスプレイだ。
2018-03-14
git submoduleを含むレポジトリをGitHub Pagesで公開するときのsubmoduleのURLはhttpsでなければならない
江添亮の詳説C++17の出版記念の勉強会で使うスライド資料をGitHubにあげてGitHub Pagesで公開する作業をしていた。
歌舞伎座.tech 番外編「江添亮の詳説C++17」出版記念 - connpass
私はスライド資料の作成は、markdownで書き、pandocでreveal.js形式に変換し、reveal.jsでスライド形式で表示させている。いつもならばreveal.jsはリリース版のtarを展開してgit add .するのだが、今回は今まで使う機会のなかったgit submoduleを使おうと思った。man git submoduleしたところ使い方は簡単で、git submodule add URLするだけだ。
さっそくGitHubに上げてGitHub Pagesを有効にしたが、なぜか404、エラー内容を読むと、どうもGitHub Pagesのレポジトリでsubmoduleを使う場合は、URLはhttpsでなければならないそうだ。
Using submodules with Pages - User Documentation
まあ、たしかに理由はわかる。とはいえ、submodule先もgithubの場合、特別に対応することもできるのではないか。
submoduleのURLを後から書き換えるには、.gitsubmodulesを書き換えればよい。
https://github.com/EzoeRyou/slide-cpp17book
2018-03-11
プログラマの数学を読んだ
結城浩著、プログラマの数学を読んだ。
内容は高校数学程度のもので、2進数、論理、数学的帰納法、順列組み合わせ、再帰、背理法あたりは高校数学で出てきた記憶がある。
剰余と再帰はプログラマーならば誰でも知っているぐらいよく使うのだが、なぜか高校数学までの間に剰余と再帰を学んだ覚えはない。私の記憶では、除算を学び始めた頃は商と余りとを区別していたはずだが、その後、実数や分数がでて余りを意識しなくなってしまう。整数の商を求めるのが除算演算子なのだから、整数の余りを求める剰余演算子も小学校ぐらいで教えていい気がするのだが、なぜ日本の教育では剰余を教えないのだろう。数学的帰納法は再帰だと言われれば再帰ではある気がするが、再帰という概念は高校数学までにしっかりと出てきた記憶がない。パスカルの三角形は高校数学で出てきた気がするが、パスカルの三角形は組み合わせの再帰的定義で表現できるという話は出てきただろうか、記憶がない。
カウンタブルな集合というのは高校数学では出てこないが、整数列の集合や実数の集合がカウンタブルではないことの証明に、カントールの対角線論法が必要なのがよく分からなかった。というのも、別に対角線である必要はないように思われるからだ。例えば各実数の1番目とか2番めの数字を順番に並べた実数とかでもいいのではないか。なぜ対角線に取る必要があるのか。調べてみると、カントールの最初の証明は対角線論法を使っておらず、後に対角線論法を使った証明を思いつき、とても便利なので様々な証明に転用されているそうだ。
第二版では、機械学習の仕組みについて軽く触れている。
本の内容の半分ぐらいはすでに学んでいたことではあったが、改めて丁寧な説明を読むことで理解が深まる。
2018-03-09
江添亮の詳説C++17が発売された
C++17の新機能を余すところなく解説した参考書「江添亮の詳説C++17」が発売された。
3月9日から全国の書店の店頭に並んでいる他、アマゾンでは紙の本と不自由なKindle版を買うことができる。
達人出版からも電子書籍を買うことができる。
この本はGPLv3でライセンスされた自由な本なので、当然本のソースコードを公開している。
https://github.com/EzoeRyou/cpp17book
本はかなり早い段階からGitHubで公開して執筆していたが、461コミットあるうちの155コミットが筆者によるものなので、全コミットの2/3は他人のPRをマージしたものである。
出版記念の勉強会を3月14日に開催する。
歌舞伎座.tech 番外編「江添亮の詳説C++17」出版記念 - connpass
勉強会の会場でも本を買える手はずになっている。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
2018-02-28
江添亮の詳説C++17出版記念勉強会を3月14日に開催
3月9日に発売される江添亮の詳説C++17の出版を記念して、3月14日に勉強会を行うことにした。
本の内容としては、2017年に発行された新しいC++17の新機能を余すところなく解説している。
本はGPLv3でライセンスされた自由な本であり、GitHubでソースコードが公開されている。
https://github.com/EzoeRyou/cpp17book
出版を記念した勉強会を3月14日に開催する。勉強会の詳細と参加登録は以下のconnpassのページにて確認できる。
https://connpass.com/event/80830/
当日は本を現地で販売できる見込みだが、数に限りがあるので確実に入手するためには、今から書店や通販Webサイトで予約注文をするとよいだろう。
最近、発表会と言うよりは、C++に興味がある人間が雑談をする場の方が有意義ではないかと思い、発表枠としてはLT枠のみを設けた。
本は購入するだけでは意味がなく、読まなければならない。今後、著者自ら読書会を開催しようと思っている。この本の所有者達が、広い部屋に、一斉に集まり、わざわざ読書会に参加したのだから、これはもう本を読むしかない。そのような状況をお膳立てするべきだろう。
さて、今年は入門書を書かなければならない。入門書の執筆は最も難しい。難しい機能を難しく解説するのは簡単だ。すべてを網羅的に記述すればいいだけだからだ。入門書は多くの詳細を大胆に省略、簡略化しなければならない。その結果、すべての入門書の内容は本質的に間違ったものになる。それでもなお間違いの少ない簡略化のルールを見いだせた入門書が良書となる。つらい仕事だ。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
2018-02-24
NPM 5.7が重要なディレクトリの所有者を書き換える凄まじいバカをやらかす
https://github.com/npm/npm/issues/19883
Do not use NPM 5.7 | Hacker News
NPM 5.7において、sudo npmを非rootユーザーから行うと、/etc/とか/usrとか/bootなどの極めて重要なディレクトリの所有者を、sudo npmを実行した非rootユーザーにchownして書き換えてしまうというとてつもないすさまじい不具合が報告されている。
一体どうすればそんな間違いをしでかすというのか。
どうもディレクトリを作成するときにパーミッションと所有者を適切に設定するという名目でmkdirをラップして実行時のユーザーでchownするcorrectMKdirを作ってmkdirを置き換えたようなのだが、そもそもsudoしたユーザーがroot権限を持つユーザーである保証はなく、そもそも/etcや/usrの所有者であるユーザーである保証もなく、本当にNPM開発者は本来とっくの昔に解決したはずの問題を、ぼくちんのかんがえたさいきょうのほうほうで引っ掻き回すことにかけては驚異的な才能を発揮するのに余念がない。これだから特定の言語専用のパッケージマネージャーというのは使うべきではないのだ。
2018-02-21
C++をプログラミング入門に使うならHTTPSぐらい喋れないと話にならんと言われたのでそういうライブラリを作った
C++17の新機能を余すところなく解説した「江添亮の詳説C++17」は3月9日に発売される。今はC++によるプログラミングの入門書を書こうとしているが、同僚から、果たしてC++をプログラミング入門に用いるのは適切なのだろうかという疑問を提示された。私はC++はハードウェアに直接マッピング可能な低級機能からそれを隠匿する高級機能まで揃っている上に、継ぎ接ぎだらけの型システムは
「21世紀の入門コード片という意味では https download くらい標準で喋って欲しさ」
と言われた。なるほど、そういうのはsystem("wget -q https://example.com")すればいいのではないだろうか。
「なるほど、しかしそれではファイル経由で扱うことになる。初心者ならstd::stringとかに入った状態で扱いたいはずだ」
一理ある。そういうライブラリを実装すればいいのではないか。せっかくだからpopenを使って標準出力をパイプで受け取り、それをistreamにしてみよう。幸い、libstdc++にはstdio_filebufがあるのでFILE *からfilebufが作れる。
というわけで以下のようなライブラリを作った。
int main()
{
popen_istream ps("curl -s https://example.com") ;
std::string line ;
while (ps)
{
std::getline( ps, line ) ;
std::cout << line << '\n' ;
}
}
とりあえず動く。
「しかし、HTTPSをお話したい理由というのは、サーバーの提供するWeb APIを叩きたいためだ。そのためにはサーバーにPOSTでデータを送りつけられるとなおよい」
wgetは標準入力からPOSTで送りつけるデータの入力に対応していないが、curlは対応している。なのでパイプで標準入力にデータを流し込み、標準出力をパイプで繋げばよいのではないか。
popenは入出力双方向のパイプに対応していない。この理由は、デッドロックを起こしやすいためだ。結局、入出力のバッファリングがユーザースペースで行われている場合、実際にパイプに書き込まれるタイミングが想定と異なる場合があり、また多くの伝統的なコマンドも、標準入力をすべて読み込まないうちは出力を始めないような設計のものも多いためだ。
なので、入出力を双方向にパイプするには古典的なpipeを使ってやる必要がある。iostreamはistreamとostreamで別々のfilebufを持てるためこのような入力先と出力先が違う場合にも対応できる。istreamとostreamが別々なので、ostreamにすべて書き込んだ時点でパイプを破棄することもできる。
とここまで考えて思ったが、やはり標準に高級なネットワークライブラリがほしい。HTTPSをお話できたりブラウザー操作ができたりするようなライブラリだ。
class popen_filebuf
{
FILE * fp ;
__gnu_cxx::stdio_filebuf<char> fb ;
static FILE * init_popen( std::string_view command, )
{
std::string cmd( command ) ; // for null-terminated c_str()
FILE * result = popen( cmd.c_str(), "r" ) ;
if ( result == nullptr )
throw std::runtime_error("popen failed.") ;
return result ;
}
protected :
auto get_filebuf_ptr() noexcept
{
return &fb ;
}
public :
popen_filebuf( std::string_view command )
: fp( init_popen( command ) ),
fb( fp, std::ios_base::in )
{ }
~popen_filebuf()
{
std::fclose( fp ) ;
}
} ;
class popen_istream :
protected popen_filebuf,
public std::istream
{
public :
popen_istream( std::string_view command )
: popen_filebuf( command ),
std::istream( get_filebuf_ptr() )
{ }
} ;
std::string popen_reader( std::string_view command )
{
popen_istream ps( command ) ;
std::istreambuf_iterator<char> iter( ps.rdbuf() ), end ;
std::string buf( iter, end ) ;
return buf ;
}
2018-02-11
アマゾンで江添亮の詳説C++17が予約可能になった
江添亮の詳説C++17 | 江添 亮 |本 | 通販 | Amazon
2017年に発行された最新のC++17の新機能をあますところなく解説した私の本、江添亮の詳説C++17がアマゾンで予約可能になったようだ。
本の内容はすでにGitHubで公開している。ライセンスはGPLv3だ。プログラミングの参考書は自由なライセンスで提供されるべきである。
https://github.com/EzoeRyou/cpp17book
次は入門書とTips集を書きたい。
2018-02-09
新しいプログラミング言語を学ぶということ
去年の暮から一ヶ月ほど、Haskellを学んでいる。目的は色々あるが、まったく新しいプログラミング言語を学ぶ経験をもう一度するのが最大の目的だ。
C++17の新機能を余すところなく解説した「江添亮の詳説C++17」は3月に出版される。C++20にはまだ間がある。C++の入門書を書く絶好の機会だ。入門書というのは毎月腐るほど出版されているが、良書は少ない。結局、参考書の中で最も売れ行きが容易に予想可能なのは、最も読み手がいるであろう入門者向けの本であり、入門書の出版がいたずらに増え、粗製濫造されているとみえる。入門書は上級者は読まないので適切な批判もされにくいのでこのような惨状になっているのだろう。
私の入門書はそのような悪書にしたくない。しかし、私はすでにC++の初心者ではないし、C++の初心者がつまづく点がわからない。というより、ここしばらくまったく新しいプログラミング言語を学んだことはない。C++以外にもPython、JavaScript、bashなどを必要に迫られて書き散らすこともあるが、相当古いJavaScript以外は真剣に学んだことがない。
これはよくない。入門書を書く前に、少し時間を割いてでも、全く新しいプログラミング言語に入門すべきだ。そのための言語としてはHaskellがふさわしい。と、こう考えた。
なぜHaskellなのか。HaskellはC++とは全く違った言語だからだ。例えばJavaScriptは表面的にはC++とかなり似た文法を持っているので、全く新しいプログラミング言語を学ぶ目的には使えない。PythonやRubyは文法的には少し違うが、すでに触ったことがあり、それほど苦労した覚えがないのでやはりだめだ。Scalaは汚らわしいJavaの実行環境を私の神聖なメインPCにインストールしたくないために不適だ。
Haskellを学ぶには、Learn you a Haskell for great goodという本が良いらしいので、早速買い求めて読み始めた。この本はとても軽くて読みやすい本だ。そしてユーモアが多い。真面目な参考書に個々までのユーモアは必要なのかと疑問に思うぐらいに多いのだが、しかし全くの初心者が読む本には、これぐらいのユーモアが必要なのではないかと思う。
というのも、だ。私自身、久しぶりに経験して驚いているのだが、全く新しいことを学ぶのは途方もなく疲れる上に非効率的な作業だからだ。不思議なもので、それほど難しいはずはない内容なのに、なぜかなかなか頭に入らない。一日に本を読む時間は何時間もあるが、短期的にいくら時間をかけても内容が頭に入ってこない。一晩寝るなどして時間を開けないとわずか数ページの内容も読み進めることができない。文字を読むことはできるにしても、その内容が理解できず、また残らないとしたら意味がない。
そういう賽の河原のような不毛な作業を続けるには、ユーモアがとても効果的だということがわかる。ただでさえ退屈な作業なのに、ユーモア欠落症患者によって書かれた文章まで読まされるのでは続くものも続かない。
私が書く入門書にはユーモアが必要だということがわかる。
プログラミング言語を学ぶには、プログラミングの実行環境が手元にあったほうがよい。しかしHaskellの実行環境はあまりすぐれているとは言い難い。Haskell PlatformはDebian系ならばapt一発で入るのでまだいいのだが、クールなキッズは皆使うというStackは、私の手元ではまともに動いた試しがない。
そもそもだ。CabalもStackもてんでなっていない。パッケージマネージャと名乗るのもおこがましい。だいたい管理できていない。パッケージを追加することはできるが削除できないのだから。削除するにはどうするか? すべてのパッケージを削除して最初からインストールをやり直す。富豪にもほどがある。Stackもだめだ。結局管理できていない。管理できないからどうするか? とりあえずプロジェクトごとに特定のディレクトリ下に全部環境を構築しよう。富豪にもほどがある。
そういうわけで、Haskellの実行環境を整えるだけで1日を浪費した。C++の入門書では環境構築についてそれなりにページを割かなければならないと痛感した。具体的にはWindowsとMacはC++の学習に不適切なOSなのでGNU/Linuxを推奨したい。主要なディストロならC++の実行環境はデフォルトで入っている。
さて、そうこうしているうちに一ヶ月たち、流石にHaskellである程度のコードは書けるようになってきた。まだモナドについて完全に理解していないのだが、理解できるのも時間の問題だ。なんだ、Haskellも言うほど難しくはないではないか。
しかし、プログラミング言語の学習に当たってはこの時期が一番つらい。プログラミング言語の学習過程では、序盤はさっぱりわからず、中盤はすべてを理解したつもりになり、終盤でやはりわからない状況になる。
プログラミング言語の学習の中盤でつまづくのは、言語の以外な落とし穴だ。例えば私の場合は以下のような例で躓いた。
モナドを学び始めたので、確認がてら、以下のようなコードを書いた。
getContents >>= 複雑な式>>= putStr
しかしこれでは見づらいので、複雑な式を関数に分けることにした。複雑な式の中身は特に重要ではないので、ここでは単にpureとする。
-- こうすればカリー化によって引数を書く必要がないぞ。Haskellは完全に理解した。
f = pure
getContents >>= f >>= putStr
その後、fはそのままにしたまま別の処理を試したくなった。
f = pure
getContents >>= 別の式 >>= putStr
するとエラーになった。意味がわからない。
Haskellではf = pureと書くとfは多相にならず、型を決定しなければならないが、型を推論するための文脈がないのでエラーになるというのが原因だ。解決方法としては、fの型を明示的に書く、引数を書く、GHC拡張を使うなどの方法がある。
しかし、これは割と初心者が引っかかりやすい落とし穴ではないか。例えば以下のコードがなぜエラーになるか、初心者がわかるのだろうか。
f = pure
main = do
print ( f 0 == Just 0 )
print ( f 0 == [0] )
どうでもいいことだが、C++を長年やっていた人間として、f = pureとしたあとにf 0 == [0]と書くと、fはpureになり文脈上リストなのでf 0は[0]になるというのは割と驚きがあるし、リストという割と特別扱いされているような昨日でさえ、単なるライブラリ実装であり、(:)はデータコンストラクターにすぎないというのも驚きがある。
早くモナドを完全に理解したい。
2018-02-05
Haskellで書いてみたらC++の10倍遅かった
エレガントな解法、エレファントな解法 〜モンテカルロ法を添えて〜
コインを100回投げたうちに、表もしくは裏が連続して10回でる確率をモンテカルロ法で計算する。
なにはともかくC++で実装してみた。
template < typename Random >
bool coin_toss( const unsigned int try_count, const unsigned int length, Random & r )
{
unsigned int count{} ;
int prev{} ;
std::uniform_int_distribution<> d( 0, 1 ) ;
for ( unsigned int i = 0 ; i != try_count ; ++i )
{
int result = d(r) ;
if ( result == prev )
{
++count ;
if ( count == length )
return true ;
}
else
{
prev = result ;
count = 1 ;
}
}
return false ;
}
template < typename Random >
double monte_carlo( unsigned int try_count, Random & r )
{
unsigned int count{} ;
for ( unsigned int i = 0 ; i != try_count ; ++i )
{
if ( coin_toss( 100, 10, r ) )
++count ;
}
return double(count) / double(try_count) ;
}
int main()
{
std::array<unsigned int, sizeof(std::mt19937)/sizeof(unsigned int)> c ; ;
std::generate( std::begin(c), std::end(c), std::random_device{} ) ;
std::seed_seq s( std::begin(c), std::end(c) ) ;
std::mt19937 engine(s) ;
for ( unsigned int i = 100 ; i != 1000000 ; i *= 10 )
{
auto r = engine ;
std::cout << i << "\t : " << monte_carlo( i, r ) * 100.0 << "%\n" ;
}
}
C++11から高度な乱数ライブラリが使えるようになったのだが、現在、実行時に適切に初期化する方法に面倒なボイラープレートコードが必要だ。実行時に乱数で初期化してくれる機能が現在提案されている。
ところで、最近Haskellを学んでいるので、このコードをHaskellでも書いてみようと思った。
まずは乱数だ。コインの表裏はBoolで表現するとして、Haskellらしく無限の乱数リストを生成しよう。
coin_seq :: (RandomGen g) => g -> [Bool]
coin_seq gen = randoms gen
あとはこれをtake 100して一回分の試行とする。1000回試行したければ、drop 100したのを再帰的にtake 100すればよい。しかし、もっといい方法がある。要素数100の[Bool]が入ったリスト、つまり[[Bool]]を無限に作ればいい。そのリストをtake 1000すれば1000回の試行分になる。
split_n :: Int -> [a] -> [[a]]
split_n n seq = take n seq : split_n n (drop n seq)
さて、seq_n = take 100 $ split_n 100 (coin_seq gen)のようなリストseq_n :: [[Bool]]は作れた。あとは[Bool]に連続した要素がn個あるかどうかを調べるcoin_toss nと、それをリストのすべての要素に対して行うmonte_carloを書けばいいだけだ。
coin_tossが結果をBoolで返すとすると、とりあえずseq_nにcoin_tossをmapしてTrueだけfilterして、その結果のリストの要素数を数えれば、成功した試行数がわかる。あとは試行数で割って確率を求めればいいだけだ。
monte_carlo :: Int -> [Bool] -> Double
monte_carlo try_count seq = ((fromIntegral n) / (fromIntegral try_count)) * 100.0
where
seq_n = take try_count (split_n 100 seq)
n = length . filter id $ map (coin_toss 10) seq_n
coin_toss len seqはInt -> [Bool] -> Boolな関数で、seqの中にlen個の連続したTrueもしくはFalseがあるかどうかを数えればよい。
いろいろ考えたが、まずgroup seqして連続した同じ要素を分割して[[Bool]]を作り、それぞれの[Bool]の要素数をいれたリスト[Int]をmapし、len以上の要素だけをfilterして、結果のリストに要素があればTrueを返す計算をすればいいのではないか。つまり、
has_contiguous_elems :: (Eq a) => Int -> [a] -> Bool
has_contiguous_elems len seq = not $ null $ filter (>= len) $ map length (group seq)
coin_toss :: Int -> [Bool] -> Bool
coint_toss _ [] = 0
coin_toss len seq = has_contiguous_elems len seq
となる。全体的にはこうだ。
import System.Random
import Data.List
coin_seq :: (RandomGen g) => g -> [Bool]
coin_seq gen = randoms gen
split_n :: Int -> [a] -> [[a]]
split_n n seq = take n seq : split_n n (drop n seq)
has_contiguous_elems :: (Eq a) => Int -> [a] -> Bool
has_contiguous_elems len seq = not $ null $ filter (>= len) $ map length (group seq)
coin_toss :: Int -> [Bool] -> Bool
coint_toss _ [] = 0
coin_toss len seq = has_contiguous_elems len seq
monte_carlo :: Int -> [Bool] -> Double
monte_carlo try_count seq = ((fromIntegral n) / (fromIntegral try_count)) * 100.0
where
seq_n = take try_count (split_n 100 seq)
n = length . filter id $ map (coin_toss 10) seq_n
main = do
gen <- getStdGen
let s = coin_seq gen
in mapM (\n -> putStrLn ((show n) ++ "\t : " ++ (show (monte_carlo n s)) ++ "%") )
[100, 1000, 10000, 100000]
このコードは動いた。ただし、最適化オプションを使っても、C++の10倍遅かった。
やはりlen個の連続した要素が存在するかどうかを調べるhas_contiguous_elemsが遅いのではないかと思い、これを再帰で実装することにした。
has_contiguous_elems' :: (Eq a) => Int -> [a] -> Bool
has_contiguous_elems' _ [] = False
has_contiguous_elems' len (x:xs) =
if count >= len
then True
else has_contiguous_elems' len $ dropWhile (== x) xs
where count = 1 + (length $ takeWhile (== x) xs)
この実装に差し替えたところ、実行速度はC++の9倍遅かった。
すると、他の部分もリストからリストを生成するという記述をやめて、再帰で実装すればもう少し早くなるのだろうか。
そして思うのは、同様のリストからリストを清々するような処理は、所詮リストのメモリーサイズがキャッシュに収まる程度なので、C++で書くとHaskellより早いのではないかとも思ったが、まだ試していない。
2018-02-04
年収1200万円と2億の資産でできる贅沢はパトロネージュ
以下のはてな匿名ダイアリーの記事が注目を集めている。
独身34歳男、年収900万+配当収入300万くらい。
贅沢の良さがわからない。
家は相続で貰ったので家賃無し。金融資産は相続したものと合わせて2億を超えた。
生活費は月に12万くらい。配当込みで年間800万以上金が増えていく。
別に金を使うのが嫌いなわけではない。ただ、使う気がおきない。
この増田は1200万円の年収と2億の資産を保有している。増田が過去に行った贅沢は以下の通り。
- 飛行機のファーストクラス(100万円)
- ホテルのスイートルーム(100万円)
- オーダーメイドのスーツ(50万円)
- 懐石料理(20万円)
- ステーキ(10万円)
これらの贅沢がありきたりでつまらないのは当然だ。なぜならば、これらの贅沢には、何も1200万円の年収と2億円の資産は必要ないからだ。ただ100万円を貯金して一度に使えばできる程度の贅沢だ。100万円を1年ぐらいかけて貯金するのであれば、34歳男の平均年収である400万円でも可能だ。
せっかく贅沢をするのであれば、年収1200万円と2億円の資産をもって初めて可能になる贅沢をするべきだ。では増田の境遇ではじめて可能になる贅沢とは何か。多額の借金と長期的な支出だ。そして、究極的にはぱとろネージュと呼ばれる行為、すなわち食客を抱えることだ。
多額の借金
増田のような境遇にない我々凡人は、それほど多額の借金ができない。大抵の凡人が生涯に負うことのできる多額の借金は、奨学金と住宅ローンぐらいなものだろう。しかも学費や住宅といった目的が限定されている。
しかし増田は違う。1200万円の年収と2億円の資産を担保に、1億や2億の借金ができるだろう。借金ができる環境はとても贅沢だ。借金で得た金を使って利息を上回る利益を出すことができれば借金をした以上の価値を生む。例えば増田は自分の興味がある事業を立ち上げることができる。ただし、これには利息を上回る利益を出すことができずに資産を失うリスクはある。
長期的な支出
増田が例に挙げている贅沢は、短期的な一度きりの支出ですむものだ。100万円を払ってファーストクラスに乗ったりスイートルームに泊まったりするのはいかにも贅沢に思えるが、一回払うだけでその後は何の支出もいらない。
しかし増田は違う。増田は月に50万円ぐらいの支出を長期的に続けることができる。長期的に支出を行えるのは、我々凡人には不可能な贅沢だ。では、長期的な支出が必要な贅沢とは何か。
答えは人だ。増田は長期的に人をフルタイムで雇うことができる。増田は秘書を雇い日常の雑事をすべて押し付けることもできる。増田の時間は貴重である。増田がファーストクラスやスイートルームや懐石料理を楽しむとして、その予約作業という雑事は、わざわざ贅沢な増田の手をわずらわす作業ではない。秘書にやらせればよい。
そして、古来より最高の贅沢とされる人への支出がある。パトロネージュ、すなわち食客を抱えることだ。
増田にはより発展してほしい芸術や学問はないだろうか。もっと具体的に書こう。自分の気にいる漫画や小説を読みたい。自分の気にいるゲームで遊びたい。ある自由なソフトウェアに自分の望みの機能を追加したい。ある病気を根絶する研究が進んでほしい。ある技術がもっと発展してほしい。しかし、増田は芸術や学問を極めることはできない。増田に絵や詩人の才能はなく、プログラミングはできず、医学博士や工学博士として研究に身を捧げる人生も送りたくはないとする。しかし、世の中には芸術や学問に身を捧げたいと願う天才はいる。増田はそのような天才を食客として衣食住の不便をなくし、芸術や学問を追求させることができる。
増田が増田を書くことができるのは、コンピューターが発展したからだ。ところで現代のコンピューターの発展はチャールズ・バベッジの研究に負うところが大きい。もしチャールズ・バベッジがコンピューターの研究に打ち込まなければ、コンピューターの発展は数十年は遅れていただろう。チャールズ・バベッジは裕福な家の出身ではあったが、コンピューターの研究のためにパトロンが必要だった。
この話の教訓としては、自分が楽しむためにカネを使えないのであれば、他人が楽しむためにカネを使ってみてはどうか。その結果として芸術や学問が発展するのであればこれ幸い。
2018-02-02
2018-01-28
CoinCheckとNemの騒動から暗号通貨について思うこと
何から語ろうか。まずCoinCheckにしよう。CoinCheckという暗号通貨の取引所が、NEMを大量に盗まれたという事件だ。私の法と技術の理解では、盗むというのは物理的な物が伴うので映画泥棒という言葉が法的に正しくないのと同様に違うのではないかと思うんだが、まあそこは置いておこう。ここでは単に悪意としておく。
これについて、自分のNEMが悪意されたのでCoinCheckに金を返せと叫んでいる人間のほとんどは、そもそも筋が悪い。もし、Bitcoinが花開いた暗号通貨が技術的に正しく運用されていたならば、そんなことは起こりようがなかったのだ。つまり、しっかり自分の手元の信頼できる環境でフルノードを実行し、物理的なコンピューターの前には武装した警備員を配備するべきだったのだ。自分でフルノードの実行もせずに、秘密鍵すらCoinCheckに知らせ、やれ盗まれたのなくなったのというのは、紙に印刷してある資料を電子媒体で送れといったら、紙をスキャンした画像をPDFやEXCELに貼り付けて送ってくるバカにも等しい愚かな行為だ。非科学的なアメリカ人は神によって通貨の価値を信じているが、我々科学の信奉者は計算力に裏打ちされた通貨を信じる。計算力の強いやつは正しい。それが暗号通貨の基本的な理念ではなかったのか。
世間では秘密分散共有によるマルチシグネチャーがどうのとか、ホットウォレットがどうのと言っているが、正直本質ではない。秘密鍵を分散させようと、エアギャップさせようと、守りが弱ければ、銃で武装した数人によって脅迫、拷問された末に情報を吐き出すのがオチだ。
さて、今回の事件の記者会見において、NEMのロールバックという面白い概念が提唱された。ロールバック、取引をなかったことにするという救済措置、これについて考えてみよう。
そもそもBitcoinから始まった、あまり正確ではないが俗にブロックチェーンと呼ばれる暗号通貨は、取引の上に取引を重ねる仕組みになっている。
例えば、アリス, ボブ, チャーリーによる取引が、アリス→ボブ→チャーリーの順番で行われた場合、取引チャーリーの取引はボブの取引が成立した上に成り立っており、ボブの取引はアリスの取引が成立した上に成り立っている。
すると、アリスの取引をロールバックするには、ボブとチャーリーの取引もロールバックしなければならないことになる。
もう少し読者が実感できる例で考えてみよう。今週、読者は給料日だったのでまとまったカネを持っている。いまからオーガニック・スシを食べようと出かけたところだ。スシ・レストランに向かう途中の自販機で、財布の中に残っていた小銭で代用ジュースを一本買った。そこに急に、日本自治区政府からのエージェントがやってきて、君に告げる。「君、実は先週、JPYの大量盗難が発生した。ついては被害者救済のために、先週に遡ってJPYの取引をすべてロールバックしたい。全員の同意が必要だ。君が今買った代用ジュースの代金は戻ってくるが、君の給与振込はなかったことになる。被害者救済のため、模範的な一般市民である君はロールバックに同意してくれるだろうね? いや、カネはなくなるわけじゃない。一週間前の状態に戻るだけなんだ。あとで必要な取引は各人で復旧してもらいたい」
読者は同意するだろうか。
しかし、全員が同意するのであれば、アリスの取引だけをなかったことにして、ボブとチャーリーの取引は存在したことにできないだろうか。もちろんできる。それはハードフォーク(通貨の分裂)と呼ばれている。
しかし、我々は計算力によって通貨を信用していたのだ。アリスの取引を無効にすると、その取引に連なるボブとチャーリーの取引は計算では信用できなくなる。しかし、皆がアリスの取引を無効にすることに同意するならば、計算力を持つ皆が信用しているので正しい。自分だけアリスの取引は発生したと主張することは可能だが、過半数がその主張を拒否するのであれば、もはやその価値はなくなる。すると、世の中にはアリスの取引が存在した通貨と、存在しなかった通貨が生まれることになる。どちらを信じるのも自由だ。両方信じてもよい。こうして通貨は分裂する。
わかりやすい例で考えてみよう。日本自治区政府のエージェントは君にこう告げる。「いまから先週発生したJPYの大量盗難の取引を取り消す作業をする。君の財布の中身、銀行口座の金額、その他あらゆるカネとその記録を出し給え。我々がJPYの盗難だけがなかったように書き換えて君に返す。今のカネの状態はすべて破棄される。我々の書き換えは信頼できる。模範的市民の同志、同意してくれたまえ」
読者は同意するだろうか。
今回、このどちらの方法もNEM財団は取らなかった。仮に取ったとしても、マイナー達の同意を取り付けることはできないだろう。
かわりにNEM財団がやったのは、悪意を持った転送先のアドレスに、盗難を意味するモザイクを付与することだ。これはNEMの機能で、詳しくは調べていないが、ここで重要なのことは、アドレスにタグ付けができるということだ。NEM財団が今回悪意を持って取引されたNEMを含むアドレスにすべて盗難タグ付けをしていくことで、そのアドレスとの取引を利用者に拒否するよう促す。これにより、悪意ある者の持っているNEMは私情でかちを失うという目論見だ。なるほど、窃盗アドレスだと知った時点ですでに善意の第三者ではなくなるから、法的にも効果的だ。
しかし、果たしてそううまくいくだろうか。今回の犯人が次にやるべきことは、NEMを小額づつ、適当なアドレスに片っ端から送りつけることだ。大多数のアドレスが窃盗タグで汚染されてしまうと、もはや窃盗タグは意味を為さなくなる。第一、そのタグ付けは計算力に裏打ちされているのだろうか。権威がつけたタグを盲信しているだけではないのだろうか。そして、権威は果たして信用できるのだろうか。するとNEM自体の価値も、果たして信用できるのだろうか。
ただし、この考え方は面白い。我々は権威と計算力のハイブリッドの信用通貨を作り出してもいいのではないだろうか。
通貨の歴史を考える。貴金属のようなもともと価値のあるものが取引されていたのが、貴金属を直接取引するのは非効率的なので、貴金属と交換できる引換券で取引するようになった。しかし、実際に存在する貴金属以上の借金が市場に出回り、この仕組みは崩壊した。今の国家に裏打ちされた通貨は、貴金属と直接交換を保証されていない。
世の中には、存在自体が価値を持つ権威が存在する。国家もそうだが、宗教開祖とかアイドルとか秒速で億ぐらい稼ぐ人とかだ。そういう信者を何十万、何百万と持つ権威が、Bitcoinのような既存の暗号通貨をhard forkして独自の暗号通貨を始めるのはどうだろうか。何十万、何百万の妄信的な信者が価値を認めるのであれば、その暗号通貨には実際に価値が出るだろう。するとマイナーも一枚岩ではなくなるので、誰も過半数の計算力を持つことができずに、マイナーは単なるトランザクション処理係に成り下がる。何か問題が起きれば、権威がイスラム教で言うところのファトワー(見解)を出して訂正するのだ。
と考えてみると、VALUは案外いい線をいっていたのではないかとも思えてしまう。もちろん私は信用しないが。
こうして考えてみると、計算力と権威のハイブリット信用通貨は、国家から通貨発行権を奪還する試みであるとも言える。面白い世の中になってきたが、私としてはもっと現実的な価値を提供してほしい。例えばチューリング完全な計算を提供するとか、実際ストレージとして使えるとか、契約への同意が行われたことの記録ができるとかのブロックチェーンネットワークがほしい。
2018-01-20
物理的に近くの相手とファイル共有する方法
以下のようなTweetを読んだ。
ごめん言葉足らずだった。インターネットへの通信品質を信頼できないコミケ会場で、ビューワであるスマホやタブレット端末にPDFを送りつけたいんだ。 https://t.co/gzbcQKXkEN
— ろ。まのふ (@kamiya344) January 19, 2018
インターネットが普及してからもうかれこれ20年以上もたつが、未だにこの問題が解決できていないのは嘆かわしいことだ。なのでこの問題を解決してみよう。
ただし、今回の問題は、大量に人が集まるため無線通信が難しい状況で、物理的に近い場所にいる相手とのファイル共有だ。
ファイルの転送自体は、前後にインターネット回線を確保してから行ってもいい場合は、どこかに暗号化したファイルを公開しておき、現場では復号鍵を配布すればいいだろう。
しかし、その場で直ちにファイルを転送したい場合はどうするのか。この場合、物理媒体による受け渡しは客が大勢来た場合にスケールせずに非現実的だ。
お互いBluetoothによる通信ができる状況であれば、Bluetoothによるファイル転送が使える。スマフォ端末やタブレット端末ならば、Bluetoothはほぼ確実にあるだろう。問題は、相手がBluetoothによるファイル転送に慣れていない場合、Bluetoothによるファイル転送の方法を説明する必要がある。これには相手の技術レベルにもよるが時間がかかるだろう。これもスケールしない。
もし転送したいファイルが2953バイト以下であれば、QRコードが使える。QRコードはディスプレイに映してもよいし、紙に印刷してもよい。圧倒的にスケールする。ただし、相手の端末にはカメラとQRコードリーダーが必要だ。ほとんどのスマフォとタブレット端末ならQRコードには対応しているだろう。しかし、PDFファイルはまず間違いなく2953バイトではすまないだろう。
やはり一番信頼できるのは有線LANだ。複数の客をさばくためにスイッチングハブを用意する。スマフォやタブレット端末にはEthernetポートがついていないことが多いが、USBポートが一つぐらいはあるだろうから、USBによるEthernetアダプターと各種USB変換ケーブルを用意しておけばよい。相手にIPアドレスを設定させる手間を省くためにDHCPサーバーも用意するとよい。家庭用のどんな格安ルーターでもDHCPサーバー機能はついているので難しくはない。
肝心のファイルを転送する方法であるが、FTP, SMB, HTTPなどが使える。SMBならば家庭用ルーターの機能として提供されていることも多い。しかし、相手への説明のコストを考えると、おそらくHTTPを使うのが一番楽だろう。ただし、URLが"http://192.168.0.100/file.pdf"などではやや相手にとってわかりにくいかもしれない。これを解決するには方法が2つある。ひとつはmDNSを使う方法で、IPアドレスのかわりに、hostname.localと書くことができる。ただし、これは相手の端末がmDNSに対応していなければならない。普通は対応しているはずだ。もう一つは、大抵のルーターについている静的DNSレコードなどと呼ばれている機能を使い、特定のIPアドレスに対してそのルーターのLAN側だけで通用するドメイン名をつけてしまうことだ。これなら相手の端末が万一mDNSに対応していない場合でも使える。
HTTPサーバーを用意する方法としては、転送したいファイルを置いたディレクトリで"python3 -m http.server"を実行するのが最も簡単だろうと思われる。
ただしこの方法は準備する機材がやや多い。ルーター、スイッチングハブ、LANケーブル、USB-Ethernetアダプター、各種USB変換ケーブル、HTTPサーバーを実行するコンピューター、知識を持った人間が必要だ。最後の知識を持った人間が一番用意しにくい。本来ならば、この2018年には全員がコンピューターリテラシーを持ち、この程度のことはできてしかるべきなのだが、スマフォが人をバカにしてしまった。
しかし、手のひらに乗るコンピューターでリアルタイム3DCGが描画でき、メモリ容量の単位がGBで数えられ、GB単位のストレージが数百円もせずに買えるこの2018年だというのに、なぜ身近の人間と物理的にファイルを転送するのにこんなに苦労しなければならないのだろう。
2018-01-19
3月に出版される「江添亮の詳説C++17」が予約できるようになった
3月に発売されるC++17の新機能をすべて解説した参考書、「江添亮の詳説C++17」が予約できるようになった。例えばアマゾンでは以下から予約できる。
江添亮の詳説C++17 | 江添 亮 |本 | 通販 | Amazon
「江添亮の詳説C++17」は、私の名前を冠するエゴの強い書名になっている。これは、次にC++によるプログラミング入門書を出したいために、名前をブランド化する試みだ。これがうまくいくかどうかはわからないが、
江添亮の詳説C++17は、GPLv3でライセンスされる。本のソースコードはGitHubで公開されている。まだもう少しは修正が聞くので、PRを投げてコントリビュートするなら今がチャンスだ。
https://github.com/EzoeRyou/cpp17book
GPLv3なので、本の組版に使ったtexのソースコードも公開されている。わたしはtexに詳しくはないが、できればビルドするためのドキュメントやMakefileなどを整備していきたい。
ちなみに、前回のC++11/14の参考書は、CC-BY-SAでライセンスされている。
https://github.com/EzoeRyou/cpp-book
すでに書いたように、次はC++によるプログラミングの入門書を書く。そのために色々と準備をしている。まず初心者の気持ちを再び味わうために、Haskellに入門した。Haskellは私の知っているプログラミング言語とはだいぶ違う言語であり、最初の数日はとても苦労した。なにより、環境構築が難しいということがわかった。次に書く入門書は、まず環境構築を丁寧に説明しなければならない。環境構築は慣れたものであれば何も意識しないが、何も知らない者にとっては意外と難しいものだということを身をもって学んだ。
C++の入門書はすでに多く書かれているが、いずれも古びてしまい、最新のC++からかけ離れてしまっている。
プログラミングの入門書を書く準備は整ったのだが、もう少しだけ寄り道をして、ブール代数を本格的に学ぼうと思っている。いままで表面的に浅くしか学んでいなかったが、この機会に深く学んでおきたい。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
2018-01-17
今流行りの投機実行脆弱性の影響を一切受けないDOOM実装が公開される
このDOOMは条件分岐を一切使用していない。したがって今流行りの投機実行の脆弱性であるMeltdownやSpectreによる脆弱性は存在しない。
ちなみに、現代の性能のコンピューターで、1フレームの描画に7時間ほどかかるので、このDOOMで遊ぶには若干の忍耐力を要求される。しかし、脆弱性の完全な排除のためには若干のパフォーマンスの低下はやむを得ないところだろう。
このDOOM実装はオリジナルのDOOMのCコードに若干の変更を施して、M/o/Vfuscatorでコンパイルしたものだ。
https://github.com/xoreaxeaxeax/movfuscator
M/o/Vfuscatorとは、IntelのMMUがチューリング完全であることを利用したmov命令のみを使うC言語コンパイラーだ。