最近のcarvo: pecoとchoをくっつけた

ブログで紹介するのはもう1年ぶりぐらいになるかな、と思ったけど直近だと去年の11月に書いていた。

note103.hateblo.jp

とはいえ、リポジトリのほうを更新するのは久しぶり。

このリポジトリ、README.mdもChangelogも前バージョンから書き換えておらず、純粋にlib以下のコードを「こんなに頑張りました!」と公開することだけが目的のような状況なので、リンクを張るかけっこう迷ったものの、まあ失うものもないし・・ということで。

一番大きな変更としては、記事タイトルでも示したように、pecoとchoという二つの優れたコマンドライツールを組み合わせて、選択系の作業をより直感的にできるようにしてみた。

手法としては、少し前に紹介したこちらとほぼ同じ。
note103.hateblo.jp

前回紹介したときのデモはこんな感じだったが、

gyazo.com

その後こんな感じになり、

gyazo.com

最近はこんなふう。(これでも最新ではないが)

gyazo.com

ひとつ前のバージョンでは、出てくる選択肢の頭にアルファベット(f,g,h,k,l)を置いておいて、それをタイプして回答を選択する方式を取っていたけど、今回からはpecoやchoで直接それらを選択する。

最後のデモにあるように、簿記の勘定科目もコンテンツとして取り入れている。(ただし、上記リポジトリにはそのカードは入れてない。入れ忘れた)

なお、最初のデモにある「英単語の頭数文字をヒントとして出し、それを見て回答(英単語)を入力する」という回答方式もやはり面白いので、最新版ではその方式も選択できるようにしている。これについては後日また紹介したい。

英語や簿記の勉強じたいは進んでやろうというものでもないのだけど、こうしてプログラミングの素材として取り入れると、その動作テストの流れで必然的にそれらを目にすることになるので、まったくやらないよりはマシという感じになる。

ひとまずそのようにだましだまし、プログラミング&英語&簿記の勉強をしつつ、いずれ何かのはずみで集中できそうになったら流れに乗ってきちんと勉強したいものだと考えている。

21世紀の文字起こし

気づき

少し前にこのようなことに気がついた。

「いずれそうなるだろう」とは思っていたが「まだしばらく先のことだろう」とも思っていた現実が、想像していたよりずっと間近に迫っていた、ということに驚いた。

さらに言えば、このときはiPhoneに向かって喋ったらみるみるテキスト化されていくので「なんだこれ……!」と衝撃を受けたのだったが、実際にはその後に起こったことのほうがすごくて、Googleドキュメントの音声入力機能と、SoundflowerというMacのアプリケーションを組み合わせたところ、もはやわざわざ喋らなくてもMacで再生した音声ファイルがそのままGoogleドキュメント上でテキスト化されてしまい、ひっくり返った。

gyazo.com

これは今年の初めに吉祥寺.pmで発表したときの録音を、後述の方法で読み込ませているところ。

日本語だとちょっともっさりした感じだが、英語だとこうなる。

gyazo.com

むちゃくちゃ速い。よく見ると、ところどころで誤認識をしているのだけど、手で打ちこんだ場合との違いを考えれば充分許容ではないかと感じる。

また日本語にしても、丁寧かつ明瞭に発声した音声ファイルであれば、上に示した日本語版の動画より速く、よどみなくテキスト化されていく。

そもそも文字起こしとは

さてしかし、ではそれがどれだけ実用的なのかというと、まだ業務の現場を劇的に変革するほどではないとも感じる。

冒頭のツイートをしたときには「未来はすでに来ていた! 今この瞬間から、文字起こしのやり方を変えなければ!」ぐらいに思ったが、それは半分正しく半分期待しすぎだった。

どういうことかと言うと、まずそもそも文字起こしという作業は、以下のような工程で構成されている。(あくまでぼく個人の場合)

  1. 音声を聴きながら、抜け漏れやタイプミス込みでよいので、一旦最後まで音を止めずに起こしていく。
  2. 2周め以降は一時停止や巻き戻しの回数を増やしながら、ひとまずすべての音声をテキスト化する。
  3. 2の仕上がりには不要な語句や倒置が多いので、文章として読みやすくなるよう修正していく。

文章を仕上げるためには、さらに

4. 全体の構成を工夫して読み物として仕上げる。

という工程が必須になるが、それはもう「文字起こし」ではなく「編集」である。*1
よって、ぼくの考える「文字起こし」は上記の3まで。

※ちなみに、3と4の違いがわかりづらいかもしれないが、3の目的は「人間が読みやすい状態にすること」で、4の目的はそれを「良いもの」にすることである。

その1〜3のうち、機械が担当できる作業は人間の判断が不要な1と2までだが、今回話題にしている音声入力技術にできることは、「1の精度をちょっと良くしたもの」であり、2はできない。

なぜ2が「できない」のかというと、2をやるためにはすでにテキスト化された部分のうちどこが抜け漏れなのか、あるいは間違っているのか、という判定や検討が必要で、しかしここで対象としている機械にはその機能がないからである。

好意的に、期待を込めて言い換えれば、「1」の精度が高くなれば「2」は不要になる。つまり現状の先にあるのは「1+2」を機械が終わらせてくれることだが、現時点ではせいぜい「1プラスアルファ」しかできない、ということ。

加えて、その「1プラスアルファ」を機械にさせるために必要な準備もけっして少なくはない。
よって、その導入コストを許容範囲内の投資と捉えるか、「そこまで面倒なら今は手を出さないよ」と捉えるかによって対応は異なるだろう。

ではその「「1プラスアルファ」をさせるために必要なこと」とは何かというと、以下のようなことである。

  1. 素材の音声を聞きながら、明瞭な調子で新たな録音を行う。
  2. それをGoogleドキュメントに読ませる間、読み込みが途中で止まってしまったら(数秒〜数十秒に一度止まる)すぐに再起動させられるよう、つきっきりで世話をする。

これをやれば、通常の文字起こしによる上記工程の1と同等か、それよりちょっとマシ、というぐらいのものができる。

また、導入を検討する際の主な要素には、そうした「精度」の他に、「かかる時間」や「労力」もある。

かかる時間に関しては、たとえば60分の音声ファイルを扱う場合、通常の工程であれば60分強で済むことになるが(あくまで1の作業のみ)、音声入力の工程だと「再録音」と「音声を読み込ませる」工程が必要なため、少なくとも素材音声の倍、あるいは2.5倍〜3倍程度の時間がかかるかもしれない。

