Scrapboxの日記にWebページをブックマークするためのブックマークレット

ネットで気に入った記事を見つけたり、部分的にコピーしておいて後で読み直したいと思ったときに、そのURLや選択範囲を手軽にブックマークして、読み返しやすい場所に整理しておきたいとは思うものの、既存のブックマークサービスではなかなか難しそうだなと思って、Scrapbox公式ブックマークレットをアレンジして使っています。

javascript:(function(){var title=window.prompt('Bookmark to Scrapbox','['+document.title+' '+window.location.href+']'); if (title==null) return; var lines=['['+document.title+' '+window.location.href+']']; var quote=window.getSelection().toString(); if (quote.trim()) lines=lines.concat(quote.split(/\n/g).map(function(line){ if (line !== '') { return ' > '+line } })); var lines2 = []; for (var i = 0; i < lines.length; ++i) { if (lines[i] !== undefined) { lines2.push(lines[i]); } } lines2.push(''); var body=encodeURIComponent(lines2.join('\n ')); dt = new Date(); dtm = dt.getMonth()+1; dtd = dt.getDate(); dh = dt.getHours(); dm = dt.getMinutes(); ds = dt.getSeconds(); if (dh < 10) {dh = '0' + dh}; if (dm < 10) {dm = '0' + dm}; if (ds < 10) {ds = '0' + ds}; time = dh + ':' + dm + ':' + ds; if (dtm < 10) { dtm = '0'+dtm; } if (dtd < 10) { dtd = '0'+dtd; } date = dt.getFullYear()+'-'+dtm+'-'+dtd; if (title == '['+document.title+' '+window.location.href+']') { title = ''; }; if (title == '' && quote == '') { body=encodeURIComponent(lines2.join(' ')); body = body+' '+time } else if (title == '') { body = body+' '+time } else { body = body+' '+title+' '+time }; window.open('https://scrapbox.io/***/'+date+'?body= '+body)})()

最後の「***」としているところを自分のプロジェクトIDに変えて使います。ぼくの場合、以下が公開プロジェクトなので

https://scrapbox.io/note103/

IDは「note103」です。ちょっとやってみましょう。

まずは、単にブックマークレットをクリックして、そのままOKボタンを押した場合。

f:id:note103:20181122233637g:plain

次、なにかコメントを入れた場合。

f:id:note103:20181122233705g:plain

次、コメントは入れずに、部分選択した場合。

f:id:note103:20181122233733g:plain

最後に、選択しつつコメントも入れた場合。

f:id:note103:20181122233809g:plain

最小限の仕様は、

ブックマークした日の日付をタイトルにしたScrapboxページを生成し、そこに時刻付きで [ブックマークしたWebページ名 URL] を投稿する

です。もしすでに日記ページがあった場合は、下に追記していきます。

その上で、もし元のページでテキストを選択している場合は、インデントしつつその部分を引用コピー。

確認ダイアログでコメントを入れた場合は、こちらもインデントしつつ下行に(引用がある場合はその下に)反映。

で、時刻は最後の行に(引用もコメントもなければ[ページ名 URL]の後に)くっつける。

という感じですね。

ちなみに、少し前のバージョンでは、選択範囲が空行を含んでいた場合、空行も引用に含めていましたが、可読性が悪いので空行は詰めるようにしました。たとえば、上の最後の動画の場合、前のバージョンだったら「二」の前後に空行が入っていたのですが、今は詰まっています。

ぼくは個人の仕事で使っているものも含めて、非公開のScrapboxのプロジェクトをいくつか持っているので、それぞれに関連するWebページや文章を分けて保存しておきたい時は、プロジェクトごとに設置したブックマークレットでサクサクっとブックマーク&コメント(または引用)を入れています。

あとは小ネタですが、現状ではダイアログの段階で[ページ名 URL]というScrapbox記法を使ったリンク情報が出ていますが、この部分をMarkdownのURL記法に変えて、以下のようにしておくと、

