Magnolia Tech

いつもコードのことばかり考えている人のために。

『理科系の作文技術』を久しぶりに読み返し、とにかく「6 はっきり言い切る姿勢」「7 事実と意見」だけは絶対にみんな読んだ方がよい、と思った

いまさら紹介するまでもないけど、とりあえず作文方法を学びたい時は、まずはこの「理科系の作文技術」を読むことをお勧めする。

最近ブログのエントリをざっと書いてそのまま公開してしまうことが多かったので、少し反省し、それを直すためにこの本を改めて読み直した。そうしたら、タイトルで全部言い切っているのだけど、とにかく「6 はっきり言い切る姿勢」「7 事実と意見」だけは絶対にみんな読んだ方がよい、と思った。


日常的にメールや、ブログの記事など、それなりの量の文章を書くことが多いけど、論文や雑誌の記事など、きちんと他人の目を通した上で公開される文章を書くことは、まずない。一度だけ、雑誌の記事原稿を書いたとき、自分なりにかなりの推敲を重ねたつもりでも、プロの編集の方から見れば言葉使いや、言い回しなど、たくさんの指摘をいただいた。おかげで、普段自分が書いている文章にフィードバックすることができ、少しはレベルが上がったと思う。

その時にも、やはり読み返したのがこの『理科系の作文技術』だった。過去に何度か、いろいろな人からこの本を紹介されることが有り、その度に読み返している。


『理科系の作文技術』は、タイトルに『理科系の』とついてはいるが、必ずしも論文のようなアカデミアで書かれる文章だけでなく、ビジネスの報告書でも使えるポイントが多数解説されている。

前半の「2 準備作業(立案)」や、「3 文章の組み立て」あたりは、ビジネスで書く文書は、量もそこまで多くなく、そもそも書く目的や、内容がある程度決まっているので、論文や、本の執筆のように「そもそも何を書くか」「どのように材料を集めるか」というところから出発することは少ないことから、ちょっと退屈というか、そんな大作は書かないし……という気分になるのだけど、そのまま読み進めていって「6 はっきり言い切る姿勢」「7 事実と意見」に差し掛かると、普遍的で、とても重要なことが書かれている。

6 はっきり言い切る姿勢

報告文書だったり、ソフトウェアの解説ドキュメントなど、本来「相手に正しく理解してもらう」「理解してもらった上で何らかの行動・意思決定を促す」ための文書において、結論があいまいなことを書かれても読む方は困ってしまうが、意外とそのような書き方になっている文書は多い。「結論はどこだっけ?」というのは、レビューで言ったことも、言われてこともたくさん有るはずだ。

本書では欧米と日本の比較、日本人の心理的な傾向からはっきりと名言しない傾向が有るころを解説している。

最後の方にこう書かれている。

私たちは,無意識のうちにぼかしたことばを濫用する習癖をもっている.仕事の文書のなかでは,「ほぼ」,「約」,「ほど」,「くらい」,「たぶん」,「ような」,「らしい」,……の類をできるだけ削ることも大切な心得の一つだ.

確かにこれだけでも気をつけると大きく変わる。

また、英語の「ステート」が“明確に表明する”という意味であり、それに類する言葉が日本語にないことも触れられていて、最後は“ステートするときには当然,一句一句に責任がともなうのである.”と締められていて、改めて文書を書くときの姿勢について考えさせられた。

7 事実と意見

意外と、”事実”と、”意見”が混在し、きちんと分離されていない文章は多い。報告書のレビューなどで「まず事実はどれ?」と聞かれたり、聞いたりしたことはよく有るのでないか。

本書では「意見」は幅広い概念で、その中には、「推論」「判断」「意見」「確信」「理論」が含まれるとしている。確かに意外と広く、そのような観点で見返すと、自分が書いた文章が「事実」と「意見」を適切に分離しているか、客観的にみることができる。

また、色々な例を持って、事実と意見を分離して書く方法が解説されている。

事実と、意見は、自分の中では区別されていても、いざ文章として書こうとすると混ざりがちなので、まずは一度メモなどに書き出すなどして、最初から分離する工夫をしないとすぐに混ざってしまうので、気をつけたい。