一方、「労力」に関して言うと、音声入力の工程における「再録音」は「音を聴きながら音を発声する」という音声同士の変換行為であり、これはその次の「つきっきりで再読み込みさせる」という作業にも言えることだが、頭や体への負荷が少ない単純作業である。

これが通常の文字起こしだと、「音を聞きながら文字を書いていく」という「音(聴覚)→文字(視覚)」の変換作業を行うため、そうした「異なる次元の感覚を駆動する」ことが独特の疲労につながる。

たとえてみると、音声入力における各工程は「一つのことを上手くやる」というUNIX的な行為であり、通常の文字起こしはオーケストラの指揮者がやるような、様々な種類の異なる作業を並行して進めるマルチタスク的行為である。

さらに言えば、音声入力ではタイピングが不要である。よって、体への負担という意味では比較の余地もなく音声入力のほうがラクである。

そもそも文字起こしとは(2)

ではそのようなメリット・デメリットを踏まえ、現時点でぼくはどう結論を出しているかというと、可能なかぎり機械の力を借りるべきだと考えている。

もしかすると、トータルのスピードはまだ、すべて人力でやったほうが速いかもしれない。しかし、上述のとおり人力による文字起こしは非常に疲れるため、それをやれば他のことができなくなるか、同等のなんらかの影響が生じることになる。

そしてそもそも、というかこれが一番大事なことなのだが、文字起こしは人間がやるような仕事ではない。
人間がやるべきことはその後の文章構成、つまり元々の話された内容を、テキスト上でどのように再構成するか検討・判断する作業であって、その前の段階まではさほど重要なものではない。*2 *3

そしてそのように、「さほど重要ではない」にもかかわらず、現在通常の工程として行われる文字起こしという作業はなかなか過酷で、見返りも少ない。

朝9時からスタートして、その日の18時までやればそれなりの進捗は出る。2時間程度の素材なら、その調子で3日もやれば充分終わるだろう。
しかし、その代償は大きい。やったことのある人にはわかるだろうが、非常に多くのものを捧げて、それはようやく仕上がる。

だからぼくとしては、もうそういうことはなるべく人間がせずに済むようになってほしいと考えている。
現時点では、音声入力のメリットはまだわずかなもので、むしろ後述するような導入コストを考慮すれば、人によってはデメリットのほうが大きくすらあるかもしれないが、いずれは比べるまでもないほどメリットのほうが大きくなるだろう。

音声入力による文字起こしの実践法(Mac)

以下、今回試みた具体的な音声入力の作業工程を示しておく。

先に概要を示すと、次のような工程をたどる。

  • 1. 素材音声の再録音
    • 大元の音声ファイルを読み込ませても精度が低いため、読み取り用に同じ内容を自分で喋り、それを録音する。
  • 2. Soundflowerの準備
    • Soundflowerというソフトウェアを使うと、事前に録音しておいた音声ファイルをテキスト化させることができる。その導入手順。
  • 3. Mac内部で再生+聞き取り
    • 音声入力をするまで。
  • 4. 音声ファイルが終わるまで再読み込みなどのケア
    • 数秒〜数十秒ごとに音声入力が止まってしまうので、その対応について。

なお、この手順はすべてぼく個人の環境に依存しているので、同様のことができない人はそれぞれの環境に置き換え・読み替えてほしい。

1. 素材音声の再録音

まずはGoogleドキュメントに読み込ませるための音声ファイルを作成(再録音)する。

「機械の力を借りるべき」などと言うわりにずいぶんアナログな工程だと自分でも思うが、食洗機に入れる前に軽く油汚れなどを落としておくようなものだと思えばよい。
マシンをより効率的に活用するためのハックとも言える。

後述のように、とくに専用ソフトなどは使わずに行うことも可能だが、ぼくの場合はMacのExpress Scribeという文字起こし用のソフトで、通常の80%程度に遅く設定した上で元の音声ファイルを再生し、それをイヤホンで聴きながら、手に持ったICレコーダーへ発声して録音していく。

通常の再生速度であればこの作業自体が早く終わるという利点があるが、聞き取れずに後戻りしなければならなくなる可能性も高まること、また逆に、遅くしすぎると作業自体がなかなか終わらなくなるので、総合的に見て80%程度が最適かと思っている。

と同時に、環境によってはわざわざ再生速度を調整しなくてもよいとは思う。
たとえば、iPhoneからイヤホンで対象の音源を聴きながら、手元のICレコーダーに喋って録音していくような方法であれば、コンピューターに縛られず作業できるのでそれなりのメリットもあるし、実際そのように試したがけっこう快適だった。

聞き落としによる後戻りの可能性などを考慮しない場合には、それもありだと思う。

2. Soundflowerの準備

Googleドキュメントに音声ファイルを読み込ませるために、Soundflower というソフトウェアを使う。

このソフトは本来、Macから流れる音声をそのまま(スピーカーを通さずにマシン内で)録音するためのものだが、今回の用途にも適している。

導入方法を検索すると、新旧様々な紹介記事が出てくるのでかえってわかりづらいのだけど、最近のヴァージョンを扱った記事としては以下のまとめが詳しかった。多謝。

インストールが完了したら、Macの環境設定/サウンドか、Optionを押しながらメニューバーのスピーカーマークを押して、以下のように設定する。

f:id:note103:20160710040842p:plain

入出力ともにSoundflowerにするのがポイント。なお、2chと64chの違いは理解していないが、2chでとくに問題ないのでそのようにしている。

3. Mac内部で再生+聞き取り

Googleドキュメントで、記録するための新規ファイルを作成する。

※この際、ブラウザはGoogle Chromeを使用する。Firefoxでも試したのだけど、下記の「ツール/音声入力」という項目名が、確認はできるものの選択できない状態になっていた。その他のブラウザは未確認。

f:id:note103:20160710125926p:plain

ドキュメントを作成したら、ファイル内のメニューバーから、ツール/音声入力の順で選択。

f:id:note103:20160710125940p:plain

マイクのボタンが出てくる。

f:id:note103:20160710125948p:plain

ボタンをクリックすると、赤くなって入力待機状態になる。

f:id:note103:20160710125958p:plain

このとき、すでに音声が流れていれば、自動的に入力が始まる。

f:id:note103:20160710130014p:plain

同じボタンを押すか、一定時間無音が続くと、自動的に録音は止まる。

なお、録音中に別のアプリケーションへ移っても、録音は止まってしまう。
よって、録音を開始してから音声を再生したい場合は、アプリケーションを切り替えなくてもキーボードから再生できるように準備しておくとよい。

また、上記2の設定が済んでいれば、再生しても音が外には出てこない。