javascript:(function(){var title=window.prompt('Markdown','['+document.title+']('+window.location.href+')'); // 以下同じ

f:id:note103:20181122235924g:plain

このように、ダイアログの部分でMarkdown記法のURLをパパっと取れてけっこう便利です。(それ以外の挙動はすべて最初に挙げたものと同じ)

肝心のコードの中身については・・例のごとく(というか)泥縄で継ぎ接ぎしながら作ったものなので、とくにコメントはありません。😇

じつはScrapboxへの投稿&連携ツールとしては、これとは別に、もう少し手の込んだ&PHPやGAEなども巻き込んだ&これもほとんど毎日使っているものがありますが、それについて書き始めるとけっこうな大作になってしまいそうなので、また時間ができたら・・と思っています。

ブックマークレットの設置の仕方

プログラマーの人にはとくに説明不要だと思いますが、Scrapboxユーザーの中には非プログラマーも多いでしょうから、念のため上記のブックマークレットを自分のブックマークバーに設置する手順を書いておきます。

1. 何でも良い何かのWebページをブックマークバーにブックマークしておく。(たとえばこの記事ページなど)

f:id:note103:20181123093614p:plain

2. そのブックマークを右クリックして「編集」をクリック。

f:id:note103:20181123093657p:plain

3. そして「URL」の部分を・・

f:id:note103:20181123094341p:plain

4. 上記のコードをまるっと入れ替え。このときにプロジェクトIDも自分のものに変更。

f:id:note103:20181123093727p:plain

5. 「名前」の部分はご自由に。

f:id:note103:20181123093744p:plain

6. 保存して出来上がり。

f:id:note103:20181123093800p:plain

以上です。

VimとRubyでScrapboxの日記に追記する

前回書いたTipsに近い話ですが。

note103.hateblo.jp

もっと手軽にVimから投稿できないかなあ、と思って作った物をご紹介します。このとき、投稿対象はその日の日記ページとします。個別のScrapboxページを作成する場合には、前回の記事後半に示した「Vimから新規ページを作ってコピペする」を使います。

目次

VimコマンドラインモードからScrapboxに投稿する

まずはVimコマンドラインモードからサクッと投稿するやつ。

https://i.gyazo.com/455a334df1d3ee3a738cda41f74a6b2a.gif

.vimrcに設定した任意のマッピングを打つと、コマンドラインに以下が表示され、テキストの待機状態になります。

:!ruby ~/scrapbox/sb-post.rb note103 (ここにテキストを入れていく)

上記のデモでは、テキスト部分に「Vimから投稿テスト」と入れています。このとき、行頭に全角スペースを入れていますが、それは投稿時に1字下げするためです。この字下げを半角スペースでやると自動的に詰められてしまうので、全角にしています。

コードを見てみましょう。まず .vimrcの方ではこのように書いています。

nnoremap <Leader><Leader>i :<C-u>!ruby ~/scrapbox/sb-post.rb note103 

1行ですね。ここではリーダーキー(ぼくはスペースにしていますが)を2回叩いてから、iを1回打つとこの待機状態に入るようにしています。

後半の「note103」とある部分は、Scrapboxのプロジェクト名です。複数の投稿先候補がある場合には、一番使うプロジェクト名をここに書いておいて、それ以外のプロジェクトに投稿したい場合には、コマンドラインに出てきてからさらっと書き換えるようにしています。

コードが1行で済んでいるのは、大半の(というかすべての)処理をRubyスクリプト(sb-post.rb)に任せているからですね。

では、そのRubyのコードを見てみましょう・・とするつもりでしたが、じつはこのスクリプトは後述の機能も兼ねているので、そこまで紹介してからあらためて掲示します。

Vimで書いている任意の内容をそのまま投稿する

コマンドラインモードから投稿できるのはお手軽ですが、上記のとおり、半角スペースを含む投稿はできなかったり、けっこう制約があります。

そこで、バッファに書いている内容の一部をサクッと投稿(日記に追記)できないか、と考えて作ったのが以下です。

まずは使ってる様子を見てみましょう。

カーソルが乗っている行を投稿

https://i.gyazo.com/a426037e4f782604e22ec86bd70414a0.gif

選択範囲を投稿

https://i.gyazo.com/73ee490c9f98e2c6bbcc2b55cf3c3bcf.gif

前回の記事で紹介した、バッファの内容をScrapboxにコピペするものはバッファ全体を対象としていていましたが、今回は「カーソルが乗っている行」、または「選択した部分」だけを飛ばしてくれます。またこの際には、最初に書いたとおり、投稿先は当日の日記を対象にしています。

コード

では、それを実現しているコードを示します。まずは .vimrcに記載している関数&マッピングがこちら。
gist.github.com

その中から呼び出しているRubyのコードはこちら。
gist.github.com

どちらもエスケープの置換が泥縄な気がしますが・・とりあえずこれである程度は機能してくれます。

近況

最近はPerlの次に勉強する言語として、なるべくRubyを使ってみるようにしています。まだまだRuby本来のパワーとか独自性などの魅力には触れられていない自覚ですが、それでもいろいろ直感的に使える*1感じがして、面白いです。使い方がわからないときも、ちょっと検索すればたくさんの情報に出会うことができます。

余談ですが、Rubyの方の22行目にある & の置換について、他と同様にバックスラッシュでエスケープしようとしても効いてくれなくて、しばらくハマりました。検索を繰り返してもなかなか解決策に出会えず・・諦めかけましたが、以下でようやく解決しました。
stackoverflow.com

先日のbuilderscon 2018では最終日のスピーカーだったAmyさんが、その発表の中で「大抵の疑問はStack Overflowで解決する」と言っていましたが、まったくその通りだなと思いました。

さらにちなみに、そのQ&Aの中ではビシッと解答が示された後にも質問者が「これじゃ動かないよ」と言っていて、解答者がそれに対して「そりゃエスケープの問題じゃなくて君のコードの問題だよ」と言っているのを見て、ああ、いかにも初心者のハマり方・・わかります・・という感じでした。

初心者は2重、3重に少しずつ間違えているので、そのうち一つの問題を解消しても不具合は直らず、正しい修正の正しさがわからない、何をどう間違えているのかもわからない、そして混沌に至る・・というよくあるパターン。でもそれも、結局はひとつずつ地道に解消していくしかないんですね。それが一番の近道というか、舗装された安全な道。

そうした地道な一歩一歩をくり返す中で、徐々にスピードが上がったり、効率的な進み方を思いついたりするのかなあ、と思っています。

*1:構文をちゃんと覚えてなくても、「こんな感じかな?」とかカンでやってみるとそれで動いたり。

最近のScrapboxの使い方

Scrapbox、最近はほとんど毎日使っています。日記的に使うことが多いですが、メールやブログの下書きに使うことも多いです。

いろんなことがScrapbox周りで効率化している気がするので、現在の使い方について記録しておきたいと思います。

目次

家族との情報共有

他人と使うケースとしては、最近では家族との情報共有で利用することが多いです。家族とは基本的にはチャットでやり取りしていますが、たとえば買物リストとか、旅行の持ち物リストや旅券に関わる各種の情報(予約番号とか)のように、何度も見直したり、チャットで流れないでほしかったりするデータはWikiのような静的な場所を使った方が良いので、そういう用途で使います。

ちなみに、以前はこれをサイボウズLiveでやっていました(が、すでに同サービスは終了)。最近Kibelaを使ってみたところ、フイフイ動くしかわいいし、家族との用途ならKibelaもいい気がしましたが(たしか5人ぐらいまでは無料だし)、非営利でプライベート用途のための料金プランをより切実に想定しているのは、Scrapboxの方かなという気もします。それに、家族はMarkdown記法にこだわったりもしないですからね・・。

仕事関連でも時々使いますが(フリーランス・ユーザーなので無料プラン)、これはたぶん本来の用途に比べてちょっと特殊で、ほとんど更新しているのはぼくだけ、というパターンが多いですね。自分が頭の中だけで覚えておくのはちょっと大変だな、というネタを入れておいたりします。ただいずれにしても、現時点では仕事での使用頻度はそれほど多くありません。

メールやブログの下書き

個人用途で言うと、最初にも書いたようにメールやブログの下書きでよく使います。と言っても、ちょっとした内容なら、そもそも下書きなんてしないわけですが、けっこう大事なメールとか、込み入った内容の場合には、ぼくは3段階ぐらいに分けてそれを書くので、そういうときに役立ちます。

第1段階では一気にラフを書き切ります。この時はサクッと手元で起動できるVimを使うことが多いですが、いずれにせよディテールや整合性などは気にせず、とにかく頭にある情報を出し切ることが優先です。

それが終わったらScrapboxにコピペして、一旦そこから離れます。いわゆる「寝かせる」状態。

しばらくしたら、その第1段階で作ったものをScrapbox上で直していきます。これが文章を作る上では一番大事な工程です。
もし分量が多く、PCを使えるならVimに持っていくこともありますが、「修正」という点に重きを置くならばやはりScrapboxが有効です。

なぜなら、Scrapboxには「編集画面」がないからですね。編集するための画面が、すなわちそのまま「閲覧画面」になっています。
これ、本当に革命的にすごいことです。Googleドキュメントもそうじゃないか、と思われるかもしれないですが、Googleドキュメントをモバイルから操作しようとすると、編集画面に移動するためにエンピツアイコンをクリックしなければいけません。そして、Scrapboxにはそれがありません。

その話からもわかるとおり、この第2工程の修正作業では、ぼくはPCとモバイルを行ったり来たりします。文章を書く際、というより直す際に、「この表現、変だな」とか「ここタイポじゃん」とか気づきやすいのはどういう時かというと、それは「無責任な人」になったときです。よくTwitterで的外れなリプライをしている人がいますが、そのぐらい無責任な態度で文章を読むと、見方がものすごく厳しく、かつシンプルになり、「こんな言い方じゃわからないでしょ(このオレ様が)」といった具合に様々な指摘が頭の中に浮かんできます。

このような「無責任な人」になるスイッチというか、トリガーになるのが、たとえば上記のような「時間を置く(寝かせる)こと」だったり、あるいは「環境を変えること」だったりします。
そして、その「環境を変える」方法の中でもとくに手っ取り早いのが、「PCとモバイルを行ったり来たりする」ことです。

ぼくの場合、あまり寝かせてる時間がないようなときは、PCで書いた文章をすぐに手元のiPhoneで読んだり、iPhoneで書いた内容をPCで見直したりします。そうすると、だいぶ見え方が新鮮になって、変な言い回しやわかりづらい表現に気づきやすくなります。別人のような目で読み返すことができます。

見つけた間違いを修正するときも、Scrapboxに文章を入れておくと、たとえば今までPCで書いたり読んだりしていた文章を、iPhoneSafariから、新鮮な目のまま(間違いに気づいたときのイメージを保持したまま)修正していくことができます。Scrapboxでは閲覧画面から編集画面に移動する時間が存在しないので、画面遷移の際に微妙なニュアンスを忘れてしまうようなこともなく、感覚的にサッサと直していくことができます。

このような感じで、ちょっと込み入った文章を直すときにはScrapboxをよく使っています。

ターミナルやVimからページを生成

Scrapboxでは、URLにあたる文字列を操作することで、任意のページを自在に生成することができます。

詳しくは以下で説明されていますが、

scrapbox.io

この機能を利用して、当日または任意の日付の日記ページをターミナルからさくっと生成したり、Vimで書いた文章を少ない手数で新規ページにコピペしたりできるようにしています。

ターミナルから日記ページを作る

まず、ターミナルからどのような操作をしているか紹介します。

ぼくは.bashrcに以下のような関数を入れています。

gist.github.com

おそらくプロの人から見れば冗長で、恥ずかしい気もしますが、やりたいことは充分に実現してくれます。

論より証拠ということで、この最後にあるエイリアス「sddn」を使って、ぼくの公開Scrapboxであるところの「https://scrapbox.io/note103/」に本日付(2018/08/31)の日記ページを作ってみましょう。

gyazo.com

できましたね。個人的には、前後の日付(8/30と9/1)が入るようにしているところがポイントです。

コードの中で、言語設定を英語にしたり日本語にしたりしていますが(L7, 33)、これは曜日を英語にするための処理なので、ターミナルの設定によっては不要かもしれません。
(ぼくの環境では、これがないと「#Friday」のところが「#金曜日」になります)

また、openコマンドを使っているので、Mac以外のOSだとそのままは使えないと思います。

if文の中の処理は、当日の日記ではない、任意の日付のページを作りたいときのためのものです。
例として、「2016/01/01」の日記を作ってみましょう。

gyazo.com

できました。ちなみに、この本文の内容は、上記のヘルプにもありますが、中身を全部URLに入れ込むことにより生成しています。

また、エイリアスの設定からもわかりますが、「sddn」というコマンドの実体は「sbdd」という関数にプロジェクト名の「note103」という引数を渡しているだけです。よって、その引数を変えればどのようなプロジェクトにも応用できます。

なお、このようにいろいろデフォルト情報(前後の日付や各種タグなど)が詰まった新規ページではなく、当日または任意の日付の日記にアクセスしたいだけ、というときには、以下の関数を利用しています。

function sbd {
    local site="$1"
    if [ -z "$site" ]; then return; fi

    local ymd=$(date +%Y-%m-%d)
    if [ ! -z "$2" ]; then
        ymd="$2"
    fi
    open "https://scrapbox.io/$site/$ymd"
}

alias sdn="sbd note103"

たとえば、すでにその日の日記を作成済みの状態で、さっきの日記ページ作成コマンドを使うと、初期設定用のタグや前後の日付がまた入ってしまうので、単にその日記を閲覧したいだけであれば、これを使うのがシンプルです。

Vimから新規ページを作ってコピペする

もう一つ、時々使うのが「いまVimで書いてる文章をそのままScrapboxにコピペする」というワザです。前半で触れた、Vimで書いたメールのドラフトをScrapboxに持っていくときなどによく使います。

先にコードを示しておくと、.vimrcに以下の関数およびマッピングを書いておきます。

gist.github.com

では、動かしてみましょう。

gyazo.com

はい。GIF動画だと順番がわかりづらいですが、

1)最初に2行だけ文が入ったVimがあって、
2)ファイル下部のコマンドラインで対話型の操作をすると、
3)一意の日時から生成されたScrapboxの新規ページが現れて(この時点では新規ページはカラ)、
4)そこにクリップボード上にコピーされていたテキスト情報をペースト。
5)で、最後に少し内容が変わった最初のVimが出てきています。

