ワット・シリントーンワララーム・プープラオ

ワット・シリントーンワララーム・プープラオというタイの寺院があることを以下のツイートから知った。

Google画像検索に投げてみたらそれらしい検索結果が返ってきたので、固有名詞っぽいものを切り出して検索してみたら

www.thailandtravel.or.jp

日本向けのタイ観光庁の公式ページが見つかった。

Google画像検索はずいぶん進化していて、「一致した画像を含むページ」の一覧の前にこの寺院に関するYouTubeの動画とか概要サイトとかが表示されるようになっていた。検索キーとして与えた画像は含まれていなかったから、単純な画像のマッチング以上のことをしているのだと思う。

本堂の壁で光っているのはカンラパ・プルックという種類の樹木で、タイでは吉兆とされているらしい。

The spectacular image at the back of the chapel is a gigantic glowing kanlapapruek tree, the work of artisan Kanagorn Parinyapunno, a friend of the abbot of Sirinthornwararamphooprao Temple. Inspired by the ‘Tree of Souls’ in the blockbuster movie Avatar, Kanagorn used phosphur to coat the images of the kanlapapruek tree and other motifs on the ground.

www.sawasdeemagazine.com

住職の友人が映画「アバター」に登場する「魂の木」にインスパイヤされて制作した、という話があって面白い。

寺院の成り立ちを紹介するページGoogle翻訳で読んでみると、歴史はそれほど長くないものの、一度は廃寺になっていたものが政治的な意図もあって再建されて今に至るという感じで、思ったより複雑な背景を負ったおしゃれスポットだった。

Go Language Specification輪読会 #8に参加した

最近参加しているkyoto.goでGo Language Specificationを読んでいきたいという提案が出されていて、「そういえばGo Language Specification 輪読会というのに参加したいと思ってたんだった」と思い出して参加してきた。

Goのspecは気になったところを雑に読んだりしてたけど、きちんと読んだことはなかった。輪読をきっかけにしてもう少し深い付き合いができるようになればいいなというのが主な参加動機。

当日のタイムテーブルはこんな感じ。

  • 19:00 Google Meetに集合 (URLは別途連絡します)
  • 19:00 ~ 参加準備 (Slackへの登録、Scrapboxへの登録、自己紹介を書く等)
  • 19:10 ~ 軽めに自己紹介 (1人max1分)
  • 19:20 ~ 輪読開始
  • 20:20 ~ 10分休憩
  • 20:30 ~ 再開
  • 21:30 終了

Go Language Specification 輪読会 #8 - go-spec-reading

意思疎通が難しくなりがちなリモート環境で、どうやって輪読会を運営しているのかという点にも関心があった。今回の記事ではそのへんの話だけ書きます。

ツール

まずはGo Language Specification 輪読会(「Spec輪読会」と略す)で採用されているツールについて、kyoto.goと比較しつつまとめてみる。

Google Meet

Spec輪読会はGoogle Meetを使ってリモートで開催されている。各自がボイスチャットで参加して、司会のsyumaiさんがspecのページを画面共有するという形で進行する。

PCからブラウザ版を使って参加したけど、特に問題なく2時間半のセッションを終えられた。UIも直感的で使いやすいという印象を持った。2021年3月までは最大で24時間まで連続してビデオ会議できるとのこと。

japan.cnet.com

kyoto.goもコロナ禍の影響でリモート開催が続いていて、こちらではZoomを使っている。使い勝手はGoogle Meetと大きく変わらないが、無料版ではミーティングの時間は40分に制限されている。強制的にタイムアップされるのも緊張感あっていいのでは、という気がしているけど、Spec輪読会では休憩を挟みつつ2時間半という長めの時間枠(kyoto.goでは19:00〜20:00の1時間枠)でもダレることなく議論が続いていた。

Slack

イベント開催中以外のやりとりはGophers Slackの#spec-reading-20-jaで行われている。

これはkyoto.goも同じ。

Scrapbox

kyoto.goと同じくScrapboxが用意されている。

scrapbox.io

後述のGoogle Docsがよく使われていて、いまは主に参加者の自己紹介テキスト置き場として使われている。