音が聞こえないので、再生されているのか少し不安になるが、プレイヤーの表示を見れば秒数が増えていくのを認識できるので、それも目に入るようウィンドウを並べておくとよいだろう。(というか、ぼくはそうしているということ)

4. 音声ファイルが終わるまで再読み込みなどのケア

前述のように、入力は音声ファイルが止まるまで続くわけではなく、途中で止まる。いわばフリーズしたような状態になる。
調子が良ければ1分以上入力され続けることもあるが、平均的には40秒程度かもしれない。

困るのは、入力がフリーズしてもそれが視覚的にわからないことだ。
マイクは赤く待機状態を示したままなので、しばらくすれば入力が再開されるのかな、と思うが、止まったままのこともあればやっぱり動き出す、ということもある。

よって2秒程度入力が止まったら、もうフリーズしたとみなして再起動する。
具体的には、マイクボタンを押して一度入力機能をストップし、もう一度押して再開させる。すると、また入力が始まる。

※上のスクリーンショットでも確認できるが、再読み込みは「コマンド+シフト+S」のショートカットキーでも可能である。というかぼくはそれを使っている。

そしてこの再読み込み作業をファイルが終わるまでくり返す。

ちなみに、音声ファイルの音質が粗かったり、喋るスピードが速かったりすると頻繁にフリーズするという印象がある。

その意味でも、音質を最大限改善するために録音し直しておくことは有効だと考えているし、逆に言うと、この聞き取り能力が向上すれば、再録音の必要はなくなるかもしれない。

また、同じくフリーズの頻度を下げる目的で、再録音の際に使用したExpress Scribeをここでも使い、再生速度を少し遅くした上で読み込ませている。

まとめ 〜そしてtextlint編へ〜

音声入力による文字起こしの工程は以上。

ちなみに、ここまでの話ではとくに触れなかったが、冒頭の動画で示したように英語の音声は再録音をしなくても*4かなりのスピードでテキスト化されていくので、音質さえよければその工程は省略できるだろう。

しかし日本語で語られた音声ファイルの場合は、その再録音や最後の再読み込み作業は避けがたい工程かなと思っている。(いずれは機能の向上にしたがって不要になるかもしれないし、それを期待するけれど)

ところで、じつはこの話題はここで終わりではなくて、前半で挙げた文字起こしの3工程のうち、

  1. 音声を聴きながら、抜け漏れやタイプミス込みでよいので、一旦最後まで音を止めずに起こしていく。
  2. 2周め以降は一時停止や巻き戻しの回数を増やしながら、ひとまずすべての音声をテキスト化する。
  3. 2の仕上がりには不要な語句や倒置が多いので、文章として読みやすくなるよう修正していく。

ここまでに紹介した音声入力が担うのは1だが、続く2と3の作業を軽減・サポートする技術として、このブログでも何度か取り上げた「textlint」がある。

ぼくにとっての未来の文字起こしは、そのtextlintを音声入力と組み合わせることによって成立するのだけど、すでにだいぶ長くなったので、この記事では音声入力についてまでとする。

textlintを効率的な文字起こしに際してどう使うか、という話はまた機会ができたときに。

*1:あくまで便宜的な腑分けだが。

*2:と言いつつ、実際にはそうした構成作業にしてもいずれは機械がやってくれるかもしれないとは思っているのだけど。人間に残されるのは、そうして出来たものをユーザーとして「楽しむ」ことだけになるかもしれないし、それならそれで、まあ構わない。

*3:さらに注釈を重ねると、じつはぼくとしても重要な原稿は文字起こしから自分で担当してしまったほうがいいと考えている。作業としてはヘビーだが、それによって元の内容を理解しやすくなるし、何より結局その後の編集作業を自分でやるのであれば、初めから自分の方針に沿って作られた文字起こしを元にしたほうが余計な修正が生じずラクだからである。

*4:むしろ自分で再録音したら発音が不正確で精度が落ちそうだ。というか落ちるだろう。

Vim の地味だけどよく使う設定

普段 Vim を使っていて、つくづく「このマッピング便利だな〜」と思うものをご紹介します。

空行・スペース処理

1行ごとに空行を入れる

このような設定で。
※以下、「<Leader>al」などのマッピングは説明用の一例です。

nnoremap <Leader>al  :%s/$/\r/gc<CR>
vnoremap <Leader>al  :s/$/\r/gc<CR>

f:id:note103:20160706002100g:plain

選択するとその範囲だけ、選択しなければバッファ全体を対象として、1行おきに空行を入れていきます。

空行をカットする

上記の逆。

nnoremap <Leader>dl  :%s/^$\n//gc<CR>
vnoremap <Leader>dl  :s/^$\n//gc<CR>

f:id:note103:20160706010907g:plain

複数行でも一気に詰めてくれます。

行内のスペースをカットする

nnoremap <Leader>db  :%s/\s\+//gc<CR>
vnoremap <Leader>db  :s/\s\+//gc<CR>

f:id:note103:20160706002336g:plain

まれに使います。

全角英数字を半角にする

普段はあまり遭遇しないものの、それだけにたまにぶつかると対応コスト・負担が大きい案件。

マッピングを設定しておいて数秒で解決するとストレスが最小限で収まります。

nnoremap <Leader>zh :HzjaConvert han_eisu
vnoremap <Leader>zh :HzjaConvert han_eisu

f:id:note103:20160706002409g:plain

これは選択範囲のみ有効なので(選択しなければカーソル行のみ)、バッファ全体に適用したい場合は全体を選択してから実行します。

f:id:note103:20160706002424g:plain

ちなみに、ほとんどやりませんが逆(半角→全角)も一応設定しています。

nnoremap <Leader>hz :HzjaConvert zen_eisu
vnoremap <Leader>hz :HzjaConvert zen_eisu

全角<=>半角の変換については以下が大変詳しい&わかりやすいです。
nanasi.jp

Markdown記法のリスト化

メモや議事録、週報など、Markdown記法でドキュメントを作成する機会が多いので、もしかすると現在一番使っているかもしれない地味設定。

直下の行頭にハイフン+半角スペースを入れる。

nnoremap <Leader>rh o<ESC>I- 

リスト行の直下に同じくハイフン+半角スペースを入れる。

nnoremap <Leader>ri A<CR>

1段深くインデントしてからハイフン+半角スペースを入れる。

nnoremap <Leader>rt A<CR><ESC>I<TAB><ESC>A 

f:id:note103:20160706002545g:plain

なお、Markdown まわりのサポート・プラグインとして以下を利用しているので、

上記はそれを前提としたマッピングです。

プラグインに関しては以下でも少し触れました。
note103.hateblo.jp