おわりに

どんな文章も一気に書いて、振り返りもしなければ、必ずよく分からない文章になってしまうので、自分でも常に以下のような工夫をしている。

  • 想定する読者の気持ちになって読み直す
  • 声に出して全部読んでみる
  • 紙に印刷し、別の部屋で読む(環境を変える)
  • 赤ペンを持ちながら読み、読み終わったところはチェックする

ただ、改めてそもそも書く前の準備や、書き方の姿勢といったところから見直していきたいと思った。

magnoliak🍧 (@magnolia_k_) | Twitter

知りたいことが有れば、勉強会を開いてみるといい…いや知らないから開いてみるといい

noteからの転載


f:id:magnoliak:20210122230240j:plain   吉祥寺.pmという勉強会をここ7年ほど運営している。

kichijojipm.connpass.com

「地域名」+「pm」という形式の勉強会は、昔はたくさん開催されていて、主にPerlの話題を扱う勉強会、という意味になる。つまり、吉祥寺.pmは、吉祥寺で開催されるPerlの勉強会、という意味になる、本来は。

トーク15分、LT5分で、だいたい10件ほどの登壇枠を用意しているけど、最近はPerlの話題は1件〜2件になっていて(まだ有るのか!という話はさておいて…)、Java, Go, PHPなど、色々な言語や、プロダクトマネージャー、採用などなど…話題の幅は広がり続けている。

正直、運営も事前にどんな内容が話されるのか把握していないし、当日聞きながら「へー」と驚くことばかり。

とはいえ、最初はPerlのことをもっと知る機会が欲しい、必ずしも開催される勉強会に行くだけでは自分が知りたいことが必ず知ることができるとは限らない、だったら自分でテーマを決めた勉強会を開催すれば知りたいことを知ることができる…という動機だったことを、昨日始めるきっかけの話をしながら気がついた。

勉強会の主催者が一番そのテーマに詳しい必要は全然なくて、むしろ知らないからこそ始めるのが動機になりやすいと思っている。知っている人は始める必要無いし。

という訳で、知りたいことが有れば、それをテーマに勉強会を開くといいという話でした。

『グッド・マス ギークのための数・論理・計算機科学』を読みはじめた

もうずいぶん昔、大学のコンピュータサイエンスの学科に通っている人に「どんな勉強をしているのか?」と聞いたら「数学」と返ってきて、「なんで?」と疑問に思ったことがある(その後、理由は身を持って理解した)。


”数学”、コンピュータを扱う上では切っても切れないけど、どこから手をつけたら良いか分からない人向けの1冊。目次を見てみると分かるけど、わずか280ページに全部詰まっている。と…言いつつ、買ったことを完全に忘れて、カバーをかけたまま本棚に眠っていたのを今日発見した。

必ずしも順番に読む必要がなく、一つ一つが独立しているのも読みやすい。おすすめです。

主要目次

“Good Math” 推薦の声

前書き

第I部 数
第1章 自然数
1.1 自然数を公理的に語る
1.2 ペアノの帰納法を使う

第2章 整数
2.1 整数とは何か? 
2.2 整数を自然に組み上げる

第3章 実数
3.1 実数を形式ばらずに
3.2 実数を公理的に
 実数の公理、第1部 :足し算と掛け算
 実数の公理、第2部 :順序
 実数の公理、第3部 :連続性
3.3 実数を構成的に

第4章 無理数と超越数
4.1 無理数とは何か? 
4.2 無理数に「あ゛ー!」となる瞬間
4.3 何を意味していて、何が問題なのか? 

第II部 変わった数
第5章 ゼロ
5.1 ゼロの歴史
5.2 イライラするほど難しい数

第6章 e:自然数でない自然な数
6.1 至るところにある数
6.2 歴史
6.3 e に意味はあるの? 

第7章 φ:黄金比
7.1 黄金比とは何か? 
7.2 伝説的なたわごと
7.3 黄金比の本当の住処

第8章 i:虚数
8.1 i の生まれたところ
8.2 i の働き
8.3 i の意味

第III部 数を書く

第9章 ローマ数字
9.1 位取りの体系
9.2 どうしてこうなった? 
9.3 算術は簡単(でもそろばんならもっと簡単) 
9.4 こうなったのは伝統のせいだ