Google Docs

Specを丸ごとGoogle Docsに貼り付けている。各自が気になった箇所にコメントをつけることができる。

docs.google.com

syumai/gpgsync

github.com

同時編集できるGo Playground。見慣れた編集画面でコードを同時編集できるのはかなり面白くて感動した。

このツールのおかげで輪読中に出てきた疑問点を即座に検証して全員で共有することが可能になっていて、輪読の効率がかなり高まっている。

ラクティス

運営上のプラクティスのように見えたものをまとめてみる。プラクティスと表現したけど、おそらく明文化されているわけではなくて、輪読会を繰り返すたびにゆっくりと暗黙的に形成されていったパターンも多いのではないかと思う。

(初回参加だし、どんな意図があるのか他の参加者に確認したわけではないので内容はまったく的外れかもしれない。)

開始前の雑談

  • やること: 最初の10分ぐらいで参加者が三々五々集まってきて雑談をする
  • 効果:
    • 簡単なアイスブレイク
    • インフォーマルな情報共有: Spec輪読とは直接関係ないけど面白そうな話題を簡単に共有できる

自己紹介タイム

  • やること: Scrapboxに簡単な自己紹介文を書いて、順番に短く自己紹介や近況報告を行う。
  • 効果:
    • 定型的なアイスブレイク: 他の人の自己紹介文を見ながら自己紹介を書くので初参加でもハードルが低そう
    • インフォーマルな情報共有

自己紹介で「Goのほかに○○という言語を使っています」とか「言語処理系を作っています」みたいな情報が開示されていれば、「これは○○という言語ではどうなっているんでしょうか?」とか「パーサーを書くときってこういう文法の処理難しくないですか」みたいに発展的な話をするきっかけになる。

自己紹介の内容をScrapboxに書くというのは以下のようなメリットがありそう。

  • 自分の番が回ってきてから何を話すか考えるという時間の無駄を省ける
    • 参加回数が多い人の自己紹介が上のほうに書かれているので、フォーマットを真似して書ける
  • 「あの人はどういう人だったっけ?」と思ったらあとから再読できる

全部入りScrapboxページ

  • やること: 会に参加するために必要なリソースへのURLをScrapboxの各回のまとめページの上部に貼っておく
  • 効果: 各回のまとめページに直接掲載しておくことで、初めての参加者の参加準備が楽になる。
    • Scrapboxの招待リンクとかは「はじめに」のページだけに書きがち。

Goクイズ

輪読会終了後に、その会で話題になったエッジケースを組み合わせて「このコードの実行結果はどうなるか?」というクイズをTwitterで出題する文化(?)があるらしい。

アウトプットをすることで学習の理解と定着が進むというのはよく知られているけど、クイズを出題するというやりかたは楽しくてよさそう。クイズという形式はTwitterと相性がよさそうだし、参加してなかった人も輪読会で出てきた知見を得られるというのもよさそうな感じがする。

自分でも作ってみた。

この挙動は https://golang.org/ref/spec#Method_values で規定されていそう。

所感

輪読会に途中参加してついていけるか心配だったんだけど、ようこそ新規参加者という雰囲気で迎えていただけてありがたい。自分の担当箇所では理解があいまいなところを訂正していただいたりして勉強になりました。

スクラムフェス大阪2019に行ってきた

www.scrumosaka.org

勤務先の人に勧められて、2/22(金)と2/23(土)の両日に開催されたスクラムフェス大阪2019に行った。YAPCとかRubyKaigiのスクラム開発版みたいなイベント。

勧めてくれた人に「スクラムってよく知らないんですが、なんかいい入門書ありますか?」と聞き、「とりあえずスクラムガイドは読んでおきなよ」と教えられて、「Amazonでは買えないんですか?」と返して苦笑されるレベルなのでおっかなびっくりという感じだった*1

懇親会

いきなり懇親会の話をします。セッションで扱われてる問題は今の自分とはレベル感が違いすぎてそれほど書くことがない。

  • なんだか開発プロセスがうまく回っていないんです。マネジメント層がきちんと仕事できてないから…
  • 僕らのチームは不具合が多いんだそうです。まあ複雑なプラットフォームを相手にしていますからね…