また、ぼくが主に使うファイル形式はじつは Markdown ファイル(.mdなど)よりもテキストファイル(.txt)なので、テキストファイルでも Markdown として振る舞うよう、「~/.vim/filetype.vim」の中に以下のような記述を入れています。

if exists("did_load_filetypes")
  finish
endif

augroup filetypedetect
  au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn,txt,text,html}   set filetype=markdown
augroup END

この辺の設定については vimdoc の以下あたりが詳しそうです。

日時をすぐに出す

これも日報、週報的な記録を取る際に重宝しています。ほぼ毎日叩いているかも。

inoremap <expr> ,df strftime('%Y-%m-%d %H:%M')
inoremap <expr> ,dd strftime('%Y-%m-%d')
inoremap <expr> ,dt strftime('%H:%M')

曜日もすぐに出す

let weeks = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]
let wday = strftime("%w")
inoremap <expr> ,ds strftime('%Y-%m-%d ').weeks[wday]

f:id:note103:20160706002749g:plain

便利。曜日の部分以外は、以下のムックから学びました。

開発ツール徹底攻略 (WEB+DB PRESS plus)

開発ツール徹底攻略 (WEB+DB PRESS plus)

曜日の部分はいろいろ検索して作りました。先人に多謝。

.vimrc をすぐ操作する

上記のムックにも近いことが書かれていましたが、まず簡単に vimrc を操作できるよう、現在使っているウィンドウに vimrc を出すマッピング

nnoremap <silent> <Leader>. :<C-u>sp ~/.vimrc<CR>
nnoremap <silent> <Leader><Leader>. :<C-u>edit ~/.vimrc<CR>

Leader を1回叩いたら下方に画面分割で、2回叩いたらウィンドウ全体が vimrc ファイルになります。

そうしてチャチャッと書き換えたら、すぐに再読み込みできるように以下も設定。

nnoremap <Leader><C-e> :source ~/.vimrc<CR>

一時的なゴミ箱ファイルをすぐに出す

画面分割ワザの応用ですが、普段テキストの編集作業をしていると、「ん〜、この辺りの説明、冗長だからバッサリカットしてもいいんじゃないかな……でももしかしたらやっぱり必要かもしれないから、一旦別の場所に避難させておきたいな……」と思うことが少なくありません。

そのようなときに、一時的に不要な文章を保管しておけるファイルを作っておいて、それをいつでも呼び出せるようにする設定。

nnoremap <Leader><C-t> :<C-u>sp /path/to/trash.txt<CR>

ここでは「trash.txt」というファイル名にしていますが、それがウィンドウ下方にパッと出てくるので、その中に不要な文章をカット&ペーストで移動。(大抵は最下行)

f:id:note103:20160706183848g:plain

一方、こうした部分的な保存ではなく、バッファ全体をとりあえず保存しておきたい、という場合もあります。
そのようなときは、以下の方法を使います。

今見ているバッファを現在時刻のファイル名で保存する

:w 版

function! s:wsave()
  execute ":w /path/to/".strftime('%Y-%m-%d-%H-%M-%S').".txt"
endfunction
nnoremap <silent> <Leader><Leader>w :<C-u>call <SID>wsave()<CR>

:f 版

function! s:fsave()
  execute ":f /path/to/".strftime('%Y-%m-%d-%H-%M-%S').".txt"
endfunction
nnoremap <silent> <Leader><Leader>f :<C-u>call <SID>fsave()<CR>

あちこち調べて、関数で設定してみました。

なお、最近の Vim では undo オプションが大変強力で、ぼくも以下のように設定していますが、

if v:version >= 703
  set undofile
  set undodir=~/.vim/undo
endif

そして実際、これによってかなり以前の状態までさかのぼれますが、それでも時々期待の動作をしないこともあるので、「今見ているバッファ、万一のために別名で保存しておきたい……!」というときには上の保存コマンドをパパッと叩いておきます。

実際にはそのように保存したファイルを使うことはほぼありませんが、無用な心配事が減るので、実用上のメリットというより精神的なメリットがある設定です。

追記: 2016-12-30
この方法については、その後に改訂版を作成して以下で紹介しました。
Vimで今見ているバッファを「現在日時+好きなファイル名」で保存する - the code to rock

簡単に連番を振る

最後に、これは vimrc の設定ではありませんが、よく使うのでシェアさせて頂きます。
何気に使う場面が多い&むちゃくちゃ便利。

f:id:note103:20160706003059g:plain

情報源は以下の @thinca さんの回答より。
ja.stackoverflow.com

Vim 7.4.765 以降であれば、対象をビジュアルモードで選択して g<C-a> をすると連番が生成されます。

知らなかった〜……。

まだ他にもいろいろありそうな気はしますが、ひとまず以上です。

OCamlのインタプリタで日本語の文字を扱う

あれはおそらく2013年、Perl入学式に初めて参加する直前頃だったと思うのだけど、以下の本を購入し、

プログラミングの基礎 (Computer Science Library)

プログラミングの基礎 (Computer Science Library)

そこに示されるまま、OCamlインタプリタで日本語を出力するためにターミナルの文字コードEUCに設定したら、その後に行ったPerl入学式で出力が文字化けしてしまい、「どうしてですかね……?」と聞いたら「端末の設定が問題なんじゃないかな!?」とサポーターさんから指摘され*1、「ああ、あのOCamlの設定でやったやつか……」と、おかげで少なからぬ時間を費やしてしまったことがあまりよくない印象として残ってしまい、その後同書を開くことはほとんどなかった。

しかし最近、以下の記事を読んで、

これといった目的もなくプログラミングを始めたいなら、静的型付き言語で始めるほうがいろいろ実りがあると思います。

とのこと。

なるほど、そういえばOCamlの本、以前買ったのがあったな、ちょっと読み直してみよう。と思って、その後なんだかんだで3年間趣味プログラミングを続けてきた今の目で読んでみると、読みやすいし、面白そうだし、期待できそう。
さすが様々な機会で良書として推薦されるだけのことはある。

……しかし、件の文字コード問題がなあ、と思ってその部分をやってみると、やはり普段使っているUTF-8の設定だと文字化けする。指示のとおり端末の方でEUCに設定してしまうことも可能だけど、他のコードを扱うときのことを考えると受け入れがたい。

インタプリタにこだわらず、ファイルにコードを保存して実行すれば問題ないのだけど、同書の構成としては前半しばらくの間はインタプリタ操作を前提に進んでいくので、できればそれに沿って読み進めたい……などとジレンマ的な状況に陥りつつ、しかしこんなメジャーな(と言っていいだろう)言語でこんなしょうもないことが放置されているはずがない、と思っていろいろ検索してみたら、ようやくわかった。端末を直接設定する以外の方法で、比較的手軽に文字化けを回避する方法が。