第10章 エジプト分数
10.1 4000 歳の数学試験
10.2 フィボナッチの貪欲アルゴリズム
10.3 時に美しさは実用性に勝る

第11章 連分数
11.1 連分数
11.2 すっきりしていて、明快で、ただただ楽しい
11.3 算術計算

第IV部 論理

第12章 ミスター・スポックは論理的じゃない
12.1 それでは論理って何なのでしょう? 
12.2 FOPL、論理的に
12.3 何か新しいのを見せて! 

第13章 証明に、真実に、木:おおこわい! 
13.1 単純な証明を木で組み立てる
13.2 無からの証明
13.3 家族のすべて
13.4 分岐のある証明

第14章 論理でプログラミング
14.1 家族関係を計算する
14.2 論理で計算
 Prolog でペアノ算術
 Prolog のクイックなクイックソート

第15章 時間がかかわる論証
15.1 時間と共に変化する命題
15.2 CTL はどんなふうに役に立つのか? 

第V部 集合

第16章 カントールの対角化:無限はただ無限なんじゃない
16.1 集合(素朴に) 
16.2 カントールの対角化
16.3 単純にしておくな、この間抜け

第17章 公理的集合論:長所を残して、短所を捨てる
17.1 ZFC 集合論の公理
17.2 選択公理の狂気
17.3 なぜ? 

第18章 モデル:数学の世界のレゴブロックとして集合を使う
18.1 自然数を組み立てる
18.2 モデルからモデル:自然数から整数、そしてその先へ! 

第19章 超限数:無限集合の数え上げと順序付け
19.1 超限基数の導入
19.2 連続体仮説
19.3 無限の中のどこ? 

第20章 群論:集合の対称性を見つける
20.1 謎めいた対称性
20.2 いろいろな種類の対称性
20.3 歴史に立ち入る
20.4 対称性のルーツ

第VI部 機械じかけの数学

第21章 有限状態機械:単純だけどすごい奴
21.1 最も単純な機械
21.2 有限状態機械が目を覚ます
21.3 正規表現から有限状態機械への橋渡し

第22章 チューリング機械
22.1 テープがあることがとても重要
22.2 メタへ行く:機械を真似する機械

第23章 計算の病理学と、その心髄
23.1 BF の紹介:偉大で見事な完全なるおちゃらけ
23.2 チューリング完全か、さもなくば完全に無意味か? 
23.3 至高から滑稽へ

第24章 計算:違う、ただの計算じゃない ― λ計算だ
24.1 λ計算を書く:プログラミングも同然! 
24.2 評価:動作せよ! 
24.3 プログラミング言語とラムダの戦略

第25章 数、真偽値、そして再帰
25.1 でもそれってチューリング完全なの? 
25.2 数を計算する数
25.3 選択? チャーチに戻ろう
25.4 再帰:ナンデ・ナンデ・ナンデ? 
 再帰を理解する
 λ計算の再帰

第26章 型、型、型:λ計算のモデル化
26.1 型と遊ぶ
26.2 証明するんだ! 
26.3 なんの役に立つのか? 

第27章 停止性問題
27.1 輝かしい失敗
27.2 止まるべきか、止まらざるべきか? 

訳者後書き

参考文献
索引

その他、オライリーから出ているこの辺りの本も、ぜひ読んだ方がよい。

コードの意味、意図や重要性を読み取る

noteからの転載

コード自体は等しく平等だけど、重要性は人間が見出さないといけない、という話。

でもひょっとしたら、いつの日か、実行時に使われるコードや、分岐の判断結果などで、重要度を見出す、みたいな研究も進むかもしれない(もう有る?)。

品質保証の観点で、ドメインエキスパートが指摘する「この機能が一番重要、こっちの昨日はまず使われることは無い、さらにこの機能は過去に一度も動いたことが無い」みたいな話を解析してくれる仕組みが有ればいいのに。


シンタックスハイライトは、コードに対して文法に応じた色づけをしてくれる点で画期的な発明なのだけど、残念ながらコードの“意味”や、”意図”、"重要度"までは表示してくれない。