この最後の段階でわかりますが、上記2)と3)の間で元ファイルの1行目に目的地のURLが追加されています。それを使って、Vimプラグインの「open-browser.vim」でジャンプすると自動的にページが生成される、という仕組みです。

github.com

なお、Vimでヤンク(コピー)したテキストは、デフォルトではMacクリップボードには入らないので、これについては別途設定が必要になります。

ぼくの場合は、以下のような設定が.vimrcに入っていました。(これを書きながら久しぶりに思い出しました)*1

" Use clipboard register.
if has('clipboard')
  if has('unnamedplus')
     set clipboard& clipboard+=unnamedplus
  else
     set clipboard& clipboard+=unnamed
  endif
endif

この方法の良いところは、新規ページを作成しても、中身が自動的にリンク先にペーストされないことです。

え、自動で反映されたほうが便利じゃないの? と思われるかもしれませんが、Vimで書いている内容ってけっこうセンシティブなものが多いので、万が一ペースト先のプロジェクトが間違っていたら大変なことになります。(いわゆる誤爆

その点、この方法だとまずは新規ページが作成されるだけで、それまでVimで書いていた中身は、まだクリップボードに保持している状態なので、行き先のプロジェクトが目当てのものであることをきちんと確認してから、安心してペーストすることができます。

・・といっても、じつはそれはあくまで結果論で、初めは自動的にペーストできるものを作ろうとしていたのですが、URLに中身を持たせる方法だと文章量に制限があるようで、長文には向かなそうだったので、結果的にこの方法に落ち着いています。

まとめ

ということで、最近のScrapboxの使い方を簡単にまとめてみました。

Scrapboxはウラではいろいろ複雑な処理やコードが動いているのだろうと思いますが、ユーザーからすればシンプル極まりない構造になっているので、そのぶん使い方も無限大というか、自由度が高くて面白いです。

また、前半にも書いたように、編集画面と閲覧画面の切り替えがないことの優位性はいくら強調してもしすぎることはないでしょう。

今後どのような新たな使い方が出てくるのか、思いつくのか、ということも楽しみです。

*1:ヤンクとクリップボードの設定についてはこれだけでは解消できない場合もあるかもしれません。もしハマったら検索で最新情報にあたってください。

YAPC::Okinawa 2018 の思い出

もう半年前になってしまいますが、3月に行われたYAPC::Okinawa 2018 ONNASONについて。

yapcjapan.org

本イベントについては、すでに2本の記事を書きましたが、

YAPC::Okinawa 2018 ONNASONの記録 - the code to rock
ノンプログラマーのプログラミング活用法 - the code to rock

まだもう1本分、これも書いておかないと・・と思っていたトピックがいくつかあったので、少し時間ができたこのタイミングで一気に書いておきたいと思います。

目次

前夜祭

まずは本番前日の前夜祭。国際通りに面したビルの5階、結婚式の2次会などもできそうなステキ・スペースで開催されました。

当日の様子はこの辺りから。
30d.jp

この前夜祭、リジェクトコン*1とその後のLTから構成されていて、ぼくは翌日の本編で発表する予定だったので、この日はただボーッと見ているだけのお客さんとして参加するつもりだったのですが、会場に着いてみると思っていた以上に知らない人が多い・・時々、Perl入学式つながりで知っている人や、過去のYAPCでお会いした人とは挨拶できましたが、でもやっぱり基本、知らない人ばかり。

んー、これ、このままじー・・っと2時間ぐらいチビチビお酒を飲みながらここで過ごすの、ちょっとつらいかも・・? と思い、急遽ホテルまでMacを取りに戻って、LTに参加させてもらうことにしました。
LTに参加することで、自分が誰なのか、少なからぬ人に周知できると思ったし、時間を持て余すこともなくなるし、と。

しかし、この中途半端な野心がその後のスタッフの皆さんの仕事を増やすことになってしまい・・。というのも、いざLTとなって接続を始めたらなかなかモニターにスライドが反映されない。押しても引いてもまったく駄目。それも、順番が最後から2番めぐらいだったので会場の皆さんは注目しているし、うわー、ミスった、すみません! もうやめます! ゴメンナサイ、じゃあ次の方! とか泣きそうになりながらその場で撤退宣言をくり返していたのですが、現場を仕切っていた id:codehex さんをはじめスタッフの皆さんが「いや、大丈夫ですよ」と優しく&粘り強くセッティングを手伝ってくれて、最後には id:karupanerura さんが「これでどうだ」とセットしてくれたのが届いてようやくスタート。

いやあ・・本当に皆さんの胆力というか、タフさ、落ち着き、頼りがい、どれを取っても見習いたいです・・ありがとうございました。とくに codehex さんの「大丈夫ですっ」には救われました。

後から思いましたが、ぼくが翌日の本編で、そこそこリラックスして、緊張しすぎずに発表できたのは、この前夜祭でもうこれ以上ないぐらいのテンパりを体験したからだと思っています。いくら何が起きるかわからない本編だとしても、上記以上の失敗というか、焦った雰囲気はもうナイだろう、と思ったので。実際、本編の方でもスタッフの皆さんにたくさん助けて頂いて、まったく滞りなく進めることができました。

さて、そんな経緯で行ったLTですが、ぼくがその会場で何かしら見るに足る、価値と言えるものを提供できるとしたら、とりあえず自動文字起こしの実演ぐらいかな・・と思ってそれをやりました*2。実際には、そのようにして起こした文字をVimからtextlintを呼び出して自動校正する、というところまでやりたかったのですが、これはtextlintが動かない・・というのを2〜3回繰り返したところで時間切れ。

まあ、文字起こしの方ではけっこう良い反応を頂いて、最後の時間切れのところでも「ああ〜、残念〜!」という雰囲気を会場で一体になって醸せた感じもするので、自分としてはOKというか、充分な成果だったかなと思っています。

ちなみに、そのときにtextlintでやりたかったのはこんな感じのことでした。(当日使う予定だったテキストで再現)

youtu.be

このワーッと滝のように流れていくのが、純粋に面白いなって。それを最後に見てもらったら最初のドタバタもチャラにできるかな・・と思っていましたが、そうも行かなかったのが自分らしいなと思っております。*3

あらためまして、当日サポートしてくださったスタッフの皆さん、そして声を上げて反応してくれた会場の皆さん、ありがとうございました。

当日(私信)

その翌日、本編の出来事に関しては上記2本のブログでだいぶ書いていますが、これまでに書きそびれたことをひとつだけ。というのは、じつはぼくの発表会場では、以前にPerl入学式のサポーターとしてご活躍&サポーターチャットの方でも時々やり取りしていた id:gomayumax さんが部屋付きのスタッフ業をされていて、でもぼくは初対面だったのでそれがgomaさんだとは結局最後まで気づかなかったんですよね〜・・。まったくまともな挨拶もせず、失礼しました・・😅また次の機会に。

ハッカソン

そのさらに翌日、3/4にはPerlハッカーの@skajiさんの呼びかけで、以下のハッカソンが催されました。
connpass.com

ぼくはもちろん(というか)こういった会にはこれまで縁がなかったのですが、今回はスピーカーでしたし、エイヤという感じで申し込みまして、参加してきました。

前日の本編後の懇親会(というか飲み会)にけっこう遅くまで出ていたので、この日は二日酔いがけっこうキツかったですね・・。

しかしその懇親会で、「いやー、明日ハッカンソン行くんですけど、やることなくて・・とりあえず雰囲気だけ味わいに行きます」みたいなことを言っていたら、 @charsbarさんから「そんなこと言わないで〜。何かできることあるんじゃないの?」とニコニコしながら突っ込まれてしまい、んー、たしかに。何かあるかなあ・・できること・・と考えていましたが、とりあえず編集者を名乗ってはいるので、じゃあPerl関連のドキュメントでも何かしら見て回って、直せそうなところがあれば手を入れてみるか・・と。

それで、会場に着いてからさっそくperldoc.jpを見に行って、何かできることはあるかな・・とひとしきり周遊。
japan.perlassociation.org

ハッカソン会場にもいらしていた @charsbar さんから、修正依頼を送るならどうするのか、などもその場で教えてもらって*4、しかしこれ、実際に何かやるとなると、ただ頭から見ていくより自分の使っているモジュールなどから見た方がいいか・・とか、けっこう砂漠に水をまくような大変さを実感。

ということで、これはこれとして見るとして、他に何かないものか・・と思っているところで、ふと、少し前に目にしたJPAさん(Japan Perl Association)のWebサイトで、部分的に情報が古かったりリンク切れになったりしているところがあったのを思い出したので、そちらの修正作業をやることに。

幸い、ハッカソン会場にはJPA理事の@karupaneruraさんもいらしていたので、さっそくその旨相談。数秒で「じゃあ、この時間だけ編集権限を渡しますよ」と決定、すぐにアカウント手続き、1分後にはもうページの編集ができる状態になっていました。すご・・。

あまりのスピード感に眩暈を覚えつつ、ざくざく作業。まずは修正対象になりそうなページや箇所をリストアップ。
要修正の箇所と、要検討につき修正案を提案するまでにする項目に分けていきます。

その後は要修正のところからどんどん修正。終わったら修正内容を簡単にGistにまとめて @karupanerura さんに共有。というあたりまで行ったところで、終了30分前ぐらい。あっという間!&なにこの充実感!

タイポやリンク切れの箇所についてはここで示してもあまり意味がないので、わかりやすい成果をひとつだけ。

以下の、Perlの参考文献を示したページで『初めてのPerl』と『続・初めてのPerl』のリンク先が古い版だったので、最新版に差し替え。
japan.perlassociation.org

初めてのPerl 第7版

初めてのPerl 第7版

続・初めてのPerl 改訂第2版

続・初めてのPerl 改訂第2版

その他、参考書籍として深沢千尋さんの『かんたんPerl』と木本裕紀さんの『業務に役立つPerl』も追加してはどうか? と提案しておきました。

かんたん Perl (プログラミングの教科書)

かんたん Perl (プログラミングの教科書)

もっと自在にサーバを使い倒す 業務に役立つPerl (Software Design plus)

もっと自在にサーバを使い倒す 業務に役立つPerl (Software Design plus)

ゆるくて鋭い突っ込みと具体的なレクチャーで作業を促してくださった@charsbarさん、前夜祭に続き圧倒的なパフォーマンスで段取りを組んでくださった@karupaneruraさん、そしてこのような機会を作ってくださった@skajiさん、ありがとうございました。

焼肉

エンジニアといえば焼肉ですが、この日の打ち上げ(?)も焼肉でした。たしか会場は以下。

焼肉酒場 牛恋 那覇松山店(那覇/焼肉) - ぐるなび

入ってすぐ、店員さんから「牛恋(うしこい)は初めてですか!?」と前のめりに聞かれて(たぶん決まり文句)、「あ、はい・・」という感じになったのが記憶に深く残っています。

参加者は錚々たる面々。上記の方々に加え、本編の最後にキーノートを務められた@yappoさん、同じくPerl Mongerとして著名な@xaicronさん、そしてもう何年も前、YAPC::Asiaのリジェクトコンのときに「malaさんですか?」と話しかけて以来のmalaさん等々。

とにかくハイレベルなプログラマーが集まっているので何を話しているのか理解できない時間の方が長かった気がしますが、まさにそういった時間を体験することこそがこのハッカソンに参加した目的でもあったわけで、考えてみるとその念願はこの焼肉会でようやく達成されたのかもしれません。

個人的には、@xaicronさんに以下の記事およびそれを含むテスト系のアドベントカレンダーがすごい参考になって何度も見直している、という話をできたこと、そしてそれを書いた頃にはどういう動機でどんな感じで書いていたのか、みたいなことをお聞きできたのは嬉しかったですね。
perl-users.jp

ちなみに、Perlアドベントカレンダーのテストの話といえば、@myfinderさんの以下もそれはもう何度も読んでいます。

Test::Moreでテスト事始め - JPerl Advent Calendar 2009

お土産屋めぐり

最終日。3/3が本編だったので、翌3/4に沖縄を発った人も多かったようですが、ぼくは上記のハッカソンに参加したので帰りはこの日、3/5でした。

結果的には、このスケジュールがYAPC恒例の飛行機ガチャにつながって、出発時刻は延期につぐ延期、もう今日はダメか・・? という頃になってようやく&突然飛んで、しかし羽田に着いた頃にはもう終電が自宅まで届いてなくて、たしか蒲田あたりに一泊するはめになったりしましたね・・。

しかしそこから少しだけ時計を巻き戻して、飛行機の予定は夕方だったので、ひとまず午前および昼過ぎまではようやくの&今回初の沖縄観光。

といっても、それほど時間があるわけでもないので、とりあえず那覇の公設市場で本場の沖縄そばを食べて、残った時間でお土産を買って帰ろう、という算段でした。

沖縄そばをどこで食べるか? については、さとなおさんこと佐藤尚之さんの以下を参考にしました。

沖縄の行った店リスト(170店)|さとなおのおいしいスペシャル

さとなおさんはぼくがかつて震災ボランティアを手伝っているときに、団体は違ったもののそのご活躍を見ながら、「ああ、すごい人だな」と思っていた方。信頼のおける、尊敬できる人。その人が沖縄のあちこちを食べ歩いて、ちょうどぼくが今回歩き回れる範囲も上のページでいろいろレポート(&レート付け)してくれています。ありがたい!

ということで、まずは目当てにしていた公設市場から。1階には生鮮食品のお店がすごい勢いで営業を繰り広げていますが、その2階には飲食店街が広がっています。そこをぐるりと一周した後、さとなおさんのレポートを読みながら「ここがいいかな」と思ったところに入店・・したつもりが、いきなり失敗。じつはこの飲食店街、店同士がびっしり並んでいるのですが、その店の境界が非常にわかりづらい。それで、目当てにしていた店に入ったつもりが、隣の店の席に着いて注文してしまいました。

f:id:note103:20180305120535j:plain:w300
(絵に描いたような普通の沖縄そば

いや、普通に全然おいしそうだし、実際「こんな感じだと思ってた」という味だったし、値段も量もほとんど隣と変わらないので、不満ということではないものの、それでもいきなりつまづいた〜・・という感じ。

幸い、このときには半量で頼んでいました。かなりレアな機会ということもあり、そば一杯で終わるつもりはなかったので・・。それで、ふたたびさとなおさんのレポートを見ながらあちこち歩いてみたところ・・出会ったお店がこちら、「牧志そば」。

f:id:note103:20180305134319j:plain:w300

ちょっと見づらいですが、ドアに「ソーキそば専門店」と謳っています(肉筆で)。そしてさっきの店よりずっと安い。さらには(たしか)100円追加で沖縄名物の炊き込みご飯「じゅーしー」も食べられる。ということでそれらを注文。

f:id:note103:20180305132328j:plain:w300
(目が覚めるほどシンプルなソーキそば)

f:id:note103:20180305132713j:plain:w300
(じゅーしー。普通にボリュームある)

んー、おいしい!(ガッツポーズ)
やっぱりさっきの市場のお店、悪くはなかったけど、いわゆる観光客向けの感じだったのかな・・と思ってしまうほどこちらの店は大満足。諦めなくてよかった・・。

気を良くして、しばらく散歩。

f:id:note103:20180305122010j:plain:w300
(パラソル通り、と言うらしい)

f:id:note103:20180305123323j:plain:w300
(でかい)

f:id:note103:20180305124525j:plain:w300
(ぐっと来る)

f:id:note103:20180305124558j:plain:w300
(猫)

f:id:note103:20180305145634j:plain:w300
(ちんすこうはここで。@国際通り

こんな感じでグルグル回ってから、ふたたび公設市場に戻ってきたところでなんだか気になるお店を発見。たぶんこちら。
www.satoukibikotobuki.com

さとうきびジュース・・。ここで飲まなかったら後悔しそう、と思っておそるおそる注文。すると、いきなりナタみたいなものでバシン、バシン、とご主人がさとうきびをタテに割り始めて、おもむろに鉛筆削りみたいなジューサーに投入。これ、すごいな・・と思って許可を取って撮らせて頂きました。

youtu.be
(マシンの下の方にコップがセットされていて、そこにジュースが入る感じみたい)

できあがり。

f:id:note103:20180305153326j:plain:w300

そのまま店内の小机を借りて飲み干しました(持ち歩いたらゴミの処分に悩みそうだったので)。けっこうスッキリした甘さでおいしかったです。

そろそろこの辺で疲れてきたので、お土産探しモードに切り替え。先ほど、公設市場の食堂階で1枚のチラシを見かけて、「ゆっくる」という観光案内所が紹介されていたので、とりあえずそこへ向かいます。

machigwa.info

アーケード街の並びにある小さなスペースですが、スタッフさんたちがいろいろ優しく教えてくれました。この辺でお土産屋は・・? と聞くと、すぐ近くにある「てんぶす那覇」というビルの1階に、この案内所の姉妹店のような感じで「ショップなは」という店が入ってるので、そこに行ってみたら、とのこと。

ショップなは

このてんぶす那覇、ものすごい穴場スポット。1階には休憩できるスペースもあるし、トイレも観光案内所もある。国際通り近辺というのは、なにげに「ちょっと休める場所」というのがないので、これはすごい助かりました。ということで、お土産を探す前にしばらく休憩・・。

の後、お土産物色。店員さんにあれこれと相談。「こういうのありますか?」「こういうのは?」と。家族へのお土産はもちろん、個人的にはなんと言っても古酒。「古酒ってあまり馴染みがないんですが、どういうのがオススメでしょう?」と聞いて、教えてもらったのがこちら。

www.koosugura.jp

たしか30度で720cc。1800円前後。初めてだったらこれがオススメ、と。折しもキャンペーン的なタイミングで、寝かせる前のそれ(いわゆる泡盛)も小瓶でついてくるという。嬉しい!&即決。

トータルでたぶん20分ぐらい、あーでもないこーでもない、とじっくり選んで、ようやく会計。そして自宅への郵送もお願いしました。

とにかく先ほどのまちぐゎー案内所「ゆっくる」にしても、この「ショップなは」にしても、まったく何も知らない状態で那覇情報を知りたかったら最適の場所だと思いました。

とくに「ゆっくる」の方では「沖縄そばめぐり」のマップなどももらって、うわー、今さら! 最初にここに来ればよかった・・と激しく後悔しましたね・・。まずはこちらの案内所を訪ねて、全体を把握してから各所をめぐれば効率が良かったなあ、と思いました。というか、次行くことがあったらそうします。

飛行機ガチャ

上にも少し書きましたが、YAPC名物の飛行機ガチャも体験できました。この日は夕方ぐらいから雨が降り出して、一気に豪雨になり、しかし東京はさらにすごかったらしくて「羽田に着陸できないから」という理由でなかなか飛べなかったようです。

個人的には、その那覇の豪雨が地味にキツくて、靴も靴下もビショ濡れ・・替えの靴下は預けた荷物に入れてしまっていて、売店で靴下、いやせめてビーチサンダルでもないかと探しましたがそういうのも無く・・。けっこう心身ともにダメージを受けました。

そんな中、ヤケになって買った紅いもソフト。少しはストレスも軽減された気がします。

f:id:note103:20180305192207j:plain:w300

結局、飛行機は21時過ぎぐらいに那覇を飛び発ち、しかし自宅までは間に合わなかったので都内で一泊して、3/6に帰り着きました。
おつかれ!!

終わりに

ということで、記録しそびれていたあれこれをまとめて書いてみました。これで思い残すこともなく、ぼくのYAPC::Okinawaを終わらせることができます。(おそい)

三度、精神的にも肉体的にもタフなスタッフの皆さん、そして誰も彼もめっちゃ優しくてすぐに友達みたいになってしまった沖縄の皆さん、楽しい旅をありがとうございました! また行きたい!!

*1:本編に届かなかった方による発表イベント。

*2:これについては他の記事で詳しくやっているので割愛。以右などご参照のほど。21世紀の文字起こし(2) - the code to rock

*3:しかしこれ、自覚的には当日とまったく同じ環境で再現したら成功したんだけど・・どうして現場で駄目だったのかいまだにわからず。

*4:管理されている@argrathさんは前日のうちに、これまた @charsbar さんからご紹介頂いていました。

bashでコマンドライン引数を複数取得する方法を間違えて認識していた

これまで気づかなかった方が不思議なのだけど、ハマって解決したのでメモ。

.bashrcに自作関数fooを作って、ターミナルから呼び出す際に、引数としてbar1, bar2, bar3を入れるという場合。

受け取り側では、今までこんな感じで関数を書いていた。

function foo {
    local arg="$@"
    echo $arg
}

これでこのように呼び出すと、

$ foo bar1 bar2 bar3

このように出る。

bar1 bar2 bar3

だから当然、この中のbar2を取り出したいと思ったら、関数にはこのように書けばいいのだと思っていたけど、

function foo {
    local arg="$@"
    echo ${arg[1]}
}

実行しても何も出てこない。

おかしい・・と思ってこのようにプリントデバッグしてみると、

function foo {
    local arg="$@"
    echo all: ${arg[@]}
    echo arg0: ${arg[0]}
    echo arg1: ${arg[1]}
}

こんな感じ。

all: bar1 bar2 bar3
arg0: bar1 bar2 bar3
arg1:

ひとつ目の要素に全部入ってるんですね〜・・😇

で、しばらくハマっていましたが、関数内での引数の受取り時に、配列として受け取ればよかったようです。

function foo {
    local arg=("$@") # <= New!
    echo all: ${arg[@]}
    echo arg0: ${arg[0]}
    echo arg1: ${arg[1]}
}

実行。

all: bar1 bar2 bar3
arg0: bar1
arg1: bar2

OK!

んーむ、引数を用いた.bashrc内の関数、少なくとも3年は使ってきた(書いてきた)と思うんだけど、全然気づいてなかった・・。

同じ変数名でなくてもいい部分を同じ変数名にして初心者を混乱させてしまう現象

最近はProgateでRubyを中心にいろいろ未知の言語に触れている。

prog-8.com

Progate、控えめに言ってグレイトすぎるサービス。勉強法についてこれほど意識的なプログラミング学習サービスを他に知らない。「回転」や「定着」や「インセンティブ(ごほうび)」みたいなものが学習者にもたらす効果について、これ以上なくストイックに追求していると思う。リスペクト。

さてしかし、そのProgateのRuby学習コースIVの中で、ありがちな不備というか、これはまったくProgateに限ったことではないのだけど、すでにプログラミングをよく理解している人が初心者に教えたり入門書を書いたりするときにやりがちな悪例(というか推奨できない教え方)を目にしたので、記しておきたい。

具体的には、上記コースの「インスタンスメソッド」の項をやっているときに、こんなサンプルコードに出会った。(部分的に編集して抜粋)

class Menu
  attr_accessor :name
  attr_accessor :price
  
  def initialize(name:, price:)
    self.name = name
    self.price = price
  end
  
  def info
    return "#{self.name} #{self.price}"
  end
end

menu1 = Menu.new(name:"すし", price:1000)

puts menu1.info

実行すると、こんな。

すし 1000円

なんの変哲もない、シンプルなサンプルコードのように見えるけど、じつはここには初心者を混乱に陥れる問題が潜んでいる。

それはどこか? たとえば、ここ。

  def initialize(name:, price:)
    self.name = name
    self.price = price
  end

「name」と「price」という変数名が、本来必要な数に比べて多すぎる。

その言い方でわかりづらければ、以下の修正例を見てほしい。

  def initialize(name_foo:, price_bar:)
    self.name = name_foo
    self.price = price_bar
  end

先ほどはnameとpriceが3個ずつ出てきたが、ここでは1個ずつあるだけで、あとはname_fooとprice_barに置き換えられている。

さて、ここでは一体、何を直したのだろうか? それは、

  • 同じ変数名じゃなくてもいい部分にも同じ変数名を使っている

という状況を、

  • 同じ変数名じゃなきゃいけないところだけを同じ変数名にしている

という状況に変えた、ということになる。

最初のサンプルコードでは、「本来なら別の変数名でも構わないところ」が、なぜか(たぶん何となく)「同じ変数名」になっている。そして、初心者がそれを見てどう思うのかというと、それらの変数名が「同じでなければいけない」と思ってしまう。

だから教える人は、自分たち経験者にとっての「同じ変数名でなくてもいい場所」が、初心者にとっては「同じ変数名であってはいけない場所」なのだということを理解しながら教えてあげなければいけない。

もちろん、ある程度習熟してくれば、これらの箇所で同じ変数名を使いたくなるのは自然なことだと思う。

だけど、「同じ変数名にしてもいい(場合によってはその方が便利)」ということと、「同じ変数名にしなければならない」ということはやはりまったく別のことだ。

そして初心者にとっては、「なぜ同じ変数名にしたのか」を説明されなければ、それは「同じ変数名にしなければならない」という意味になる。

どうもこのたぐいのズレがプログラミングの入門書では散見される。そしてそのつど、「ああ、わかってないな・・」と思ってしまう。*1

変数名というのは、その中身がどんな値であるのか、外側から(中身を見なくても)大体判断できるように付ける目印というか、外装みたいなものだろう。

プログラミング経験者は、コード全体の文脈を踏まえて変数を見ているから、その中身をすぐに想像できるだろうけど、初心者はそうではない。初心者は同じ変数名を見たら、中身も同じなのだろうと思ってしまう。あるいは、「中身は違うかもしれない・・けど、確証はない・・」という状態。言い換えると、変数の中身を頭の中でトレースできていない。

中身も役割も異なるのに、同じ変数名が付いているというのは、人間で言ったら「別人なのに全身同じ服」みたいなもので、だからプログラミング初心者にとっての「値が異なるのに同じ変数名」という状況は、欧米人にとっての「背格好がほぼ同じでまったく同じ服を着ている日本人」みたいなもので、見た目から中身が異なることを判断するのは難しい。というか単純にまぎらわしい。

変数名は中身を想像させるために付けているのだから、経験者ほどにはその中身を想像できない初心者に対して、そういうまぎらわしいことをしてはいけないと思う。外から見ただけで、「ああ、あそこで使った変数をここでも使い回せるのか」と直感的にわかった方が、より効率的に、本質的な構文を学びやすくなるのではないだろうか。

話を戻して、上記の修正はメソッドの呼び出し部分にも影響を及ぼすから、一応全体に修正を行き渡らせたサンプルも示しておく。

class Menu
  attr_accessor :name
  attr_accessor :price
  
  def initialize(name_foo:, price_bar:)
    self.name = name_foo
    self.price = price_bar
  end
  
  def info
    return "#{self.name} #{self.price}"
  end
end

menu1 = Menu.new(name_foo:"すし", price_bar:1000)

puts menu1.info

https://i.gyazo.com/b61dff70f93446898a9fa85426e0e0e8.gif

しかしこの、「同じ変数名でなくてもいい部分」をつい普段のクセで「同じ変数名」にしてそのまま入門書などに書いてしまう現象、なにか名前をつけたい。そしてこうした問題がくり返されないための目印みたいなものにしたい。

*1:とはいえ、本の場合はこういうズレは編集者さんが見つけるべきだと思うけど。執筆者はプログラミングのプロではあっても、入門書のプロであるとは限らないわけだから。

文字起こししないインタビュー記事の作り方

前回、自動文字起こしについて書きましたが、

note103.hateblo.jp

今回は、文字起こしをせずにインタビュー記事を書く方法について書いてみます。

事例

これについてはすでに適用事例がありまして、もう1ヶ月以上前になりますが、以下の記事をこの方式で書きました。

geek-out.jp

普段引きこもりがちなぼくにしては珍しく、このときは山口まで出張して、YCAM(ワイカム)というアートセンターで同館のスタッフとしてあらゆるドキュメンテーション業務、また広報その他の分野において、ITを駆使しながら活躍されている渡邉朋也さんにインタビューしてきました。

*同記事については、以下のブログでも簡単に紹介しました。

note103.hatenablog.com

渡邉さんはとにかくすごいバイタリティの方で、どれだけ面白い話をしても尽きないし、ぼくもけっこうお付き合いが長いのでずーっと話していたらトータルで7時間弱、ICレコーダーが回っていまして。そうなると、もうこれ文字起こしなんかしてたら一生原稿できないな〜・・ということで、今回の方法を開発することになったのでした。

ちなみに、じゃあ普段はどうしているのかというと、たとえばぼくがこれまで手がけてきた以下のCDブックの場合、

このブックレットでは、毎回大体3万字弱ぐらいになる座談会を掲載しているのですが、これは平均で4時間ちょいぐらい、ずっと参加者の皆さんがおしゃべりをして、その録音をもとに8万字ぐらいの文字起こしテキストを作って*1、それを見ながら構成(文章全体の構造や流れなど)を立てて、その後はどんどん不要な部分をカットしながら原稿を作っていきます。

しかしながら、今回の7時間録音はさすがに文字起こしはしたくないというか、やってはいけないというか、文字起こし自体が目的(提出原稿)ならそれでもいいんですが、目的はその先にあるまとまった原稿であり、また現実的に締切りなどもあるので、「いかに効率よく、時間をかけずに、良質な記事を作れるか」という目的のもと、工程自体を新たに考えた次第でした。

作業の流れ

さて、その具体的な工程ですが、大まかに記すと以下の2つの流れに分かれます。

原稿編

  1. 事前に作った「A. 質問事項」と「B. そのカテゴリ」を原稿の「A. 文中の質問」と「B. 小見出し」に置き換えて列記する。
  2. 当日の実際の流れに応じて、原稿上の小見出しや質問を編集(追加・削除・修正)。
  3. それぞれの質問事項に対して、現場での相手のコメント(回答)をひたすら思い出しながら書いていく。
  4. 記憶に曖昧なところがあれば、キーワードマップ(後述)で該当箇所を特定し、音声を聴いて発言を厳密に把握した上で煮詰めていく。
  5. もうこれ以上できません! というぐらいまで3と4をくり返す。
  6. 文字数に合わせて分量調整。原稿を仕上げる。

キーワード起こし編

  1. とりあえず音声ファイル全体のうち、キリのいい時間で区切って(1時間ごと、または全体の2等分・4等分・8等分時点など)そのポイントだけ数秒〜数分ずつ聴いていく。
  2. 聴いた時点でどんな話をしているか、単語や短文など簡単でよいので、スプレッドシートのフォーマットに合わせてメモしていく。
  3. 1, 2の時間間隔を徐々に狭めながら、スプレッドシートを埋めて「キーワードマップ」を完成させていく。10分おきか5分おきぐらいまで詰めればひとまず充分。

後者の「キーワード起こし」というのがこの方法のキモで、これが従来の方法における「文字起こし」の代わりになります。

キーワード起こし/キーワードマップ

ということで、そのキーワード起こしについてもう少し解説します。解説用のサンプルとして、いつものように今年3月に沖縄で登壇したときの再現動画を使います。*2

www.youtube.com

これに対して何をやるのかというと、まずは録音全体を任意の間隔で区切り、それぞれの時点でどんな話をしているのか、スプレッドシートにメモしていきます。

この発表は全長85分ということで、それほどの長尺でもないので、5分間隔で区切りたいと思います。もしこれが7時間とかだったら、とりあえず15分おきぐらいまででいいと思います。あまり細かく区切るとやる気が続かないので・・。

とはいえ、85分でも初めから5分おきに聴いていくのは大変なので、まずは半分に分割してみましょう。大雑把に80分と考えて、半分の40分時点ではどんな話をしているでしょうか?

*視聴中・・

はい、発表全体の内容を知らないとちょっとわかりづらいかもですが、「再帰的にどうしたこうした」と言っています(40:20頃)。ここでは「シェル(ターミナル)」を使った作業に関して話していて、具体的には自作の「f」というコマンドを使って、任意のディレクトリ内で再帰的にファイルを検索する方法について喋っています。

それを把握できたら、以下のような感じでGoogleスプレッドシートに記入します。

f:id:note103:20180802114340p:plain

A列の10行目、「40」とある行に、

シェルのパートでfコマンドについて。再帰的にどうしたこうした

と書きました。

では、もう少し分割して、今度は20分時点と60分時点を埋めてみます。

20分時点では、「どんどんカットしていける。後で戻せる」と言ってますね。この部分は先ほどのシェルの話の前のパートで、Vimに関する話をしているようです。

60分時点では、「ついでというか。こんなのもやってますよ、というのをここに書いてますけど」と言ってます。こちらはシェルの話のひとつ後のパートで、Perlに関する話をしています。

ということで、これらを先ほどと同様に記入すると、こんな感じ。

f:id:note103:20180802114410p:plain

A列の「20」「60」と書かれた行に、それぞれ上記のメモを入れています。

こんな具合にメモを入れつつ、どんどん間隔を狭めていって、最終的にはとりあえず5分おきに「その時点でどんな話をしているのか」を記入したら作業完了です。

なお、これらの作業をするときには、上の例からもわかるように、喋ってる内容をそのまま書いてしまっても構いませんし、発言そのものには触れず、概要や感想をさらっと書くだけでもOKです。

たとえば、20分時点のメモでは、最後に「たぶんゴミ箱の話」と書いていますが、これはそのときに「Vimを使って文章を仮置きしておく場所」を「ゴミ箱」と呼んでいたことを指しています。こうしてヒントのようにキーワードをくっつけておくと、後で役立つことがあります。

ちなみに、上の方でもちらっと書きましたが、ぼくはこの作業自体を「キーワード起こし」と呼び、その結果としてできたものを「キーワードマップ」と呼んでいます。よって、この後は基本的にそのような言葉の使い分けをしながら説明していきます。

話を戻すと、このように5分単位(あるいは面倒なら10分単位でも15分単位でも)で「そのとき何を話していたか」を示すヒントというか、アンカーというか、索引を記しておくことで、後から「あの話、7時間のうちの何時間何分ぐらいの時点で喋ってたんだっけ・・」と思ったときに、効率的にその箇所を特定し、元の発言を聴き直すことができるようになります。

キーワードマップの効果と由来

ところで、上のスプレッドシートの画像を見ると、縦方向に5分おきに進む行だけでなく、横方向に0〜4まで進んでいく列も用意されています。次はこの部分について解説します。

これら横方向に伸びる列は、1分単位でメモを取りたい場合に使います。たとえば、上記シートのA3は「5」ですから、5Bには5分時点の発言にもとづく情報(メモ)が入っていますが、そのひとつ右に進んだ5Cは、5分に1分加算した6分時点の情報が入ります。同様に、5Dには7分時点の情報が入ります。

つまり、A行に記された5分おきの数字に、B〜F列の「0〜4」を足すと、そのセルの時間が算出されることになります。

「え、ということは、結局1分おきにメモを埋めなきゃいけないわけ? だったらやっぱり文字起こしぐらい大変なんじゃないの?」と思われるかもしれませんが、そうではありません。

この1分単位のマスは、あくまで「必要になったらここに書く」というために作ったものなので、必要が生じるまでは空マスのままで大丈夫です。

とはいえ、おそらく最終的には、この1分ごとのマスもけっこう活用されることになるとは思います。というのも、原稿を作成する過程においては、現場での発言内容を細かく精査していくことになるので、そうなると、次第に5分おきのメモ程度では足りなくなるんですよね。(この実例はもう少し後の方で画像で示します)

また、たとえばインタビューの中でとくに重要なキーワードが27分30秒時点で飛び出したような場合、後からそれを参照したくなることが何度もあるのですが、ここで5分おきにしか情報が入っていなかったら、対象の発言が存在している場所は「25〜30分のどこかにある」ということまでしかわからないので、それを見つけるまでに最大5分かかることになります。しかし、1分単位で記録してあれば、その後同じ情報にアクセスする際、「27〜28分のどこかにある」という風に1分以内に絞り込まれた状態で情報を取り出すことができます。

以上のように、キーワードマップとは、音声情報をテキスト情報として検索するためのツールなのだと言えます。もちろん、同様のことは文字起こしテキストでも(その方がより高い精度で)実現できるわけですが、それを作るにはあまりにも時間と体力を消費してしまうので、いわば「簡易版文字起こし」として、手っ取り早く、かつそれなりに効果を発揮するツールとして考えられたのがこの「キーワードマップ」で、それを最小限の労力で作る方法が「キーワード起こし」なのだと言えます。

ちなみに、この仕上がり状態を「マップ」と呼ぶのは、縦軸の行と横軸の列から特定のポイントを割り出していく様子が、緯度と経度から場所を特定していく「地図」のイメージに近いことによります。

時短・省力化

この手法を「労力」や「作業時間」の観点から考えてみると、最初の5分おきにメモを埋めるところまでなら1〜2時間もあれば充分でしょうし、また1分おきのメモにしても、必要に応じてそのつど数分で埋めていけるので、すべての作業時間を合わせても1日分の作業量にもならないと思います。

*ちなみに、1分ごとにセルを埋めていくときには、文字起こし的に、喋っている内容をそのまま入れてしまった方がラクだと思います。いちいちメタ的視点から感想や意見を考えていると、そのたびに頭を使って疲れてしまうので。逆に、音声をそのまま文字化していくのであれば、あまり頭を使いません。この一連の方法は、時短・省力化を目的のひとつとしていますから、無理をして疲れてしまっては意味がありません。

一方、いわゆる文字起こしというのは非常に時間がかかるもので、ぼく自身の経験で言うと、完成するまでには元音声の12倍の時間がかかるので、今回の音声なら85分*12/60で、17時間かかる計算になります。また、文字起こしは大変体力を使うので、1日4〜5時間に留めるとして*3、まる4日は使うことになります。

その上、冒頭に挙げた記事であれば音声ファイルは7時間弱という超・長尺ですから、これを文字起こしするには通常の何倍もの時間がかかる上、できあがるテキストも膨大な量になり、それに対する編集作業の労力は加速度的に膨らみ、さらには使われないテキストも多くなるため、ようは元の音声が長いほど文字起こしの非効率さは増大していきます。

ぼくの場合、普段のこういった原稿作成では、まず録音から文字起こしテキストを作成し、それを読みながら構成を練ったり、どの部分を残してどの部分をカットするかと考えたりするのですが、上記のような理由により、今回はその「文字起こしをもとに原稿を作っていく」のではなく、「まず完成像をイメージしながら構成を組み立てて、そのディティールを埋めていく際にキーワード起こしを(文字起こしの代替として)使用する」という方法をとることにしました。

木彫か粘土か

ここで一瞬、工程の説明から離れて概念的な話になりますが、上記の「文字起こしから作るのではなく、構成を立ててからキーワード起こしを使う」というイメージは、前者を木彫、後者を粘土制作に置き換えて捉えることができます。

前者の作業では、まず膨大な文字起こしテキストがあって、その中から不要な部分をカットしながら、本当に必要なところだけを残していきます(厳密には、カットした部分にも大事な要素はたくさんあって、それが残った部分に投影されるような操作をしていますが、大局的には大差ないのでここの説明では無視します)。

ぼくはこの作業をイメージするときに、丸太から仏像を彫り出す作業を思い浮かべます。仏像を彫る人は丸太を見ているようで、じつは丸太の中に初めから存在している仏様を抜き出すようにして、仏様以外の部分を削り取っていきます。*4

一方の後者、粘土制作の方は、初めに大きな粘土のカタマリがあるわけではなくて、まずは木片や針金などを使って芯棒を組み立てて、そこに徐々に粘土をくっつけていきます。*5

そして今回採用した方法は、普段やっている前者の木彫スタイルではなく、後者の粘土スタイルであったと言うことができます。

原稿作成の手順

では話を戻して、ここからは上記のキーワードマップを使って、どうやって原稿を作っていったのかを解説していきます。

1. 事前に用意した質問を並べて原稿の叩き台にする

前記の手順から「原稿編」の方を再掲すると、

  1. 事前に作った「A. 質問事項」と「B. そのカテゴリ」を原稿の「A. 文中の質問」と「B. 小見出し」に置き換えて列記する。
  2. 当日の実際の流れに応じて、原稿上の小見出しや質問を編集(追加・削除・修正)。
  3. それぞれの質問事項に対して、現場での相手のコメント(回答)をひたすら思い出しながら書いていく。
  4. 記憶に曖昧なところがあれば、キーワードマップ(後述)で該当箇所を特定し、音声を聴いて発言を厳密に把握した上で煮詰めていく。
  5. もうこれ以上できません! というぐらいまで3と4をくり返す。
  6. 文字数に合わせて分量調整。原稿を仕上げる。

ということで、まず1番の質問事項について、これはインタビューの現場で「とりあえずこれについては聴いておきたい」という内容を事前に書き出しておいたもので、ぼくの場合はルーズリーフ2〜3枚に手書きでリスト化して、色ペンでさらにマーキングやメモなどを付して用意していました。

このときの質問は全部で23個でしたが、質問にはある程度共通する傾向というか、カテゴリが存在するので、そのカテゴリごとにまとめておきます。

冒頭の記事で言うと、最終的には以下5本の見出しが立っていますが、

  • YCAMってなに?
  • YCAMインターラボの活動
  • IT技術の生かし方
  • オープン化の取り組み
  • 既存の文化事業を超えて

元々の質問はプラス2〜3本(計7〜8本)のカテゴリに分かれていました。

で、この段階では実際のインタビュー内容については一旦忘れて、その質問カテゴリを「小見出し」として、またそれぞれにぶら下がっている質問事項を各見出し内のコンテンツとして列記していきます。それだけ、といえばそれだけですが、これがこの後の作業の大元というか、叩き台になります。

2. 質問の内容やカテゴリ(小見出し)を実態に即して調整

1の状態はあくまで事前に作った資料を元にしたもの(叩き台)なので、今度はこれを現場で行われたインタビュー内容に合わせて調整していきます。

ただし、ここでは次の工程3と同様、当日の流れを厳密に再現する必要はありません。事前に予定していた質問のうち、カットしたものを外したり、追加したものを適宜加えておく程度で充分です。

3. 想像力で回答を埋めていく

次に、上記2で作った質問に対する相手の回答を、自分の記憶や想像でどんどん埋めながら全体の流れを作っていきます。このとき、相手が実際にそう言っていたかどうか、という確認はひとまず後回しで大丈夫です。

なぜなら、第一に大半の内容はその後に修正することになるからで、第二に、この段階でそういった確認をちまちまやっていると全体の流れを作りづらくなるからです。通常、読者は記事を最初から最後まで1本の線を辿るように読んでいきますが、現場ではあちこちに話が飛びながら進んでいますから、現場の実態を重視していたらいつまでも読者に読みやすいものは作れません。

よって、ここではあたかも自分が最初の読者であるかのように、「こういう流れでこの記事を読みたい」と思う文章を作っていきます。上記1と2の工程はその準備であり、この3はそれを踏まえた通し稽古のようなものです。実際の進行順と異なるからといって、「事実と違うぞ!」などと指摘してくる人はいませんし、そもそも執筆者は現場で話した内容を誰よりも把握しているはずなので、「事実と異なるかも」などということは心配せず、まずは自分の想像の世界で1本の物語を作ってしまいましょう。

4. 記憶が曖昧な箇所をキーワードマップで確認する

ある程度、質問と回答のやり取りを埋めて全体の流れができたら(あるいはその途中でどうしても現場の発言を確認したくなったら)、ここでキーワードマップの登場です。おもむろにGoogleスプレッドシートの検索機能を使って、検索したい箇所で話しているはずのキーワードを入力し、音声ファイルの中のどこでそれを話していたのか、突き止めましょう。

不思議なことに(あるいは当然のことかもしれませんが)、現場で話したことや、相手から聞いた言葉というのはけっこう頭の中に具体的に残っているもので、それを検索クエリとして使うと、大抵の発言は見つけ出すことができます。

たとえば、山口の記事で使ったキーワードマップで「パブリシスト」を検索すると、こんな感じになります。(一部伏字の上抜粋)

f:id:note103:20180802164652p:plain

はい。1時間21分の時点で話題に上がっているようです。

もし目当てのキーワードが見つからなかったら、その前後の記憶を辿って、近いところで話しているはずの別のキーワードを検索してみましょう。それでも見つからなければ、最初にマップを作った際の間隔が少し広すぎる可能性があるので(10分、15分間隔になっているなど)、一旦作業を止めて、5分おきぐらいまで詰めておくのも有効です。

大抵の場合、5分おきまで詰めておけば、ピッタリその箇所を見つけられなかったとしても、「この20〜35分の範囲で話題に挙がっていたことは間違いない」ぐらいまでは特定できるので、そこで15分間、再生速度を上げて音声を聴き通してもよいかもしれません。

5. 通して読める原稿に仕上げる(分量は調整不要)

あとはひたすら、3と4をくり返します。このとき、文字量についてはまだ考えなくて大丈夫です。まずは自分が通して読んで面白いと思えるまで原稿を仕上げてしまいましょう。

また、3の段階では記憶だけで流れや内容を作っていきましたが、この段階で、すべての発言内容に関して、「相手が本当にそのような発言をしていたかどうか」を確認します。

といっても、ここでは本人の「現場での発言」と「文中の発言」が一言一句同じである必要はありません。記憶で再現した発言と実際のそれが完全に一致するはずはありませんし、一致させなければならない理由もありません。むしろ、一言一句同じにしたところで本人の言いたいことを再現できるとは限りませんし、逆に記憶や想像をもとに再現した文章の方が、より自然に本人の言いたいことを伝えられている場合もあります。

文字起こしをもとに文章を作る際には、つい現場での実際の発言に引っ張られがちになりますが、この方法を使えば、そうした足かせにとらわれることなく、より相手の意向を汲み取りながら、臨場感のある文章を作りやすくなります。

そしてこのとき、キーワード起こしは相手が本当にそういう意味のことを言っていたことを確認するための裏付けとして、あるいは「相手が言ってもいないことを言ったことにしていないか」を確認するためのチェックシートとして機能します。

なお、今回の山口の記事では、この段階で冒頭のリード文(インタビューに入る前の簡単な解説)と、最後のプロフィールも作っておきました。

導入と締めを置くことで、よりリアリティを持って、「最初の読者」として本文を読み通しやすくなります。

6. 原稿の仕上げ。分量調整

最後に、原稿の分量を目的の文字数まで調整します。なお、これまでとくに説明していませんでしたが、この段階の原稿の文字数は、目的の文字数よりもオーバーしている前提です。もし目的よりも少ない場合には、手順2まで戻って構成を再考する必要があるかもしれません。

一方、今回の方法では上述の「木彫」方式ではなく、「粘土制作」方式ですから、目的の文字数に比べてべらぼうにオーバーしていることもないはずです。ある程度の構成・構造を立てた上で、その完成像をイメージしながら肉付けしてきたので、着地点も比較的目的に近いところにあるのではないかと思います。

その他、原稿の仕上げに際して、執筆者として気をつけることはいろいろあると思いますが(読者に対して提示すべき前提や、用語の説明に抜けはないかなど)、今回の本論とはズレるので割愛します。*6

まとめ

以上、「文字起こし」を使わずに「キーワード起こし」でインタビュー記事を作る方法をまとめてみました。

いつものように、だいぶ長くなってしまいましたが、要点は、

  • 録音した音声が長大になるほど、文字起こしを使った方法では効率が悪くなる
  • キーワード起こしを使えば、その無駄を軽減できる。(時短・省力化)
  • キーワード起こしを用いる場合には、木彫型ではなく粘土型にスタイルを切り替える

といったところでしょうか。

個人的には、この方法を使うことによって、執筆者は現場での発言に縛られすぎることなく、より自由で本質的な表現を実現しやすくなると思っています。

ただし、それは「キーワード起こしの方が文字起こしより優れている」という意味ではありません。

たとえば、1時間以内ぐらいの内容だったら、一気に文字起こしをしてしまった方が早いかもしれませんし、あるいはインタビューではなく、座談会のように複数の人が同時に喋るようなものだったら、記憶しておける現場の情報も限られてしまい、文字起こしに頼った方がよいかもしれません。また、事前の構成ができないような場合も、素直に文字起こしから入った方がよいかもしれません。

具体例としては、初めの方で触れた、坂本龍一さんの音楽全集に掲載している座談会にしても、事前にわかっているのは大まかなテーマとそれにもとづくいくつかの主要曲ぐらいで、あとはその場の流れに合わせてほとんど即興で話が進んでいきます。よって、この場合は事前に構成を組んでから肉付けしていくような作り方は不可能で、まずは文字起こしを中心にベースの原稿を作って、それを眺めながら、自分の意識は極力排して、テキストが進みたい方向へ進むようにサポートしてあげる、という感じで手をかけていきます。*7

ただいずれにしても、手法の選択肢が増えれば、それだけ作業の効率が上がり、成果物のクオリティが上がる可能性も高まるので、誰かの参考になればと思ってまとめてみました。

*1:この音楽全集の文字起こしは直近でも昨年の1〜2月頃にやったものなので、当時はまだGCPの使い方なども知らなかったし、Googleドキュメントでの文字起こしもそれほど費用対効果が高くなかったので今のところすべて人力で起こしてます。

*2:誰にも許可を取ったり気を遣ったりする必要がなく使いやすいので。

*3:実際にはそんなにやらない気もしますが・・。

*4:あくまで喩えなので、本当にそんなことを考えてやっているかはわかりません。

*5:これも喩えなので、実際のところはわかりません

*6:文字校正などについても同様の理由で割愛。

*7:オカルトっぽい説明ですが、きちんと説明しはじめるとそれだけでまた長文になるので割愛。