Toplevel (REPL) での文字化けを防ぐ

内部的には壊れているわけではない EUCUTF-8 の文字列ですが、出力が狂ってしまうのは不便です。 これは OCaml の string プリンタを変更することで回避することができます:

EUC もしくは UTF-8 環境:
# let print_non_escaped_string ppf = Format.fprintf ppf "\"%s\"";;
val print_non_escaped_string : Format.formatter -> string -> unit = <fun>
# #install_printer print_non_escaped_string;;
# "こんにちは";;
- : string = "こんにちは"

ここでは 文字列をエスケープせずに標準出力に出力する関数、 print_non_escaped_string を定義し、 それを #install_printer ディレクティヴによって string 型のプリンタに指定しています。 これにより文字列を ISO-8859-1 とみなしたエスケープが行なわれなくなります。

Toplevel で日本語を含んだ文字列などを多用する場合は、毎回この内容を打ち込むのをさけるために、 次の内容を OCaml toplevel が起動時に実行するファイルである .ocamlinit に 書き込んでおくとよいでしょう:

let print_non_escaped_string ppf = Format.fprintf ppf "\"%s\"";;
#install_printer print_non_escaped_string;;

ということで、最後に示されている2行を .vimrcや.bashrc と同様に、.ocamlinitとしてホームディレクトリに置いたら、インタプリタでも日本語が普通に出力されるようになった。

OCamlに関する日本語で書かれた情報じたいはけっして少なくないのだけど、本件についてはけっこう限られていて、なかなか大変だった。
そのような中、上記のリポジトリのほか、以下でも同様の言及があり、参考になりました。ありがとうございます。

*1:おぼろげな記憶だけど、それはたしかYAPC::Asiaのイベント内で開催されたPerl入学式で、このときに相手をしてくれたのはウズラさんだった気がする。

C言語の航海日誌(5)〜文字列をポインタで渡す〜

前回の知見と繋がりながらもちょっとズレる発見があったのでメモします。

note103.hateblo.jp

まず前回の復習をしておくと、int型の数値をこんなふうにポインタで渡す、ということをやった場合。

#include <stdio.h>

void foo(int *c, int *d)
{
    printf("*c: %d\n", *c);
    printf(" c: %p\n",  c);
    printf("*d: %d\n", *d);
    printf(" d: %p\n",  d);
}
int main(void)
{
    int a = 33;
    int b = 55;
    printf(" a: %d\n",  a);
    printf("&a: %p\n", &a);
    printf(" b: %d\n",  b);
    printf("&b: %p\n", &b);

    printf("\n");

    foo(&a, &b);

    return 0;
}

実行。

 a: 33
&a: 0x7fff5377a60c
 b: 55
&b: 0x7fff5377a608

*c: 33
 c: 0x7fff5377a60c
*d: 55
 d: 0x7fff5377a608

こんな感じで、メイン関数からはアドレス(&a, &b)を投げているのに、受け取る側のfoo関数では数値(*c, *d)で受け取っているので、直感的じゃないなあ・・という結論だったのですが。

同様の感覚で文字列を渡そうとしたらハマりました。
文字列で同様のことをしようと思ったら、こうなるようです。

#include <stdio.h>

void foo(char *c, char *d)
{
    printf(" c: %s\n", c);
    printf(" c: %p\n", c);
    printf(" d: %s\n", d);
    printf(" d: %p\n", d);
}
int main(void)
{
    char a[] = "apple";
    char b[] = "orange";
    printf(" a: %s\n",  a);
    printf("&a: %p\n", &a);
    printf(" b: %s\n",  b);
    printf("&b: %p\n", &b);

    printf("\n");

    foo(a, b);

    return 0;
}

実行

 a: apple
&a: 0x7fff59a3360a
 b: orange
&b: 0x7fff59a33603

 c: apple
 c: 0x7fff59a3360a
 d: orange
 d: 0x7fff59a33603

ということで、数値の場合は「*c」で受け取ったものが値(「33」)で、「c」はアドレス(「0x7fff5377a60c」)でしたが、今回は値(「apple」)もアドレス(「0x7fff59a3360a」)も「c」でした。

じゃあ、文字列を渡したときの「*c」には何が入ってるの? と思って見てみると、

#include <stdio.h>

void foo(char *c)
{
    printf("*c: %c\n", *c);
}
int main(void)
{
    char a[] = "apple";

    foo(a);

    return 0;
}

実行

*c: a

ということで、「apple」の最初の1文字が入っていました。

これはCにおける文字列がじつはchar型の配列で、その先頭要素が出てきているということだと思いますが、ともあれ問題は、「文字列を参照渡しした場合には、数値のように * が付いた変数に値が入っているわけではなく、* を取ったほうの変数に入っている」ということで、まあそういうものだと覚えればいい気もしますが、あとで回収すべき宿題が増えた感じでもあります。

正規表現の最短マッチに関するVimとPerlの違い

以前にも近い話題でひとつ書いたのですが、

note103.hateblo.jp

それと重なりながらもちょっとズレるトピックでけっこうハマったので、おもに未来の自分用にまとめておきます。

例題

以下の文字列に対して、最初の apple だけマッチさせるパターンを考えてください。

appleorangeapple

という問題があったとき、「最初の apple だけ」ということは、いわゆる「最短マッチ(非欲張り型)」のパターンを考えることになりますから、Perl だとこんな感じで作れます。

Perl

my $foo = 'appleorangeapple';
say "最短マッチ: $1" if $foo =~ /^(.*?e)/;
say "最長マッチ: $1" if $foo =~ /^(.*e)/;

実行。

最短マッチ: apple
最長マッチ: appleorangeapple

パターンの部分だけを取り出すと、こんな感じになります。

/^.*?e/

Vim

次に、Vimで同じことをやりたい場合、通常のVim正規表現だと、丸括弧などのエスケープ処理がPerlのそれに比べてかなり面倒なため、ここではそうしたVim特有のクセをなるべく排除できるように \vパターンスイッチを使います。

\vパターンスイッチとは何か? ということについて、Vim教科書のマスターピースこと『実践Vim』から説明を抜き出すと、

\vパターンスイッチを使うと、すべての特殊記号に関する規則を正規化できる。(略)
\vパターンにより、Vim正規表現エンジンの振る舞いはPerlPythonRubyにより近いものに切り替わる。それでも違いはあるけれど。
(p244)

とのこと。