ロジックの順序や、スコープの切り方で意味や意図を見いだしやすくすることはできるし、コメントや変数名、メソッド名に直接的にそれを込めることは、できる。

だけど、最終的には意味を見いだすのは人間側だし、その時点で読み取りたい内容は異なってくる。

例えば、人事システムで過去にM&Aなどで社員に移行登録された人を示す特別なフラグが有り、評価や昇給などの判定のために度々そのフラグが使われたとする。M&A直後は該当する社員も多く、そのロジックを通る割合が一定のマジョリティを占めていた…が、月日も経ち、それに該当する社員がほぼいなくなった時、コードの保守性という観点では、このフラグの存在、対応する数々のコードがノイズになってしまう可能性が出てくる、とか。

コンピュータは正直だ。書かれたコードの通りにしか動かない。例外的な対応や、一時的に必要だったコードも、全て等しく「コード」だ。だけど、そこにどう意味を見いだすかは、そのコンテキストによって全然変わってくる。

”どのコードが重要か?”は、極めて主観的で、かつ動的なものであり、客観的に、静的には確定できない。

また、時に非常にトリッキーなコードが、とても短いコードで実にさまざまなパターンに”対応できてしまっている”時が有り、このような時に、”できること”のバリエーションが分からなくなってしまうことも有る。

ざっとコードの流れを知りたいのか、王道ルートのパターンを知りたいのか、特定の条件での処理の詳細を知りたいのか…それら全てに等しく答えられる万能の解は今の段階では無いので、僕らはせいぜい今日もコメントにコードの意図や、表層的には読み取れないパターンなどを補足を書いていくのだけど、いつの日かこれが解決してくれる方法がアッという方向からやってくるのかもしれない。


magnoliak🍧 (@magnolia_k_) | Twitter

プログラミングを難しくする要素って何だろう

以前noteに書いた記事からの転載 エクスポートできないので、定期的に少しずつ転載していきます。

いつかちゃんとしたスライドに書き起こしたいとおもいつつ、まだ手がついていないけど、この記事に書いている「プログラミングは、コードと、データと、改修の歴史の3つの要素が絡み合う」を分解していきたい。

コードと、データは本質的には不可分だし、その結びつきを分解できないように密に結合させているのが、改修の歴史なんだ

よく「データの寿命はコードよりも長い」と言われるけど、受け継がれたデータは、当たり前だけどそれが作られた当時のコードに強い影響を受けていて、不可分だし、暗黙のうちにコードの特性を引き継いでいる。

つまり、例え直接的にはコードが無くなったとしても、コードの影響が無くなるわけではない。

そして、それらの蓄積が歴史となって、全体を形作っていくんだ。

だから、データとコードの寿命は同じくらい長い、と言えるんだ。


プログラミングは難しい…たぶん。

オブジェクト指向プログラミング、関数型プログラミングアジャイル開発手法、各種設計原則や、テスティングフレームワークを使ったTDD等々…色々なプログラミングを支える要素技術はここ10年で爆発的に進化して、「とりあえず動くものを作る」という段階から、「先を見据えて、ずっと維持できるものを作る」という段階に変わってきたように思える。

きっとそれはシステムが動く環境が変化し、以前のような「動いているものを触るな!」という思想ではとても維持できなくなって、「なにもしていないからこわれました」という時代に変わってきたからだと思っている。

とはいえ、じゃあオブジェクト指向プログラミング言語を覚えて、各種設計原則を頭に畳み込めば魔法のように解決するか?と聞かれればやっぱりそんなことは起きない。

なぜか?

定量的な情報は探しても見つからなかったけど、感覚的にコードは書いている時間より読まれている時間の方が圧倒的に長い、ということに異論は無いと思う。たいてい、プログラムの寿命から言って、新規にコードを書くより既存のコードベースに対して改修を加えていくことが多い。

その時に考えることは、以下のようなことだと思う。

  • 今のコードは何をするのか、どうゆう意図か、理解できたか?
  • 今回、修正すべき箇所はどこか、漏れなく抽出できたか?
  • 既存のコードと、今回の修正コードは整合しているか?
  • 次回修正する際の障害となるような一貫性の欠如は無いか?
  • 上記の情報は、後で追跡可能なように、まとまっているか?