という話をしたら「振り返りができてない? じゃあ君が率先してやれば?」「その複雑なプラットフォームで起きる不具合を減らすにはどうしたらいいと思う?」と問いかけられて胸を突かれる思いがした。言葉につまってしまって、やれることをやらずに責任を外部に押し付けていたと気づいた。

何ヶ月も前から、もしかしたら1年も前から、今のままではまずいという感覚はあって、「今の仕事で脳味噌が腐っていってるんじゃないかと心配しているとしたら、 たぶん腐っているよ。」*2という文句が何度も頭をかすめてはいたが、腐っているのは職場環境のほうだろうとなんとなく思っていた。懇親会という場で第三者から指摘をいただいて、なるほど自分の中でなにかが淀んで腐っていたと理解できた。

問題に向き合うことを先送りしていると問題自体が見えなくなってしまう。自分の言葉から腐敗臭を嗅ぎとるというのは恐ろしい出来事だった。

セッション

ざっといくつかのセッション感想だけメモしておく。

感想

もしかしたら役に立つ知識が得られるかもしれないからとりあえず一度行ってみようというふわっとした感じだったけど、参加できてよかった。

懇親会では問題の洗い出しからどんなアクションを起こせば現状を改善できるのかというところまで、時間の許すかぎり相談に乗っていただきました。感謝しかない。またどこかの機会に、こうやったら前よりよくなりましたとご報告できたらいいなと思います。

初回からこういう場ができあがっているというのはすごいことで、イベントの運営を支えた方々やコミュニティの皆さんの活動の賜物だと思います。ありがとうございました。

おまけ

ノベルティとしてYahoo! Japanのふせんケースが配られてたのでもらってきた。ロゴがかっこいい。

f:id:yebis0942:20190225123143j:plain

*1:スクラムガイドはウェブで公開されている!

*2:素晴らしきハッカー

「詳細は_こちら_」のようなリンクがダメな理由を調べた

「詳細は_こちら_」のようなリンクがダメな理由? - Lambdaカクテルを読んで、気になったので調べてみた。

「詳細はこちら」式のリンクの乱用は「here症候群」とも呼ばれている。here症候群 | 鳩丸ぐろっさり (用語集)では、

  • アンカー部分だけを抽出する機能が使えなくなる(音声系ブラウザなどのリンク読み上げ機能や、サーチエンジンの重み付けなど)
  • 視覚系ブラウザでも、アンカーからリンク先の内容が想像できたほうが分かりやすい

という理由が挙げられている。

W3Cの文書では、Web Content Accessibility Guidelines (WCAG) 2.0日本語訳)というのがあって、"2.4.4 Link Purpose (In Context)"と"2.4.9 Link Purpose (Link Only)"でリンクのテキストについての推奨事項が説明されている。「詳細はこちら」の場合、2.4.4には適合するが、2.4.9には不適合(here症候群とWCAG 2.0 | 富永日記帳の解説が参考になる)。各推奨事項の隣にあるHow to MeetやUnderstandingのリンクを辿ると、実装例や意図などが読める。かいつまんで書くと、例えば肢体・認知・視覚に不自由のあるユーザーに便利ということらしい。

国内の文書では富士通ウェブ・アクセシビリティ指針 指針39 : 富士通が見つかった。内容はWCAG 2.0の2.4.9とだいたい同じ。

Alfred便利

Alfredみたいなオシャレツール使わなくても、ターミナルから叩けばいいじゃんと思ってたけど、実際に使ってみると、どこでもワンタッチで入力ウィンドウが出てくるのは快適。

駅探検索AlfredWorkflowをさらに更新した - あんパン

ソースコードに効率的にアクセスする(git grep + peco, helm plugins)

これまでhelmとシェルの補完機能で生きてきたけど、ファイル数が100を超えるようなプロジェクトだとそれではしんどくなってくる。もう少し規模の大きいコードベース向けの既存の解決策をいくつか紹介する。