ぼくは少し前までは、「Vim正規表現にはいろいろクセがあるけれど、これを体得してこそ真のVimmerになれるはず……」と思って一つ一つ地道にエスケープしていましたが、最近になって「そろそろラクになってもいいのでは……」と思って\vを使うようになりました。

するとたしかに、大半のケースにおいてはそれでほぼ、Perlで使うのと同様の結果を期待できるのですが、今回とり上げる最短マッチはその例外、つまり上の説明で言うところの「それでも違いはあるけれど。」にあたるようで、Vimで\vパターンスイッチを付けながら同じことをやろうとすると、こんな感じになります。

/\v^.{-}e

先ほどのPerlの場合とパターンの中身だけ並べてみましょう。

^.*?e
^.{-}e

Perlでは「*?」となっていた部分が、Vimだと「{-}」になっています。

ちなみに、最長マッチのほうでは、PerlVimもこれで行けます。

^.*e

つまりここでの違いは、Vimにおいては「?」を最短マッチの道具として使えない、ということですね。

もう少し具体的に言うと、Vimでは単純に「?」の代わりに何かを使うとか、「?」をエスケープするとかではなく、「*?」を「{-}」で代替するという、なかなか自然には想像のつきづらい規則だったので、把握するまでにけっこうな時間を要しました。

ちなみに、Perlにおける「.+?」はVimだと「.{-1,}」になるようです。

このあたりの情報については、前回の記事でも謝辞とともに紹介しました以下のサイトのまとめが非常にわかりやすかったです。

実際の用途としては、ひとまずVimでは「.{-}」がPerlにおける「.*?」の代わりになる、とだけ覚えておけば大半の状況は乗り越えられそうな気がしますが、とはいえ1年で何回使うかわからないぐらいレアなパターンかもしれないので、忘れてしまってもすぐに思い出せるよう、ここにまとめておきました。

実践Vim 思考のスピードで編集しよう!

実践Vim 思考のスピードで編集しよう!

変数は「箱」か?(2)

以下の記事の続編です。
note103.hateblo.jp

目的を共有する

前回の記事はわれながら丁寧に書いたなと思っていて、その時点で書き残したことはほとんどなかったのですが、ただ全体が長かったせいか、ちょっと意図が伝わっていないかな、と思える反応をいくつか見かけたので、まずは以下のまとめ部分を中心に、そもそもの目的について簡単に補足してみたいと思います。

変数は「箱」か? という問いに対しては、そうとも言えるし、そうじゃないとも言えます。

しかしいずれにせよ、大切なことは、「それが入門者にとって効果的なのかどうか」を考え続けることだと思います。
「とりあえずこんな風に説明しておけばいい」という標準的な例を洗練させながら、最終的には、その瞬間に目の前にいる「教わる人」に最適な説明を考えていくべきでしょう。

この最後の1行では、二つのことを言っています。

少しアレンジしつつ2行に分けると、このようになります。

  1. 「とりあえずこんな風に説明しておけばいい」という標準的な例を洗練させていけるといいな。
  2. でもそれはそれとして、最終的には一人ひとりの「教わる人」に最適な説明を考えていくべきだよな。

ぼくはこの二つは対立するものではなく、矛盾なく両立させることができると思っています。

より具体的に言うと、「もっと良い喩え話はないかなあ?」という考えは前者(1)にあたります。

そして、「喩え自体はなんでもよくて、教わる人が理解しやすければいい」という考え方は後者(2)です。

前回のぼくの結論は、「結局は2が大事だけど、現実的には1が必要とされる状況も少なくないから、1の質も継続的に高めていけるといいよね」みたいなことです。

しかしながら、Twitterなどで記事への反応を見てみると、1の「もっと良い喩え」に関する考えに対して、「結局は喩えなんだからそこにこだわっても意味ないよ」と、2の論理で打ち消すような意見が少なくないように感じました。

これは話が通じていません。長文ゆえの斜め読みのせいか、ぼくの文章が悪かったか、あるいはその両方でしょう。

くり返しますが、1と2は矛盾なく両立するものであり、打ち消し合うものではないと思います。
2を大切にしながら、1を洗練させられたらいいなあ、というのがぼくの言いたかったことです。

「わかりやすい」が意味するもの

ところで、本件に関する意見の中には、「箱の例を使ったほうがわかりやすいのだから云々」というものがいくつかありました。
しかし、これには以下のような疑問を感じます。

第一に、そこで言う「箱の方がわかりやすい」の「わかりやすさ」とは何を指しているのか? という疑問。

ぼく自身も前回の記事で、「箱の方が直感的」と書きましたが、しかし「具体的な箱のイメージを思い浮かべやすいこと」と、「変数を理解する際の助けになりやすいこと」は必ずしも一致しないような気もします。

もしかすると、そこで言われる「わかりやすさ」とは、「箱のイメージしやすさ」のことであって、それがいつのまにか「変数の理解しやすさ」にすり替わってしまっているのではないか、と思いました。

第二に、「箱の方が変数を理解しやすい」と言える根拠はなんだろう? という疑問もあります。
そのような実証研究が行われているのか、そしてその研究成果はすでに広く知れ渡っているものなのか。

おそらくそういうことではないですね。
これももしかすると、素朴に「箱自体のイメージのしやすさ」のことを言っているだけかもしれないと思っています。

ヒューリスティック

この話について考える際、何度か頭をよぎったのは「ヒューリスティック」という概念でした。

ぼくはこれまで、それについてくり返し言及しているので、またかと思う人もいるかもしれませんが、以前に別のブログに載せた引用を再掲します。

複雑なことにも私たちがなぜ直感的に意見を言えるのか、私から明快な説明を提案しよう。難しい質問に対してすぐには満足な答が出せないとき、システム1はもとの質問に関連する簡単な質問を見つけて、それに答えるからである。このように代わりの質問に答える操作を「置き換え(substitution)」と呼ぶ。ここでは、もともと答えるべき質問を「ターゲット質問」、代わりに答える簡単な質問を「ヒューリスティック質問」と呼ぶことにする。
 
ヒューリスティックの専門的な定義は、「困難な質問に対して、適切ではあるが往々にして不完全な答を見つけるための単純な手続き」である。ヒューリスティックという言葉は、「見つけた!」を意味するギリシャ語のユーレカを語源に持つ。(略)
 
ではここで、表1の左欄を見てほしい。ここに掲げられているのは難しい質問であり、どれ一つとっても、まともな答を出すためには他のもっと難しい問題に取り組まなければならない。
 