ベストプラクティスとして、影響箇所が局所化されるように疎結合に作っていく、改修漏れが起きないように一貫性を保って作っていく…原則は分かっていても、現実の要求に照らし合わせていくと、必ずしも全てを満足することはできない(満足することができないからこそ、原則に立ち返るのは大事だけど、それはまた別の話)。

ここで唐突に結論めいたことに突入するのだけど、プログラミングは、コードと、データと、改修の歴史の3つの要素が絡み合うと思っていて、結局コードだけ見ても分からないし、データを追加しても分からないし、これまでの改修の歴史まで見て初めて分かることが多い。

いくらテストコードにデータパターンが網羅されていても、結局git blameで改修の歴史を追いかけたことは皆さん有るはずだ。

そして、これらのテクニックは非常にプロジェクトローカルなバッドノウハウになりがちで、技術書や、スクールでも扱われない…みんなゼロからきれいなコードを書くこと、きれいな設計にしないと後で困るよ、という事は言ってくれるは、現実にそうではないコードとの向き合い方は教えてくれない。

リファクタリングの技術は、解説された本も有るけど、きれいではないコードをリファクタリングする前に、まずは理解して直すだけ、というシチュエーションの方が多い…そんな時に、どうすればいいのか?

そんな時に、プログラミングを難しいもの…と感じるのではないか?

ということで、ここではプログラミングを難しくする要素は、既存のコードベースを読み解くことです、しかもコードだけでは駄目で、データも、歴史も見ないと分からないことが難しくしているのではないか?ということを書いてみました。

この辺、もっと事例を挙げてスライドにいつかまとめて発表したいと思っている。


twitter.com

JSONのStringのエスケープ方法

今さらですが、JSONにおける文字列のエスケープ処理について調べたので、そのメモ。


JSONの規格は現在では幾度かの改定を経て、RFC8259にまとまっている。

https://tools.ietf.org/html/rfc8259

文字列については「7. Strings」にまとまっている。あまり長くないので、順番に全部見てみよう。

The representation of strings is similar to conventions used in the C family of programming languages. A string begins and ends with quotation marks. All Unicode characters may be placed within the quotation marks, except for the characters that MUST be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).

必ずエスケープされなければいけない文字として、quotation mark(")と、reverse solidus(\)、the control characters (U+0000 through U+001F)が指定されている。

JSONにおける文字列はquotation markで囲まれ、エスケープreverse solidusを使うからですね。あとは、画面に表示できないthe control characters(改行とか、タブとか)が指定されている。

エスケープされなければならないthe control charactersの範囲としてU+0000からU+001Fまでしか定義されていなくて、DELを示すU+007Fが指定されていないのは何故だろう?

ちなみに、DELがU+007Fに用意されている理由、今回調べて初めて知った。面白い。

https://ja.wikipedia.org/wiki/ASCII

ASCII 127(全てのビットがオン、つまり、2進数で1111111)は、delete(削除文字) として知られる制御文字である。この記号が現れた場合、その部分のデータが消去されていることを示す。この制御文字だけ先頭部分になく最後にある理由は、パンチテープへの記録は上書きが出来ないため、削除する際には全てに穴を空けることで対応できるというところからきている

続いて、エスケープの表記方法へ続く。

Any character may be escaped. If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), then it may be represented as a six-character sequence: a reverse solidus, followed by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. The hexadecimal letters A through F can be uppercase or lowercase. So, for example, a string containing only a single reverse solidus character may be represented as "\u005C".

Unicode基本多言語面(the Basic Multilingual Plane (U+0000 through U+FFFF))は、どれもエスケープ表現OKで、「バックスラッシュ」+「u」+「4文字の16進数」で表現される。

16進数を表すのに使うAからFは大文字でも小文字でも可。

Alternatively, there are two-character sequence escape representations of some popular characters. So, for example, a string containing only a single reverse solidus character may be represented more compactly as "\".

それとは別に、いくつかのメジャーな文字については、2文字でのエスケープ表現が用意されている。おなじみのバックスラッシュで始まる表記。