git grep (またはgit ls-files) + pecoで絞り込む

Goソースをgrep → pecoで選択 → vimで該当行番号にジャンプした状態で開くワンライナー - Qiitaが参考になる。ファイルパスを**/*.goから**/*.pmなどに変えればPerlソースにも使えるし、後述するようにawkの部分を変えるとEmacsでも使える。使ってる様子

git find foobarのように使えるシェルスクリプト版もあります。

git findコマンドを作った - あんパン

仕組み

  • git grep: Gitで管理しているファイルからgrepする。
    • 検索対象はカレントディレクトリ以下になる。つまり/lib/Bananaにいる状態では/lib/Plackや/scriptなどのファイルは検索されない。
    • grepのオプションが通らないことがある。
  • peco: ターミナル上で汎用的なインクリメンタル検索をするためのツールpeco/peco - GitHubGIFデモを見るのが早い。

これらを組み合わせると、grepして見つかった行を選択したら、そのファイルのその部分を開いたエディタが立ち上がるということができる。

pecoのインストール

Installation - Mac OS X / Homebrew

Macを使っていてHomebrewを入れていれば二行のコマンドを入れるだけで30秒以内に使えるようになる。

その他

EmacsPerlのファイルを開く場合はこんな感じ。/Applications/Emacs.app/Contents/MacOS/bin/emacsclient -n $(git grep -n "$1" -- "**/*.pl" "**/*.pm" "**/*.t" | grep -v "[0-9]:\s*//" | peco | awk -F ":" '{print "+"$2" "$1}')

ファイルパスだけを使って絞り込みたいときにはgit ls-filesが使える。

Emacs

シェルを使わずにEmacsだけでアクセスするためのelispを紹介する。helmプラグインがメイン。

ファイルパスから開く

helm-ls-gitというelispもあるが、anything-git-projectの helm版. - Life is very shortはmodified filesを上のほうに表示してくれたりするので(と書いたけど、helm-ls-gitにも同様の機能があった)、僕はこちらを使うことにした。

git grepから開く

yasuyk/helm-git-grepがよさそう。package-installからインストールできた。

helmを使ってない人はemacs内でgit grepする方法 - $shibayu36->blog;が便利なのでは。

todo

ソートアルゴリズムの勉強2

ソートアルゴリズムの勉強 - yebis0942’s blogの続き。

マージソートのマージする部分で、「配列Aと配列Bのどちらも空ではないなら」という条件文にするべきところを「配列Aと配列Bのどちらかが空ではないなら」と書いていたのが原因だった。コードを慣れないC++で書いているので、自分の言語に対する知識が間違っているのか、ロジックの理解に問題があるのかはっきりしなくて難しい。しかし自分がそれなりに習熟しているのはスクリプト言語ばかりで、それでソートとかを書いてもアルゴリズムの勉強をしているという気分が出ない。

vector<int> mergesort(vector<int> v) {
  if (v.size() == 1) return v;

  if (v.size() == 2) {
    if (v[0] > v[1]) swap(v[0], v[1]);
    return v;
  }

  vector<int> a, b;
  for (int i = 0; i < v.size(); i++) {
    if (i <= v.size() / 2)
      a.push_back(v[i]);
    else
      b.push_back(v[i]);
  }

  auto a_sorted = mergesort(a);
  auto b_sorted = mergesort(b);
  int a_sorted_idx = 0;
  int b_sorted_idx = 0;
  vector<int> sorted;

  while (a_sorted_idx < a_sorted.size() && b_sorted_idx < b_sorted.size()) { // ここを || にしていた。
    if (a_sorted[a_sorted_idx] > b_sorted[b_sorted_idx]) {
      sorted.push_back(b_sorted[b_sorted_idx++]);
    } else {
      sorted.push_back(a_sorted[a_sorted_idx++]);
    }
  }

  while (a_sorted_idx < a.size())
    sorted.push_back(a_sorted[a_sorted_idx++]);

  while (b_sorted_idx < b.size())
    sorted.push_back(b_sorted[b_sorted_idx++]);

  return sorted;
}