ターゲット質問 ヒューリスティック質問
絶滅危惧種を救うためにいくら寄付するか? 瀕死のイルカを見かけたらどんな気持ちになるか?
現在の生活はどのくらい幸福か? 今の自分は気分がいいか?
今から六ヶ月後の大統領の支持率はどの程度か? いま現在の大統領の人気はどの程度か?
高齢者を騙したフィナンシャル・アドバイザーにはどの程度の刑罰を与えるべきか? 金融詐欺に自分はどのくらい怒りを覚えるか?
次の予備選挙に立候補予定のこの女性は、政界でどこまで出世するか? この女性は誰か政界の大物と似ているか?

 
たとえば、絶滅危惧種以外に考慮すべき環境問題や社会問題は、他にどんなものがあるか。幸福とは何か。今後六ヶ月の間に政治はどのような展開になりそうか。他の金融犯罪に対する標準的な判決はどうなっているのか。候補者が直面する競争はどの程度厳しいのか、等々。
 
こうしたことを真剣に考え抜いてから答えるのは、どうみても現実的ではない。だがあなたは、完璧に論理的根拠のある答を出すには及ばない。ヒューリスティック質問に答えても、そこそこ筋は通る。このやり方はときにうまくいく――が、ときに重大なエラーにつながる。
 
右欄の質問は左欄の質問からたやすく思いつくし、答えるのも容易である。瀕死のイルカや金融詐欺に対する気持ち、自分の今の気分、候補者の政治的手腕の印象、大統領の今の人気などは、すぐに思い浮かぶことだろう。ヒューリスティック質問は、難しいターゲット質問に対しても、すぐさま答を出してくれる。

(ダニエル・カーネマン『ファスト&スロー(上)』第9章より)

喩え話について考えているとけっこう頭を使いますし、その喩えと現実を結びつけながら次々に現れる矛盾を解消していこうとすると、負担はさらに増していきます。

だからその途中で、「あ〜面倒くさい! そもそも喩え話なんて本質的な問題じゃないんだから(以下略)」というふうに考えてしまっても、それはそれで自然なことだと思います。

とはいえ、冒頭にも書いたとおり「より良い喩えを考えてみよう」ということと、「どうすれば変数の概念をわかりやすく説明できるか」ということは、繋がってはいますが別々の問題です。

そしてこの「関係はあるけど別の問題」を、あたかも同じ問題であるかのように結びつけて、より答えやすい方の質問に答えてしまうという状況が上の引用で挙げたヒューリスティックというものです。

本件に関する反応には、そういった傾向が少なからずあるように思えました。

参照渡しと値渡し

一方、そのようなズレが生じてしまう理由は、ヒューリスティックによるものだけでなく、単に元記事の説明不足による場合もあるだろうと思っています。

たとえば、前回の話を「参照渡し」(CのポインタやPerlのリファレンスのような)の概念にまで広げて適用させようとしている人が何人かいましたが、ぼく自身は「初めてプログラミングを学ぶ人に変数を教えるなら」という前提で考えていたので、ここで言う「変数」とはあくまで初歩的な「値渡し」をするものであり、さらに言えば、Perlにおける「スカラー変数を使った値渡し」を想定していました。

「参照渡し」については、「値渡し/参照渡し」というふうに別々の名前が付けられていることからもわかるように、「値渡し」とは別の現象ですから、「箱」であれ「名札」であれ、ひとつの物をその両方に適用するというのはちょっと無理があるように感じています。

またそれに限らず、元々ぼくの方で暗黙のうちに設定していた様々な条件から外れた上での意見も複数あったので、こうした「喩え」に関する話、とくに「箱」や「名札」や「変数」のような一般的な(定義の曖昧な)名詞を使った喩え話について、不特定多数で考える際には、その土台となる条件・要素を可能なかぎり細かくすり合わせておかないと、不毛な状況になってしまいそうだなと感じました。

名札の問題

ところで、というかそれとも繋がる話で、これは後から気づいたことですが、前回「名札」の例を使った書籍として紹介した『初めてのRuby』では、名札のイラストを用いながら、じつは「参照渡し」が解説されていました。

ぼくは上記のとおり、前回の記事を書いた段階では「値渡し」についてのみ考えていたので、これにはけっこう驚きましたが、たしかに喩えの使用条件を限定すれば、その説明も成り立つとは思います。

またもう一点、これも後から気づいたことですが、その『初めてのRuby』や、同じく名札の例として挙げた『たのしいRuby』では、「名札」を旅行カバンに付ける荷札のように、実体(値・オブジェクト)と直接結びつかせるイメージとして使っていたので、これだと結果的に、箱の例がやっていることとあまり変わらないかな? とも思いました。

いずれにせよ、このことからもわかるように、名札には名札の問題があって、それは「名札」という言葉からイメージされるものが曖昧で、受け手にとって解釈の余地がありすぎるということです。

ちなみに、ではぼく自身は前回、「名札」と言いながらどんなものを想定していたのかというと、それは付箋や名刺のようなもので、同じ言葉(変数名)が記載されたそれを複製していくようなイメージで考えていました。

しかし後述のように、こうした喩えに人物の実名を交える場合は、書き換え可能な「変数」よりも書き換え不能な「定数」の例に使うほうが適切であるように今は思っているので、名刺は定数の説明に限定して使うべきかもしれません。

また、付箋の例だとそこには通常、変数名だけが記載され、それが指し示す対象の情報が入らないため、これもちょっと惜しい気がします。

あえて言えば、メールのように「件名(変数名)」と「本文(値)」を書き込める付箋(またはメモ用紙)という喩えなら諸条件をクリアできそうですが、実際にそのような道具や商品はあまり見かけないので、その点がちょっと弱いかもしれません。

新たな喩えの案

さて、そのように、既存のアイデアにそれぞれの問題があるのだとすれば、また新たな喩えを考えてみたくなるのが人情です。

これはあくまで楽しみとしての思索であり、その案を誰かに押しつけたいわけではまったくありませんが、この機会に考えたものをつらつら書いてみたいと思います。

「物体」ではなく「情報」として扱う

まず原則的な話として、「箱」や「名札(または付箋)」にどのような問題があったのかというと、実際のプログラミングでは物体ではないデータ(数値や文字や式など)を扱っているにもかかわらず、それを物体に置き換えようとしていた点に各種の不整合の原因があったように思えます。

ですから、ここからの案ではなるべく物体に置き換えるのではなく、それ自体は実体を持たないデータ・情報の領域から使える概念を探し、それに置き換えてみたいと思います。

値渡し(スカラー変数)

手始めは、最も初歩的かつ重要な値渡しの例ですが、これには「あだ名(ニックネーム)」、あるいは「仮の名前」という案を挙げたいと思います。