コード 文字 2文字でのエスケープ表記 6文字でのエスケープ表記
U+0022 quotation mark \" \u0022
U+005C reverse solidus \\ \u005C
U+002F solidus \/ \u002F
U+0008 backspace \b \u0008
U+000C form feed \f \u000C
U+000A line feed \n \u000A
U+000D carriage return \r \u000D
U+0009 tab \t \u0009

通常文字列の中に入る制御文字といえば、タブと改行くらいなので、UTF-8が取り扱える環境であれば、ほぼ6文字でのエスケープ表記は不要になるように、イイ具合に対象の文字が選定されているのが分かる。

ここで一つポイントなのは、いくつかの実装でsolidus(スラッシュ)がエスケープの対象でなかったり、オプションだったりすること。JavaScriptの規格であるECMA-262ではエスケープ表記の対象にsolidus(スラッシュ)は含まれていなかったけど、最初の独立したJSON規格であるRFC 4627以降では対象に含まれている、という違いが有る。

正規表現の開始と終了の文字としてよく使われることからエスケープ対象になったのか……正確な経緯はわかりませんでした。ご存知の方、教えてください。

また、JavaのStringクラスもスラッシュを除くと、同じ2文字表記のエスケープを扱える(6文字の表記も扱える)。しかし、スラッシュの2文字表記は扱えないので、注意が必要(エラーとなる)。

To escape an extended character that is not in the Basic Multilingual Plane, the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. So, for example, a string containing only the G clef character (U+1D11E) may be represented as "\uD834\uDD1E".

基本多言語面を越えるものはUTF-16サロゲートペアに該当するものは12文字で表現される。


では、おなじみPerlでの実装を見てみましょう。

https://metacpan.org/release/JSON-PP/source/lib/JSON/PP.pm

エスケープに関する変換を行なっているのは、string_to_jsonという関数です。

my %esc = (
    "\n" => '\n',
    "\r" => '\r',
    "\t" => '\t',
    "\f" => '\f',
    "\b" => '\b',
    "\"" => '\"',
    "\\" => '\\\\',
    "\'" => '\\\'',
    );

sub string_to_json {
    my ($self, $arg) = @_;
 
    $arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
    $arg =~ s/\//\\\//g if ($escape_slash);
    $arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
 
    if ($ascii) {
        $arg = JSON_PP_encode_ascii($arg);
    }
... 

    if ($utf8) {
        utf8::encode($arg);
    }
 
    return '"' . $arg . '"';
}
  • 最初に、2文字形式のエスケープ処理する文字を優先的にreverse solidusでエスケープ
  • 次に、solidusがエスケープ対象と設定されていれば、2文字形式でエスケープを実施
  • 続いて、必ずエスケープが必要なthe control characters (U+0000 through U+001F)のうち、先の二つのステップでエスケープ処理をしていない文字を6文字の形式でエスケープ
  • 最後に、文字コードに合わせて処理の振り分け…US-ASCIIだけで表現したい場合は、U+0080以降の文字を全て6文字の形式でエスケープする
  • サロゲートペアに該当する文字は、12文字の形式でエスケープ
*JSON::PP::JSON_PP_encode_ascii      = \&_encode_ascii;

sub _encode_ascii {
    join('',
        map {
            $_ <= 127 ?
                chr($_) :
            $_ <= 65535 ?
                sprintf('\u%04x', $_) : sprintf('\u%x\u%x',_encode_surrogates($_));
        } unpack('U*', $_[0])
    );
}

sub _encode_surrogates { # from perlunicode
    my $uni = $_[0] - 0x10000;
    return ($uni / 0x400 + 0xD800, $uni % 0x400 + 0xDC00);
}

サロゲートペアの場合の、UTF-8からの変換方法がスマートだ。

ただし、JavaScalaなどの言語では、最初からUTF-16のコードポイントで取り出すので、1コードポイントずつ変換していけば良い。


意外と知らないことが有って調べてみると、学びが有った。

充電用のUSBケーブルの長さについて考える

充電用のケーブル、いざ買おうと思うとユースケースによって、必要な長さが全然違うし、だからと言って一番長いやつ!ってのもアレだったりするので、適切な長さについて考えた。