これらに共通するのは、「本名Aのことを仮名Bと呼ぶ」という、本来の名前を別の名前に付け替えるという行為です。

子供がどんな犬を見ても「わんわん」と言うとき、「わんわん」は変数(仮の名前)であり、そこで指し示される値(実体)は犬です。

そしてこの際、子供はその犬がゴールデン・レトリバーなのか、シベリアン・ハスキーなのか、柴犬なのかといった犬種を知らずとも、「わんわん」と言うことで「その犬」を指し示すことができ、これは次々と変数の値を更新し、また活用している状況を彷彿させます。

あるいは、自社にいる能力の高い社員を外部の人に紹介する際、「うちのエースです」などと言うことがありますが(実際にそういう現場に何度か遭遇したことがありますが)、ここで言う「エース」も仮の名前に入るでしょう。

この「仮の名前」があることによって、以前にそれを聞いた取引先の誰かが「ほら、なんという名前だっけ、御社のエースの……」と言えば、対象となる人物の実名を思い出せなくても、その人についての話を続けることができます。
これもまた、「一度宣言(+代入)すれば、その後は元の値をくり返し記述しなくても複数箇所で処理を進められる」という、変数を利用した状況に似ていると思います。

ちなみに、ここで大事なことは、その変数に格納される対象は交換可能な何かであって、交換不能ではない、ということです。

「わんわん」や「エース」は状況や時期によって交換可能ですが、もしこれが「イチロー」だった場合、それは厳密には本名ではない「仮の名前」ですが、とはいえ現実世界ではそこに格納される人物は一人だけですから、こうした名前は交換不能な「定数」の例として利用する方が適切だと考えられます。

値渡し(配列)

次に、配列の変数の例としては、プロ野球の「球団」と「打順」と「選手」の関係を使えるのではないかと考えています。

たとえば、「巨人の4番」といえば王貞治、原、落合、松井、清原、高橋……などが浮かびますが(世代によるでしょうが)、ここでは選手名が「値」、4番という打順が「添字」にあたります。Perlっぽく書くと、

print $巨人[4番];

という感じでしょうか。

実行すると、

松井秀喜

みたいな。
(添字の4って実際は5番目だろ、というツッコミはご容赦ください)

あるいは年度も指定して、「配列の配列」とすればより具体的になりそうです。

print $巨人[2000年][4番];

実行。

松井秀喜

値渡し(ハッシュ)

また、ハッシュ(連想配列・辞書型)の例としては、会社などの「肩書」や「役職」という案があります。

A商事の社長、B株式会社の営業部長、C銀行の広報担当というふうに、所属先の名前は基本的に一定ですが、それらの社名や肩書を通して呼び出される個々人は、人事異動のたびに入れ替わります。

たとえばマイクロソフトのCEOは、現在ナデラさんですから、以下のようになるでしょう。

print $マイクロソフト{CEO}; #=> サティア・ナデラ

あるいはこれも、年数と組み合わせれば「配列のハッシュ」の例に使えるでしょうか。

print $マイクロソフト[2008]{CEO}; #=> スティーブ・バルマー

参照渡し

さて、懸案の「参照渡し」ですが、これについては先に喩えを成立させるための条件を洗い出してみたいと思います。

第一に、これは変数であり、複数の場所に置いていろいろな人が同時に触れられることが前提ですから、変数自体が複製可能でなければいけません。

と同時に、この変数の中身を誰かがどこかで変更すると、他のすべての人の手元にあるそれの中身も変わってしまうものでなければいけません。

それで最初に思いついたのは、Wikipediaです。
Wikipediaは、基本的に誰もが自分の手元で更新でき、更新された情報はすべての人の目の前のスクリーンに反映されます。

「それを言ったらべつにWikipediaじゃなくても、掲示板やTwitterなどのWebサービスの大半がそれに該当するじゃないか」という気もしますが、まあ、イメージしやすいので……ということで。

とはいえ、コンピュータ・プログラミングの学習に関する喩え話で、コンピュータに関わる現象を例に使ってしまうのもちょっと筋が悪い気がするので、もう少し物質感のある、ソフトウェアから離れた例はないかと考えましたが、少し近いかなと思ったのは、新聞や雑誌、あるいはテレビやラジオにおける人生相談のような、ユーザー参加型のメディアです。

そういったメディアは不特定の人が購読・受信していて、たとえばラジオの人生相談のコーナーでは、リスナーの電話の声がそのままラジオ番組の内容を更新し、即座に他のリスナーの耳にも届きます。

ただ、これの問題は、あまり日常的には触れない事象だということですね。

もう一つ、いくつかの条件は無視することになりますが、感覚的に「これって参照渡しっぽいよなあ」と思う事例があって、それは「酔っ払ってTwitterに投稿すること」です。

覚えのある人ならわかると思いますが、当人はあくまで自分の半径数メートルぐらいの、ローカルな世界に対して何かを発信しているようなリラックスした(あるいは向こう見ずな)気分ですが、実際には仕事先や家族なども含む全世界のネットユーザーにその意見を開陳しているわけで、翌朝つらい気分になることも少なくありません。

これは値渡し(コピー)で得た手元の値に手を加えたつもりが、呼び出し元の実在に影響を与えていた、というときの冷たい驚きに通ずるものがあると思います。

寿限無

いろいろと考えてきましたが、最後に、本件に関する自分の原点のような例を挙げて終わりにしたいと思います。

じつはぼくが変数について考えるとき、いつも頭に思い浮かべる話が一つあって、それは落語の「寿限無」です。

寿限無 - Wikipedia

もしもここに出てくる子供の名前を、実際の長々とした名前ではなく、「$寿限無」という数文字の変数に格納できていたら、話(噺)はずっとラクに進められるだろうに、と思います。
(もちろん落語の意味は変わってしまいますが)

これは前記した「仮の名前」の例でありながら、同時に落語の中のその子供、という特定の人物を収める変数なので、実際にはやはり定数の例として使ったほうが適切だと思いますが、とはいえ、ぼくが変数に対して「便利だなあ」と感じる以下の要素を満たしているという点で、それなりに有効と思われる題材です。

本来長い名前が入るはずの部分に変数「$寿限無」を使えば……

  • 同じことを何度も言わずに済む。
  • 修正をひとつの場所で行える。
    • 1回修正すれば複数の箇所がすべて更新される。
    • 複数の箇所を直す中で新たなミスが生まれるという事態を回避できる。

https://www.youtube.com/watch?v=JfDF7J06AeYwww.youtube.com

関連記事

note103.hateblo.jp