北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

今日はYAPC::Okinawa 2018 ONNASONの日です

YAPC::Okinawa 2018 ONNASON に参加していますので、自分用のメモを残しておきます。

オープニング / @codehex さん

  • めんそーれ!
  • A会場は飲食禁止
  • モバイルルータは電源切ってね
  • 赤いリストバンドの人を撮影しないで
  • JPAの行動規範を守ってね
  • #yapcjapan にて案内あるよ
  • 懇親会受付は12:00からです
  • ベストトーク章に投票してね
  • リゾートYAPCをお楽しみください

万国之津梁 / @chasbar さん

  • London Perl Workshop 2017 へ参加した
    • YAPCより少し小さいイベント。国単位
    • 基本言語は英語で集まりやすい
  • PAUSE Operating Model について聞きに行った
    • 削除やコメンテナの登録など
    • DBIx::Classでトラブったので、新しい権限が考えられている
  • それだけで15万はもったいないので、LTしたり、クラフトジン(お酒の仕事)とか、古代遺跡(大学の専攻)とか、橋(YAPC::Okinawa)とか
  • 海外カンファレンスの参加で観光の予定を入れておくと良い
    • ジェットラグの緩和
    • 必要な品の入手
    • 入国審査の簡易化
  • LT Perl::PrereqScanner::NotQuiteLite
    • Y8でのネタ
    • require とかをうまく扱う
  • PAUSEのオーナーと議論ができた
    • 新しいモデルがどの程度役に立つの?
    • Mojoliciousの作業を優先することになった
  • YAML::PP : bookの扱いがJSONと違う問題
  • DBD::SQLite
    • 巻き添え食って新しいモジュールをリリースできなかった
    • ブロックしていたDBIx::Class の変更をリリースしてもらえた
    • SQLite
  • YAPC::EUにも参加した
    • JSON::PP が壊れた問題
    • 数値に見えたらクォーテーション外すようにした
    • Cpanel::JSON::XS の問題が残っている
    • LT PAUSEをMojoliciousにポートする話
    • DBICのメンテナと話したり、Booking.com を訪問したり
  • "Three Little Words" Damian Conway さん
  • "glob() の問題について" Lukas Mai さん
  • "perldoc の改善" Herbert Breunung さん
    • perlの同梱ツールを良くする、という話が聞けるのは海外のyapcの醍醐味
  • "cperl" Reini Urban
    • perlに機能が入らなくて不満の方は聞いてみると
  • DBIC のメンテナと話をして、リリースはしてもらった
  • gugodさん「台湾語を学ぶ」
  • Vickenty Fesunov さん
    • YAPC Hokkaid Tシャツ来てた
  • YAPC::NAには参加できなかった
    • 6月はフリーランスには大変な時期
    • が、ビデオは見れる
    • CPAN Testers has an API / Doug Bellさん
    • perl5のヘルステストの仕方について / James E. Keenan
      • CPANモジュールもテストしている
      • 社内のものも、公開するとテストしてもらえるのでオススメ
  • Perl Toolchain Summit 2017
    • PAUSEのMojolicious化のため行った
    • 終わらなかったので、持ち帰り
    • skaji++ さんはPerl5でもPerl6でもいい仕事をしていた
  • FOSDEM 2017
    • YAPC::Kansaiのための情報をとりに
    • @INCdo の絡みの問題の議論
    • Perl 6.d
    • MooseじゃないOOP、Perl6高速化、など
    • UVのためにtypesterさんの連絡先アナウンスしたり
  • ???
    • YAPC::Hokkaido用の情報
    • ビートルズの情報
    • 5.14以降であれば Try::Tiny よりも別のモジュールを使ったほうがいい / Paul Evansさん
  • YAPC::EUは参加できなかったり
  • QAハッカソン2016
    • PlackにしたときにPAUSEが遅くなったかも
    • 遅くなっていたら、また行ったときに修正します
  • FOSDEM 2015
    • Perl6のクリスマスリリース
    • sasadaさんのトーク聞いたり
  • 海外への行く橋はいくらでもかかっている
    • ビデオが見られる
    • スケジュールを見るだけで情報がわかる
    • 自分たちがやっていることとの違い
    • 自分自身が、橋をかける、かけない、を決められる
  • 海外のコミュニティへ行き続けることが大事
    • 最初はお客さん
    • その後知り合いになったほうが、いい情報が得られる
  • 海外
    • Perl Toolchain Summit 2918
    • The Perl Conference YPAC::NA
    • The Perl COnference YAPC::EC
  • Q). 日本と海外の違いは?
    • A). 海外のカンファレンスではコアメンバーにあえてperl自身の議論ができて楽しい
  • Q). 過去と比べて何か違いはあるか?
    • A). 2012年ころからの傾向しか比べられないが、数字だけで見ると、日本のほうが海外より非活発になる速度が速そう

HTTP/2で速くなるときならないとき / Kazuho Okuさん

  • HTTP1.1とHTTP2のどちらが速いのか → 「場合による」と思っている人が一番多い
  • 速くする理由 → 利益につながる
  • HTTP2が必要な理由
    • ロード時間がバンド幅に比例しない
    • レイテンシに比例している
  • 理由 → HTTP/1 に多重性がないため
    • データが流れない時間が発生する。
    • 4割しかデータを流せてなかった → バンド幅が増えるほど利用率はどんどん下がる
  • HTTP2ではリクエストをまとめて投げて、まとめて受ける
    • バンド幅を有効利用
  • HTTP2は制定されて3年
    • バイナリ、多重化、ヘッダ圧縮、push
  • ブラウザとサーバによって変わる
  • 遅くなるケースもあるのはなぜ?
    • クライアントがJS, CSSを優先することをサーバに伝えねばならない
    • サーバも、それに対応できなければならない
    • ワークアラウンドの対応が必要。それをやってるかの差
  • push ができるようになると、HTMLとJS, CSSも同時に戻せてさらに速い!!
  • 大量の画像を取る、WebPageTestを使ったベンチマーク
    • 2% 程度のパケットロスを入れると、HTTP2がHTTP1より遅くなる
    • 3Gとか、遅いネットワークも同等の傾向
    • ChromeFirefoxでも傾向は同じ
    • DCL(DOM content loaded)や Speed Index (Googleさんの指標) も似たような傾向
  • 理由
    • HTTP1のほうが同時接続数が多いので、パケットロスがあっても数で速度を稼げる
  • 実データでのテスト
    • 例: Fastly customer page (3MB)
    • 同様の傾向。パケットロスがあるとh2が不利に見える
    • 3G回線では、パケットロスがあってもHTTP2とHTTP1は半々
    • ロード完了時間はパケットロスの影響を受ける。DCLは影響を受けにくい
  • 実世界のパケットロスはどのくらい?
    • 7割の人はパケットロスを経験していない
    • 1.5msの人もいるので、ないといえない
  • QoS : ユーザごとの使用帯域を均等に
    • 帯域を使いすぎるとパケットロスは上昇
  • 色んな要素が関連しすぎるので、HTTP2とHTTP1の速度差は「場合による」
    • こういうときはどっちが速い、とか言ってる人は信用ならない
    • ベンチマークを取るしかない
    • セグメントを分けて95パーセンタイル値を比べるとよい
  • webpagetest を使う
    • 世界中に端末があり、指示を出すとベンチマークしてもらえる
    • 自分の顧客と同じ環境とは限らない (ex. androidが顧客の場合など)
  • Resource-Timing
    • リソースのロード時間をJSで取得するしくみ
    • 独自に継続的に集計ができる
    • H2へ以降すべきかの判断材料にしたりできる
    • iOS safariにも入った
  • Resource-Timing では問題の切り分けには使えない(ネットワークのせい?サーバのせい?)
    • Server-Timingヘッダ : HTTPヘッダにサーバ側での値を埋め込める
    • Server-Timing: connect; dur=132, req; dur=0, process; dur=230
    • HTTPヘッダのトレーラーの仕様を使っている Server-Timing: response; dur=11, total; dur=373
  • HTTP/2はブードバンド時代に必須
    • 断定的な結論は信じない
    • 測定と最適化のループ / webpagetest, Resource-Timing, Server-Timing
  • Q). パケットロスが半々の場合に、どうするのがいい?
    • A). それを取得する仕組みがない。いい質問なのでゆっくり考えたい
  • Perlのパの字も入ってなかったが、次でxaicronさんが話します

そろそろPerlでのHTTP/2について触れたい / xaicron さん

  • HTTP2とHTTP1はどちらが速いと思いますか? → 「場合による」
  • はじめにSPDYがあった
    • 2010年のChrome6から実装
    • HTTP/2.0から正式名称がHTTP/2に
    • 2015年にHPACKとRFC
    • 16年ぶり
    • バージョン3.1で終了
  • HTTP2が必要な理由
    • コンテンツ容量が増加、WEBの表示が遅い
    • ネットワーク帯域が遅い → 5G、10Gbps固定回線, 120Tbps海底ケーブル
    • TCPの無駄が多い → 輻輳ウィンドウを大きく、googleのBBR、UDPベースのQUIC
    • HTTP/1.1 の問題の解消
  • HTTP/1.1の問題
    • リクエストを多重で送れない、HTTPヘッダを何度も送っている
  • リクエストが直列
    • HTTP/1.1ではリクエストを待つ仕様になっている
    • Chromeなどでは6つまでTCPコネクションを張る / HTTP/1.1の仕様では2つまでだけど無視
  • HTTPパイプラインによる解決
    • 罠: リクエスト順に返す必要がある(最初のリクエストの処理が遅いと巻き添え)
    • べき等なリクエストでのみ利用可能 (GET, HEAD はO.K. PUT, DELETE もよいと言われているが・・・)
      • ブラウザがデフォルトでONにするのは難しい実情
  • その他 : リソースの結合(CSSスプライト)、インライン化(が、パケット30%増)、ドメインシャーディング(1つにつき6個なので)
  • HTTP/1.1 では HTTP Header は、職人が削るしかない
  • HTTP/2 の特徴
    • セマンティクスは互換性 (URLやポート、ヘッダなど)
    • ストリーム : 優先度付き全二重多重化通信、独立してブロックしない、ストリーム数は設定可能
    • ストリームはサーバからも初められる : ストリームIDはクライアントが奇数、サーバーは偶数
    • 送受信はバイナリ、フレーム単位で送受信
    • バイナリプロトコルになって、パースが厳格化
    • ライブラリがラップするので、フレームを意識することはほぼない
    • HPACK 形式で圧縮 ( gzip ではないのはCRIME攻撃手法を避けるため)
  • HPACKの特徴
    • 辞書更新、ハフマン符号
  • サーバプッシュ
    • サーバがHTMLと紐づくCSS、JSを知っていれば、一緒に送れる
    • 問題もあり、いろいろと改善中
  • はじめに、クライアントがHTTP/2を喋れるサーバなのか調べる
    • Connection: Upgrade, HTTP2-Settings Upgrade: h2c
    • 101 Switching Protocols が返ってくる(web socketと同じ)
    • そのままHTTP/2のレスポンスも返ってくる
  • HTTP/2でのhttps
    • TLS1.2以上が必須
    • ALPN
    • PRI メソッドを送る。HTTP/1.1サーバが壊れないようなリクエス
  • HTTP/2 まとめ
  • Perlでは?
    • http2-perlとProtocol::HTTP2 (後者のみメンテ)
    • Perlはほぼすべての機能が実装されている
  • Protocol::HTTP2
    • TCPコネクションの管理はやってくれない
    • LWPやFurlほど簡単ではない
  • Protocol::HTTP2::Client, Protocol::HTTP2::Server, PSGI
  • nghttpd -notls でサーバを立てておく
    • Protocol::HTTP2::Client ->new
    • ->request : で始まるようなHTTP/2のヘッダ、HTTP/1.1のヘッダ、on_done
    • AnyEvent でコネクションを張る。
    • ->next_frame リクエストのフレームを得られるので、コネクションへ push_write する
    • ->feed へ読み込んだ値を渡すと、 on_done が呼ばれる
  • ブロックできないので AnyEvent で書かなければならない
  • Protocol::HTTP2::Server も同様
    • ->newon_request で処理
  • クライアント側で request を複数呼ぶだけで並列になっている
  • TLSに対応する
    • ただし、強めの暗号化が必要
    • HTTP/2にしか対応していないサイトもスクレイピング可能
  • サーバプッシュも実行できる (省略)
  • shuvgeyPSGIのサーバができる
  • gRPCの話は略
  • まとめ
    • 最適化の議論は続いているので、これから使えるようになってくると期待できる
    • Perlでも使える(イベント駆動)
    • gRPCライブラリ出して欲しい

正規表現と曖昧性】マッチングの困難さについて / sinya8282さん

  • 正規言語の研究をずっとしている
    • 河野真治先生 (入門Perl著者)の弟子
    • Shibuya.pmで話した
  • (\D|<\d+>)*[!?] はバックトラック(VM)型の正規表現エンジンと相性が悪い
    • pcregrepだと 'a' x 30 に対して結果が戻ってこない ( egrep は大丈夫)
    • * の中に + があること : バックトラックの可能性が増える
  • (a?){22}a{22} もバックトラック型の正規表現エンジンと相性が悪い
    • 22 の壁、と呼んでない(けど、これがしきい値
    • マッチする文字列に重なりがある
  • どちらの正規表現も、曖昧であるのが問題
    • 曖昧がゆえに、どこでバックトラックするかが決まらない
    • 7章に書いてます
  • 曖昧な文脈自由文法
    • content-free grammar
    • A = {a, b}。 G = ({S}, {(S, ε), (S, aSa), (s, bSb)}, S) : 開始集合、書き換え規則
    • L(G) は {a, b} 上の偶数長の回文全体
  • 文法Gとw ∈ L(G)について、wの構文木を考える
  • 構文木を作ること == 構文解析
  • 統語的曖昧性 (ジャーゴン)
  • 構文木が複数ある場合もある
    • G = (v, R, S) について、すべての w について構文木が一個しかなければ、無曖昧という
    • 無曖昧でない文脈自由文法は、本質的に曖昧
  • Time flies like an arrow
  • if a then if b then s else s2 : elseはどちらにつくのか?
    • プログラミング言語であれば、曖昧ではないはず
    • 曖昧のない文法になっているか、文法ではないレベルで決まりを決めている
    • PEGs など、曖昧にならないクラスをつかうとか
  • {(s, ε), (s, ss), (s, >s>) } と {(s, ε), (s, >s>x) } は等価。後者は無曖昧
  • 正規表現をバックトラックしないように変換することはできる
    • 自由言語はそうでもない
  • Fz = ∑#(L∧An)zn 母関数 (#...は言語Lで長さnのものの個数)
    • 無限和だとコンピュータでは使えない
  • s = ε|>s>x は1 + zS(z)z(S(z)) = 1 + z2 S(z)2 は S(z) である、と記述できる
    • これを解いてテイラー展開すると、無限級数になる
    • 無曖昧な文法から、その言語の母関数を作れる!
  • 文法が、全部の文字列を生成できるか
    • 普遍性が決定可能、等価性が決定可能、などのクラスに分けられる
    • 曖昧、無曖昧が決定可能問題に関係してくる
  • 深い数学的対象、の一つに曖昧性があ る
  • Q). 欠点のあるバックトラックの実装を使うのはなぜ?
    • A). 魔改造しやすい。Perlのように。また、DFAよりまともに使える実装は楽
  • Q). 正規表現を無曖昧にするツールは

High (Availability|Performance) WebSocket for Perl Real-Time Application / macopyさん

  • 高可用性 : どこまで担保するか、を決めておくと、作りやすい
  • High performece : 10k ~ 100k
  • リアルタイム : サーバプッシュ技術
  • Aさんはどうやってサーバから相手のターンの終了を知るか?
  • ポーリング : 定期的に書くにする 10秒とか
  • サーバプッシュ : タイムラグなし
  • 現状、生のTCP/UDPで、各々が独自実装している
    • WEBの世界で生きるなら、WEB標準をつかいたい
    • しかし、HTTPはクライアントから通信が始まる
  • ロングポーリング、Server-Sent Events、WebSocket、HTTP/2 Server Push, WebRTC ・・・など
  • 同期型preforkモデルを使っている
    • 寿命が長いHTTPリクエストを処理するとプロセス枯渇で死ぬ
    • C10K : 並行プログラミングが必要
  • リアルタイム通信のために、アプリ全体のパラダイムを変えるべきなのだろうか
  • kuiperbelt
    • WebSocketとHTTP/1.1の相互変換プロキシ
    • Goで書かれている
  • 機能 : 接続認証、スケールアウト
  • 思想 : WebSocketのサーバはロードバランサーの下に入れる
    • 他の事例では直接インタネットに晒すことが多い
  • LBに入れる理由 : 管理コストを下げる
  • 思想 : 独自のデータ構造を作らない。HTTPをしゃべる
    • kuiperbelt ではクライアントからサーバへの通信はサポートしない(HTTPを使う)
  • X-Kuiperbelt-Endpoint X-Kuiperbelt-Session
    • Web appはkuiperbelt経由でpushする。HTTPでよい。
  • PerlでWebSocketを使うには、AnyEventとか必要
  • websocketの接続をもっているやつにHTTPで依頼するのがいい、という思いつき
    • 先人の知恵 APNS::Agent : HTTPでiOSのpush通知を送れる
    • 偉大: アーキテクチャごとに分けてデーモンで切り出す
    • 偉大: 土管に徹する
    • Gunfhsiというデーモンで、androidでもできるようにしている
  • 彗星(commet)の起源 エッジワース・カイパーベルト
  • 認証の方法
    • kuperbelt の実装が太る。クライアントに矯正したくない
    • WebAppに任せる。成功時は200レスポンスUpgrade。kuperbeltの実装をコンパクトにしてバグが入らないように
  • 宛先 : kuiperbeltはUUIDのジェネレータがない
    • WebAppにおまかせ
    • HTTPヘッダでやりとり
    • Bodyはappのものなのでさわらない
  • 土管に徹することで、言語は自由
    • Perlにこだわるとユーザ数が少なくなってしまう
  • 切断検知
    • 行儀が良ければ知れる
    • kuiperbeltがコールバックURLを呼ぶ
  • モバイルの世界では行儀が良くない
    • トンネルとか
    • 能動的な切断検知 : メッセージ送信失敗、無通信時間(pingフレーム)
  • WebSocketに必要な要素をステートレスHTTPに分解
    • WebAppでも扱える
    • 土管に徹すると任意のWebAppで利用可
  • 趣味プロダクトを宣伝して本番投入した
  • 複数台にする理由
    • 1台落ちると障害になる
    • 1台の上限は決まっている
  • KVSのシャーディングを参考
    • 自分は分散されていることを知らない
  • 設計のときにボトルネックを知っておくことが大事
  • ユースケースを想定したベンチマーク
  • メモリ使用量はクライアントに依存、CPUは秒間メッセージ数
  • fujiwaraさんによるチューニング
  • mackerel-plugin-kuiperbelt
  • プロダクションレディとなった
  • 産みの苦しみはあるが、概念を作れる
    • 小説の世界観設計に似ている

2018年春のPerl / karupanerura さん

  • charsbar さんの取り組みを支えるため、みんなで情報を集めた
    • charsbar さんにも手伝っていただいた
  • 5.26
    • インデント付きheredoc <<-@INC
    • @INC. がなくなったので、対応が必要
  • 5.26.1 、 5.24.3
    • 脆弱性の修正 (5.24 未満には公式パッチはないので注意)
  • CVE-20107-12837
  • CVE-20107-12883
    • 正規表現のパース時における、バッファオーバーリード
    • 5.22以上だと影響を受ける
  • CVE-20107-12814
  • これからのPerl5 (5.28)
    • 5~6月でしょう
  • 古い文法、特殊変数の削除
    • <<""
    • $/ = -1
    • :unique :locked
    • これらは、警告されてから、削除。こまめにアップデートしていれば気がつける
  • Unicode 10.0 support
    • Bitcoin symbolなどが追加されている
  • ${^SAFE_LOCALES}
    • ロケールがスレッドセーフに扱える環境かを表す
  • XS向けインタフェース
  • delete %hash{@keys}
  • 文字列結合の高速化
    • 5.26.1 より 4.85 倍に
  • 正規表現マッチの高速化
    • 5.26.1 と比べて 1.1倍
  • 正規表現文法の拡張
    • *positive_lookbehind と説明的に書けたり
  • スマートマッチの変更は?
    • 5.27.7で変わったが、5.27.8で差し戻された
    • 今回は無理そう
  • Rakudo-Star 2018.02
    • まだまだ活動が活発
  • Perl6をプロダクションで使い始めている人もいる
  • :atom: 系の演算子
    • 演算をatomicに行う
  • Perl6高速化計画の完了報告
  • Elizabeth氏からの暑いニュースレター
  • Perl6のモジュールが1,000件を超えた
    • Inline::Perl5 を使えばperl5の資源は使えるけど
  • Cro というWAF
    • Perl6の機能を活かすためのWAF
    • リアクティブな分散システム
    • HTTP2対応、ZeroMQ, Web Socket対応
    • 試したら 223.84 rps 程度だった
  • まとめ
    • Perl5もPerl6も進化している
    • アップデート差分が大きい。人気の低い機能の積極的な廃止?
  • Q). Perl5の文字列結合がなぜはやくなった?
    • A). とにかくはやくなった。非常におめでたい話
  • Q). Perl6高速化計画は?
    • A). 訳しきれていない部分もあるので、サイトを参照してください
  • Q). 5.28までは語呂合わせで 5/28 だったぽいけど?
    • A). 把握してません
  • Q). charsbarさんと同じように外国行ったりされますか?
    • A). 今のところはないですが、やっていきたい

Perl's work inside the company (Perl のお仕事)

Perlで実装されたLINE NEWSの裏側 / @nipotan さん

  • LINE NEWSに最初から携わっている
    • livedoor News と LINE NEWS でパイの奪い合い
    • livedoor News も LINE NEWS もちょっと前まで両方担当していた
    • 前者は中高年、後者はナウなヤングな女性スマートフォン
  • どちらもPerl
  • 2013年からサービス開始
    • ネイティブアプリ中心 MAU 300万
    • アプリをインストールしてもらうハードルの高さ
  • LINEニュース 公式アカウント
    • LINEが作っているbot
    • アクティブユーザを増やす施策
  • LINE NEWS DIGEST を開始
    • 文字情報だけから、画像も送るように進化
  • CMSでのデータから自動生成
    • 指示がわかりにくい
  • Perl、Imagerモジュール、フォント、画像
    • ご家庭のIPAフォントでも
    • 文字幅から位置を計算
    • グラデーション、影、文字を指示通り置いていく
    • 文字なども位置を決めて配置
  • 4年間、毎日3回配信
    • Imagerすごい
  • 2015 LINE上で読める Web App 1200万MAU
  • 2015 LINEアカウントメディア
  • 2016 タイムラインに掲載
  • 2017 5番目のタブ 5900万MAU
  • LINEニュースが重い
    • DIGEST配信による同時アクセスにより、重くなる
    • ささやかな未読バッヂも
    • 地震情報によるスパイク
    • 描画まで3秒
  • 世の中AMP, Instant Articles, Smart View
  • 0.5秒化計画
    • SPAをやめ、HTML表示
    • 読まない後続する記事を生成しない(調べたら後続の記事は読まれてなかった)
    • ローカルキャッシュ
  • 通信を速くする
    • CDNでのキャッシュを利用
    • HTTP2が自動的に利用されたり
  • PSGIの高速化
    • PSGIによるキャッシュ
    • クエリ結果のキャッシュ
    • 行単位のキャッシュ
  • 2016年にミッションを完了した
    • 日経電子版とは違い、全く話題にならずに実現した
  • LINE NEWS の未来予測
    • エンジニア採用は難しい
    • JavaエンジニアにJavaで実装してもらうことが多い
    • Perlを死守する理由はないでしょう
    • Java : Perl は 6 : 4
  • LINE NEWSではPerlエンジニアは積極的にはしてないが、他のサービスでは使っているので
  • 沖縄からまたPerlの火をともしましょう

Perl in Mercari 2018 / @kazeburo さん

  • 重要なお知らせ memcached UDP リフレクション
    • DDoS攻撃ができる
    • さくらで 11211/UDP をフィルタリング
    • memcached をグローバルに後悔しない
    • UDP無効化したほういいのでは (デフォルト無効に変わってる)
  • メルカリとは?
    • 国内最大級のフリマ。
    • 簡単に出品 : 機械学習で自動で商品名が埋まったり
    • 安心安全な決済
    • 1億DL、100万出品、月間100億取引
  • プログラミング言語はPHP7
  • 小さなPerlスクリプトが根幹にいる
  • slacklog (OSSでもなんでもない)
    • cronlog + slack notification
    • cronlog : 終了コード0じゃないときだけSTDOUTへ
    • 「監視とは継続的なテスト」
    • slack通知できるようにしたもの
    • 起動と終了のログ (slack通知、syslogへ)
    • hostnameとpidの付加
    • メルカリのほぼすべてのcronで利用
  • slackboard
    • メッセージを集めて、一元管理
    • リージョンごとに分けて送信
    • 大量のメッセージが来た場合など
  • ログを /dev/null に投げない
  • トイルの撲滅 「あのバッチ動いてますか?」
  • 商品閲覧履歴サービス
    • 数千req/sec
    • consul, nginx, q4m, ワーカがDB更新とmemcached
    • Gazelle 、 XS moduleによる速度向上
  • workerはスケールアウトが可能
  • ボトルネックMySQLレプリケーション遅延
    • Multi Thread Slave 18,000
    • 念のためSharding は設計に組み込み済
    • golangが3ms、Perlが4ms
  • なぜPerl
    • System Call friendly
    • coreモジュールで作ればDeployが容易
    • Perlで培われた技術の継承
  • コンテナからPerlが消えていく
    • SRE研修でPerlを触ってもらう
    • UNIX哲学に触れて、技術を底上げ

Perl's work inside factory / @n0442 さん

  • 15人中Perlを書く人は9人
  • 事例からPerlについて見ていく
  • NTT docomoさん Inter bee 新しいライブの形
    • Shibuyaのスタジオから配信
    • 観客はスマホを持って見る
    • アートワークをクライアントから受けている部分がAWS
    • Pocket.IOでリアルタイム
  • WEBキャンペーン企画
    • 期間限定のコンテンツ
    • 人をあつめるのが目的
    • スパイクが起きるのでLambdaとS3で分散させて対処
    • AWSを使って、おいしいところだけPerlで書く
  • PUMAさん ファッションシミュレーション
    • Image::Imlib2、Imager、Image::Magick
  • ユニフォームシミュレーションの方法
    • 写真か、CGベースか
    • 写真で。素材感が伝えやすい。工数が低い
    • 同じ角度で写真を取って
    • ベタ塗りのレイヤと質感のレイヤをレタッチでわける
    • パーツをPerlで合成する(Image::magick)
  • オムニチャンネルのECサイト
    • 2着目以降はスマホだけでオーダー可能にする
    • 2週間という短納期
    • たくさんのPerlモジュール : AnyEvent, Data::Validator, FormValidator::Lite, HTTP::Session, Net::APNs::Extended などなど
  • なぜPerlか?
    • よく知っているから
    • 不得意な部分ではHUBとしてつかう
  • 今後もPerlは活躍していけるか?
    • まだまだPerlを使って魅力的なものづくりができる
    • Perlを使って何ができるか?
    • Perlを書きたい人を募集
    1. Perlで大変な点は
      1. 大人数だと開発がきつい

フリークアウト社の事業を支えるPerl / 本間雅洋

http://hiratara.hatenadiary.jp/entry/2018/03/03/174252

Keynote / yappoさん

  • AKBの握手会の日だった・・・
  • 受託、メール広告、モバイルポータル、EC、フォトサービス、LINE
  • LINEと言えば ikebe さんのキーノート
  • Perlのコミュニティにどう関わってきたかの話をする
  • 学研のBASICの本
    • ファミコン買ってくれなかった。パソコンも
    • こどものしろの1Fでコード書いてた
    • 作文用紙にコードを書いてた。消しゴムでeditができる
    • 実行環境は頭の中で
  • 高校で SHARP PC-G815
    • マシン語のほうが高速なことに気がついてアセンブラ書いてた
    • バンドとかチャラいことをやり始めた → シンセ
    • 全自動でmidiの操作をしたくなった
    • Macintosh Collor Classic II を作曲に使う
    • パソコン通信 / Internet
      • 雑誌とかにはサンプルプログラムついてた
      • 定額ではない。自分でパソkん通信は厳しい時代
      • パソコン通信のマクロとApple scriptでホスト曲を作った
    • プロバイダの契約は敷居が高い
    • 1997年にYappoというサービスを作った。iYappo
      • ユーザ層が若くなり、インタネットを知らない人も使い始めた
      • サポートをしていたときに、Yappoという名前で回答していた
  • 規格、開発、OSS開発元へのfeedback、プレスリリース(広報/マーケティング)
    • 一人だと意思決定が速い
    • サービスがスケールすると一人では無理・・・だが
    • 声は受けたが、当時は断った
    • リリース、企画持ち込み、代理店契約周り
  • 転職感
    • 鹿児島の人から声を受けて会社をはじめた。リモートワーク
    • インタネットのメールを送受信できるケータイ
      • 生年月日を送るとバイオリズムを返すメールサービスを作ってたら、声をかけられた
    • 声をかけられた事が多い。livedoorさんだけ、転職活動をした
  • miyagawaさんから喋らないかオファーを受けたのがきっかけ
    • miyagawaさんからの架け橋
    • チャンスと思って、話すことを初めた
  • オライリーじゃないラクダ本
    • 1998年くらい
    • 近藤さんのサインの10年後にLarry Wallのサインをもらった
  • 参加したPerlイベント
    • Shibuya, Fukuoka, Kyoto, Hokkaido, Kansai, Yokohama, Kamakura, Kokusaitenjijomae, Okinawa
    • OSDC Taiwan (Shibuya.pm枠)
  • IRC文化
    • 色んな会社の人々がいるので
    • いいチームワークが生まれた
    • miyagawaさんが転職したという理由で一晩でfreenodeへ
    • miyagawaさんが声をかけて一晩でUTF-8
  • coderepos
    • gitがなかった時代。パッチのためにリポジトリをクローンするしかなかった
    • 個人のリポジトリを一つのレポジトリにまとめた
  • HTTP::Engine
    • Catalyst Engineがよくできていたので再利用
    • このあとWSGIを元にtokuhiromさんとmiyagawaさんがPSGIを策定
  • perl-users.jp
    • JPerl Advent Calendar 2日くらいで開始
    • 指名制度。最後まで書いたら飲みに行く
    • 日本のJPerl Advent Calendarの走り
  • Perl Hackers HUB 49記事、作者が50人
  • LINEにもYAPC関係者が多い
  • 開発チームマネージャー
    • 少人数では仕事がスケールしない
    • もっと上のレイヤで仕事をしていたらそうなった
  • NEED, SPEED, DETAIL, ENJOY, TEAMWORK
  • 一人では何も成し遂げられない
    • いいなと思う人に橋をつなげて、より面白い仕事をしましょう
  • Shibuya.pmの次のリーダーをやりたい人は言ってね

キーノートスポンサーセッション

思いは言葉に / papixさん

  • はてなブログのトップページの言葉
  • 思いをみんな持っている : 「ライブラリ最高」「バグ最悪」「資料楽しい」
  • 「自分の書いたものなんて誰にも読まれない」
    • 自分のために書くもの
    • 言葉にして自分で整理する
    • 絶対需要ないとおもったものが反響あったりする
  • 思いが綴られた言葉を読みたい!
    • ブログを今すぐ書いてくれ! (登録サイトがhttps対応!)
    • 仲間を募集中! (こっちはHTTP・・・?)
  • yappoさんありがとうございました

Lightning Talks

PPRとKeyword::Simplと言えば後Variable::Declaration / @kfly8 さん

  • PPR 静的解析モジュール
    • 正規表現による解析が可能
    • キャプチャとかも直感的
  • Keyword::Simple
    • PurePerlでPerlにキーワード追加
    • PL_keyword_plugin (Devel::Declareが前進)
    • defined 関数で
  • PPR と Keyword::Simple *Pure Perlperlを静的解析して拡張
  • Variable::Declaration
    • ほか、 Type::Tie, Data::Lock
    • let Int $foo = 2018 などと指定すると、型制約チェックを入れられる
    • buggy、静的型付け言語を入れればと言われる→「いいじゃないですか」
  • ゴーン! (時間切れ)

・・・ / 芹川葵さん

Windows 10がブルースクリーンになったのでメモなしです・・・。

ActivityPub について / @argrath さん

  • 分散ソーシャルネットワークネットワーキングのためのプロトコル
  • サーバ間、サーバクライアント間は HTTP + JSON
  • 広くて浅い作りになっている
    • 実装レポートを参考
    • mastodon 完全な実装ではない
    • Kroeg .NET 大変
    • pylodon クラウドサービスが必要
  • actub : Perl/Mojoliciousとactub
    • 色々未実装
  • デモ
  • これから作っていきたいのでよろしく

Perl入学式に参加した結果、その後 / 城後さん

  • 大学編入後に新しい言語を
  • AnaTofuZさんから彼女ができると言われて
  • 入学式を感想した
    • AnaTofuZさん彼女いなかった
  • すぐわかるオブジェクト指向perlの読み会
  • 今年からサポーターやります

Acme大全が電子書籍化されて1年が過ぎました / makamakaさん

  • Acme::Sundial
    • 日の出、日の入
    • 日の出後は何も出ない
  • use oist
    • -Moist しっとり
  • 664個のAcmeモジュール
    • 重くて買ってもらえない → 電子書籍
    • donzoko.booth.pm
      • 5名
  • Acme大全が10年目
    • 全巻収納ボックスにチャレンジします
  • ゴーン!(時間切れ)

クロージング

  • でーじ上等!!! (すごくよかった)
  • スタッフ撮影 「はやくはやくはやく」「まんなかまんなか」「まえまえまえ」
  • ベストトーク
    • Supershipさん(エンジニア募集中です!)からappleTV、airpodsgoogle home
    • kazuhoさん
    • 「アメリカの同僚が書いた資料だったが、架け橋、なので」
  • ベストLT
    • colsisさん kindle oasis
    • 芹川さん
    • 「一ヶ月前まではRuby書いてましたから」
  • 35のスポンサー
  • 3人のゲスト、15 + 4のトーク、50の個人スポンサー、150人出席
  • Next...
  • 懇親会に移動しましょう

Haskell入門のサンプルコードの最新LTS対応

拙著 Haskell入門 のサンプルコードは lts-8.24 にしか対応していなかったので、現時点の最新である lts-10.6 に対応させた。変更したのはすべてライブラリがアップデートされたのを追随したもの。 master ではなく lts-10 というブランチにしてあるので、注意。

github.com

LTSのアップデートによって ghc-8.2 が使えるようになったのが嬉しい。引き続きHaskellでの開発をお楽しみくださいませ。

Re: 凸頂点の数 横へな2016.4.2 問題

凸頂点の数 。30分。

今回は共通化したいところをグッとこらえてコピペ。前回まで初めから抽象化にこだわって問題を解いていたが、具象例が集まってからのほうが良い抽象化ができることから、60分という時間を考えると抽象化はし過ぎないほうが良い選択だということだろう。

この問題の場合、各頂点に2つの図形が隣接するが、そのうちいずれかと隣接してしまうと凸頂点ではなくなる。 00 でも同じ性質を満たしており、ほぼ同じ関数が使いまわせたのは面白いと感じた。

package Nofconv;
use strict;
use warnings;
use Exporter qw(import);
use List::Util qw(sum);

our @EXPORT_OK = qw(solve);

my @chars = ('a' .. 't');
my %chars = map { $chars[$_] => $_ } 0 .. $#chars;

sub _next ($) {
    my $c = shift;
    my $next = ($chars{$c} + 1) % @chars;
    $chars[$next];
}

sub _prev ($) {
    my $c = shift;
    my $prev = ($chars{$c} + @chars - 1) % @chars;
    $chars[$prev];
}

sub round1 ($) {
    my $c = shift;
    (
        '00',
        '00',
        '1' . _prev($c),
        '2' . _prev($c),
        "2$c",
        '1' . _next($c),
    );
}

sub count1 ($$) {
    my ($c, $ps) = @_;
    my @round = round1 $c;
    my $result = 6;
    for (0 .. $#round - 1) {
        $result-- if $ps->{$round[$_]} || $ps->{$round[$_ + 1]};
    }
    $result-- if $ps->{$round[$#round]} || $ps->{$round[0]};

    $result;
}

sub round2 ($) {
    my $c = shift;
    (
        '1' . _next($c),
        "1$c",
        '2' . _prev($c),
        '3' . _prev($c),
        "3$c",
        '2' . _next($c),
    );
}

sub count2 ($$) {
    my ($c, $ps) = @_;
    my @round = round2 $c;
    my $result = 6;
    for (0 .. $#round - 1) {
        $result-- if $ps->{$round[$_]} || $ps->{$round[$_ + 1]};
    }
    $result-- if $ps->{$round[$#round]} || $ps->{$round[0]};

    $result;
}

sub round3 ($) {
    my $c = shift;
    (
        '2' . _next($c),
        "2$c",
        '3' . _prev($c),
        '4' . _prev($c),
        "4$c",
        '3' . _next($c),
    );
}

sub count3 ($$) {
    my ($c, $ps) = @_;
    my @round = round3 $c;
    my $result = 6;
    for (0 .. $#round - 1) {
        $result-- if $ps->{$round[$_]} || $ps->{$round[$_ + 1]};
    }
    $result-- if $ps->{$round[$#round]} || $ps->{$round[0]};

    $result;
}

sub round4 ($) {
    my $c = shift;
    (
        '3' . _next($c),
        "3$c",
        '4' . _prev($c),
        '5' . _prev($c),
        "5$c",
        '4' . _next($c),
    );
}

sub count4 ($$) {
    my ($c, $ps) = @_;
    my @round = round4 $c;
    my $result = 6;
    for (0 .. $#round - 1) {
        $result-- if $ps->{$round[$_]} || $ps->{$round[$_ + 1]};
    }
    $result-- if $ps->{$round[$#round]} || $ps->{$round[0]};

    $result;
}

sub round0 ($) {
    my $c = shift;
    map { "1$_" } 'a' .. 't';
}

sub count0 ($$) {
    my ($c, $ps) = @_;
    my @round = round0 $c;
    my $result = 20;
    for (0 .. $#round - 1) {
        $result-- if $ps->{$round[$_]} || $ps->{$round[$_ + 1]};
    }
    $result-- if $ps->{$round[$#round]} || $ps->{$round[0]};

    $result;
}

sub count ($$) {
    no strict 'refs';
    my ($name, $ps) = @_;
    $name =~ /^(\d)(\w)/;
    &{"count$1"}($2, $ps);
   
}

sub solve ($) {
    my %ps;
    while ($_[0] =~ /(..)/g) {
        $ps{$1}++;
    }
    return sum map { count($_, \%ps) } keys %ps;
}

1;
use strict;
use warnings;
use Nofconv qw(solve);
use Test::More import => [qw(is done_testing)];

while (<DATA>) {
    tr/\r\n//d;
    my ($num, $in, $out) = split /\s+/, $_;
    is solve($in), $out, "Test #$num";
}

done_testing;

__END__
0  1a2t3s2s    11  リンク
1  1a1c1d00    22  リンク
2  00  20  リンク
3  3q  6   リンク
4  3t2a    8   リンク
5  3t3a    8   リンク
6  3t4a    12  リンク
7  004q2g  32  リンク
8  4c2g2k4i    24  リンク
9  1o1a4f4i1t  26  リンク
10 4t3a4g2a2o2p    24  リンク
11 4i4o3i3c3n3h2c  30  リンク
12 4m3n3m002b1b3a  34  リンク
13 001b2a3t4s3s2s1s    27  リンク
14 1n1j3o4o1h2n2r1k    36  リンク
15 4o2a2j1m2e4l2l3m3o  42  リンク
16 1j2p1a4r4b1i3h4e3i2i    42  リンク
17 001a1c1e1g1i1k1m1o1q1s  30  リンク
18 3n2j3e3a2n1f1p2q3t4t3h  53  リンク
19 4a4b4c3d2e1e1d1c3a2a1b  22  リンク
20 3n3e3t4i3m2d2g3i1j4o2i4t    52  リンク
21 3t2m4n2l4g4h1a1n2t4m2h3a1m  44  リンク
22 1p1i2n3q1d2o2c1q3m3f003k3l2s    53  リンク
23 3r1q3p2d4k4n1r3o4l2j2c1a4o3q4f  56  リンク
24 4d2f4r3f2p4t1j1p4g1q4f1k2j2s4i4j    62  リンク
25 002c2d1f2f3e4d4c4b4a3a1b2t1t2a2g1h  40  リンク
26 3r2i2j3d2t3j3g2s1p2o2p1n1m2d1k1r3i  59  リンク
27 4o4s1i4p3p3s3b4n3r1k4a3t4g3n1o2m3i2o    66  リンク
28 3k2j1i2p2n3l2f2s3f1n4s2h3s1l1m4n4k4q2k  65  リンク
29 1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1r1s1t    40  リンク
30 4n3f1c1a3o2s1h2p2k3g3n2e4s2t1j1b3h2a1n3k    73  リンク
31 1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1r1s1t00  20  リンク
32 2a2b2c2d2e2f2g2h2i2j2k2l2m2n2o2p2q2r2s2t00  60  リンク
33 2m1p2c2o2n4n002s1m3i2t4l2b3r2h1j4q1c4t1s1a  65  リンク
34 4m2t4r4h3b4b2e3g3n2i4e3m1q4i2q2b2m3i2a1b2s1h    77  リンク
35 1c004g1k4o3p3l3h1r4d2t2c2d1n4t2e1s1j2p1d4j1h1f  74  リンク
36 1s3j4a4h3h3q3n3b3f2m3o3c4i3r2r1f1c2p4s1e3a2j2o3e    80  リンク
37 2c1b3b3k2f1e4q1d1m4n3t4b4s3h3d3g3n1f4p4j2e4f4c3e1k  78  リンク
38 2p4k3t1h4e1n3g4p2j1a1k1p4f1o3a4s4q4i3p2t4l3k2k3s2r4h    77  リンク
39 1d1p4o3n3m4d1m2f3c1o3f3g3a2o1f4n2c4e2j4p4f1b1j1i1k1h2m  74  リンク
40 3m4d4e1i3t4f1f3n2p2g1q4g2c2m1s2r2i3f3o1h3g2e1o3a4r3h3r4o    75  リンク

後、細かい点だけど、 キーバインドを整いきれてないIDE (Cloud9) を使うのであれば、マウスをどんどん使ったほうがいいという点にも気が付き始めた。

Re: ぴったり含む長方形 横へな2016.3.5 問題

ぴったり含む長方形 。点1個から帰納的に最小の長方形を出そうと方針を誤り、敗北。

方針変換後は45分。これはさらに15分かけて足切りを入れたもの。そもそも点を累積して数えるようにすればもっと速い。

package Pire;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw(solve);

my @chars = ('0' .. '9', 'A' .. 'Z', 'a' .. 'z');
my %chars = do {
    my $i = 0;
    map { $_ => $i++ } @chars;
};

sub eq_point ($$) {
    my ($p1, $p2) = @_;
    $p1->[0] == $p2->[0] and $p1->[1] == $p2->[1];
}

sub uniq {
    my %done;
    grep { ! $done{$_}++ } @_;
}

sub area ($$$$) {
    my ($x1, $x2, $y1, $y2) = @_;
    (abs($x1 - $x2) + 1) * (abs($y1 - $y2) + 1);
}

sub contains ($$$$$) {
    my ($points, $x1, $x2, $y1, $y2) = @_;

    my @contains = grep { $x1 <= $_->[0] and $_->[0] <= $x2 and $y1 <= $_->[1] and $_->[1] <= $y2  } @$points;
    scalar @contains;
}

sub solve ($) {
    my ($point_num, $encoded_points) = split /:/, $_[0], 2;
    my @points = map { [map { $chars{$_} } split //, $_, 2] } split /,/, $encoded_points;

    my @xs = sort {$a <=> $b} uniq (0, 61, map { $_->[0] - 1, $_->[0], $_->[0] + 1 } @points);
    my @ys = sort {$a <=> $b} uniq (0, 61, map { $_->[1] - 1, $_->[1], $_->[1] + 1 } @points);

    my $min_area = 63 * 63;
    my $max_area = 0;
    my %done;
    for my $x1 (0 .. $#xs) {
            for my $y1 (0 .. $#ys) {
      GROWUPX: for my $x2 ($x1 .. $#xs) {
                for my $y2 ($y1 .. $#ys) {
                    next if $xs[$x1] < 0 or $xs[$x1]  > 61;
                    next if $xs[$x2] < 0 or $xs[$x2] > 61;
                    next if $ys[$y1] < 0 or $ys[$y1] > 61;
                    next if $ys[$y2] < 0 or $ys[$y2] > 61;
                    next if $done{join ',', $xs[$x1], $xs[$x2], $ys[$y1], $ys[$y2]}++;
                    my $contains = contains(\@points, $xs[$x1], $xs[$x2], $ys[$y1], $ys[$y2]);
                    next if $contains < $point_num;
                    next GROWUPX if $contains > $point_num;
                    my $area = area($xs[$x1], $xs[$x2], $ys[$y1], $ys[$y2]);
                    $min_area = $area if $area < $min_area;
                    $max_area = $area if $area > $max_area;
                }
            }   
        }
    }

    $min_area <= $max_area ? "$min_area,$max_area" : '-';
}

1;
use strict;
use warnings;
use Test::More import => [qw(is done_testing)];
use Pire qw(solve);

while (<DATA>) {
    tr/\r\n//d;
    my ($num, $in, $out) = split /\s+/, $_;
    is solve($in), $out, "Test #$num";
}


done_testing;
__END__
0  3:Oh,Be,AF,in,eG,ir,l5,Q8,mC,7T,Ty,tT   108,1920    リンク
1  3:00,zz,0z,z0   -   リンク
2  1:ho    1,3844  リンク
3  2:am    -   リンク
4  4:00,zz,0z,z0   3844,3844   リンク
5  4:00,11,zz,yy,1y,y1 3600,3721   リンク
6  2:00,01,10,11,yz,zy,yy,zz   2,3660  リンク
7  2:AA,AB,BA,BB,SZ,SY,TZ,TY   2,2046  リンク
8  3:Lw,qw,cw,Ow,2w,fw,yw,ow,Zw,iw 7,2170  リンク
9  4:0o,0s,0x,0C,0L,0k,0V,0y,0m,0S 9,2852  リンク
10 5:Tz,gz,Ez,Pz,3z,Jz,iz,Bz,ez,9z 17,2604 リンク
11 6:mQ,mp,mv,mW,mo,mZ,mC,mc,mt,mH 23,3100 リンク
12 5:a0,a4,aa,ac,ag,aB,aG,aK,aO,aY,az  19,2480 リンク
13 10:Ep,Jx,Qh,M7,Zr,lk,yp,Ya,aW,J3,OJ 1938,3720   リンク
14 5:00,01,10,0y,0z,1z,y0,z0,z1,yz,zz,zy   3721,3721   リンク
15 7:00,01,10,0y,0z,1z,y0,z0,z1,yz,zz,zy   -   リンク
16 8:00,01,10,0y,0z,1z,y0,z0,z1,yz,zz,zy,TE    -   リンク
17 11:bU,cl,d5,Fn,NW,gA,2O,YU,H4,0y,qE,Hd,ZH   1748,3658   リンク
18 12:SM,cf,AD,6P,cm,mo,if,s0,Mt,GJ,9m,Sp,lA,x4,NE 1806,3186   リンク
19 2:00,I0,c0,z0,0I,II,cI,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   19,2520 リンク
20 3:00,I0,c0,z0,0I,II,cI,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   39,2562 リンク
21 5:00,I0,c0,z0,0I,II,cI,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   -   リンク
22 6:00,I0,c0,z0,0I,II,cI,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   741,3660    リンク
23 7:00,I0,c0,z0,0I,II,cI,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   -   リンク
24 7:00,I0,c0,z0,0I,II,cJ,zI,0c,Ic,cc,zc,0z,Iz,cz,zz   1178,2623   リンク
25 13:Lk,y3,uO,Gk,sF,7y,ED,FP,rK,vw,Lo,kT,ib,MR,sC,Cu,xQ   1794,3472   リンク
26 1:lz,en,81,M2,M1,8p,7m,ND,m0,gd,3v,hr,hA,Yr,XB,x2,AR,5Q,3V,B3   1,1785  リンク
27 2:s1,Dx,yu,H8,c4,6C,95,FK,xV,Q7,h2,Wk,BI,i0,bl,9Q,KF,1q,52,PS,Jh    6,1824  リンク
28 2:0R,20,fz,0a,0X,zp,P0,0b,0S,zw,e0,0G,g0,06,a0,Zz,zW,zn,00,z6,zq,U0 2,3660  リンク
29 3:H5,jF,AT,HH,k7,Ho,Mz,07,Tt,Sq,Zh,Yt,e5,oS,qf,YY,JD,bP,s4,hB,TC,PW 28,1224 リンク
30 4:oe,pg,Np,zP,ho,pe,OV,S0,oM,wO,pM,Ah,Vq,9d,6U,3I,C9,AR,1L,rg,69,as,Nx  12,1989 リンク
31 2:n0,V0,zL,i0,4z,Nz,xz,z0,z1,0f,P0,zw,80,zC,zB,Az,0P,50,k0,rz,0D,jz,qz,E0   2,2928  リンク
32 2:tz,0F,0y,zo,0K,01,qz,zU,gz,Xz,zc,0m,zD,Q0,Yz,zb,0a,zp,zW,z7,0o,h0,1z,0p   2,3660  リンク
33 5:8r,NI,gL,3z,EK,hy,L9,g2,Kh,Gw,Dg,ZB,Sg,LY,ig,sS,I8,U0,DI,cq,Bu,qJ,C4,jP   143,1520    リンク
34 2:7s,z7,so,zw,X2,59,r1,0Q,70,q2,C6,J6,wz,at,2w,Vq,f9,st,sI,rf,wG,zg,f3,L2,4j    4,2340  リンク
35 2:kw,Gz,zp,se,8e,2S,C7,1A,B9,5v,AM,sN,zH,m8,Cx,rG,4w,q2,W0,ta,AC,G5,y0,Vq,3i    4,2080  リンク
36 3:Lr,pX,y7,2Y,qI,6w,t5,R6,e8,57,5f,R1,Up,9q,33,1Z,05,Eu,6S,AW,au,7S,zd,CA,R7    7,2120  リンク
37 3:ut,0C,6p,5w,9M,uj,I6,sq,Yr,Tz,Bp,p7,Rt,JB,6O,Bw,T1,tY,pn,MA,7I,GC,BO,m0,ts    12,2016 リンク
38 4:sE,Wy,Oo,uY,4t,os,B1,xq,4j,ex,s7,y7,54,ud,Cj,0L,S7,fx,11,cs,zc,tn,S6,Zq,2r    56,2400 リンク
39 6:eZ,5V,xT,2x,W7,vy,yT,8R,XT,4c,yX,s9,3E,KZ,tp,Sj,Su,wp,4F,BV,aC,qw,cJ,Gl,aA    192,1849    リンク
40 7:6Q,av,UZ,0c,IV,fo,Vv,mg,no,qM,06,zy,jW,R0,Qo,sK,wQ,1b,De,Iy,zI,cx,rn,ot,cN,45 250,2303    リンク
41 4:0A,15,5k,Bi,mz,0f,vr,EZ,4z,vj,6p,vP,8X,16,x7,S3,2z,zJ,wI,wY,Wv,ky,9K,8u,Eo,4s,y0  48,2700 リンク
42 8:zN,2J,ta,HL,Dg,up,Qn,W8,8K,k4,Is,uL,dT,tA,PN,UQ,DB,gA,OO,lv,4h,Rv,D6,23,Tg,4S,Zb  418,1763    リンク
43 5:px,sp,cr,dB,fz,65,gq,zb,sN,42,o0,y3,iE,pv,sn,Al,RE,48,l0,7X,DE,xL,wC,qQ,w5,C3,P3,i1   102,2397    リンク
44 9:Ic,Dk,Ef,6R,GK,NZ,76,L0,oQ,9f,S3,oL,lX,7v,8d,pX,dZ,z7,zx,fR,pe,w7,aj,U9,lO,kv,wL,s0   396,2088    リンク
45 10:JJ,LR,Xe,kg,LU,lI,3w,ZV,Td,Mu,tA,g8,VC,I7,N8,zN,kY,Ux,3t,mg,4m,FO,Ug,vQ,qY,jl,Ne,Zq,GN   416,1794    リンク
46 11:lQ,EN,vO,tn,qO,F3,9k,K2,UC,P0,XY,DB,QO,ps,hy,fl,Dt,ex,Vc,vF,Pf,Vk,uo,Xc,Sh,KE,9g,3H,l6   658,1995    リンク

Cloud9 を Windows 上から使っているのだが、 emacs キーバインドが使えない のが死ぬほど辛い。全身を鎖で巻かれて海中に放り込まれた状態で解いているような感覚。コードフォーマッタもPerlは用意されておらず、インデントを直す気力はない。プログラムなんて考えるのがほとんどでしょ、って思ったこともあったが、エディタがここまで作業に支障を与えるとは。

Re: まわせ! Bouwkamp!! 横へな2016.2.6 問題

まわせ! Bouwkamp!! に挑戦したが、惜敗。90分かかった。

正攻法で挑むと楽しい良問ではあったが、意外に手数が多い。

package Bouwkamp;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw(solve);

sub decode_bouwkamp ($) {
    my @result;
    while ($_[0] =~ /\((\d+(?:,\d+)*)\)/g) {
        push @result, [split /,/, $1, -1];
    }
    \@result;
}

sub sort_points (@) {
    sort { $a->[1] <=> $b->[1] or $a->[0] <=> $b->[0] } @_;
}

sub rotate_point ($$$) {
    my ($w, $h, $p) = @_;
    [$w - $p->[1] - $p->[2], $p->[0], $p->[2]];
}

sub parse_bouwkamp ($) {
    my $bouwkamp = $_[0];
    my @results;
    my ($w, $h) = (0,0);
    my @next_points = ([0, 0]);

    for my $edges (@$bouwkamp) {
        @next_points = sort_points @next_points;
        my $cur_point = shift @next_points;
        for my $edge (@$edges) {
            push @results, [$cur_point->[0], $cur_point->[1], $edge];
            # ここで、 @next_points から上辺がかぶる点を消す
            @next_points = grep { ! ($_->[1] == $cur_point->[1] and $cur_point->[0] <= $_->[0] and $_->[0] <= $cur_point->[0] + $edge) } @next_points;

            push @next_points, [$cur_point->[0], $cur_point->[1] + $edge];
            $cur_point = [$cur_point->[0] + $edge, $cur_point->[1]];

            $w = $cur_point->[0] if $cur_point->[0] > $w;
            $h = $cur_point->[1] + $edge if $cur_point->[1] + $edge > $h;
        }
    }
    
    (\@results, $w, $h);
}

sub deparse_bouwkamp ($) {
    my @points = @{$_[0]};

    my @results;
    while (@points) {
        my $point = shift @points;
        my @cur_group = $point->[2];
        my $next_point = [$point->[0] + $point->[2], $point->[1]];
        while (my ($x) = grep { $next_point->[0] == $_->[0] and $next_point->[1] == $_->[1] } @points) {
            push @cur_group, $x->[2];
            @points = grep { $next_point->[0] != $_->[0] or $next_point->[1] != $_->[1] } @points;
            $next_point = [$next_point->[0] + $x->[2], $next_point->[1]];
        }
        push @results, \@cur_group;
    }
    
    \@results;
}

sub solve ($) {
    my ($answer_num, $encoded_bouwkamp) = split /:/, $_[0], 2;
    my $bouwkamps = decode_bouwkamp $encoded_bouwkamp;
    my ($points, $w, $h) = parse_bouwkamp $bouwkamps;
    my @new_points = map { rotate_point $w, $h, $_ } @$points;
    @new_points = sort_points @new_points;
    my $new_bouwkamp = deparse_bouwkamp \@new_points;
    '(' . join(',', @{$new_bouwkamp->[$answer_num - 1] // []}) . ')';
}

1;
__END__
use strict;
use warnings;
use Bouwkamp qw(solve);
use Test::More import => [qw(is is_deeply done_testing)];

while (<DATA>) {
    tr/\r\n//d;
    my ($no, $input, $output, undef) = split /\s+/, $_;
    is solve $input, $output, "Test #$no";
}

is_deeply Bouwkamp::decode_bouwkamp('(33,29,50)(4,25)(37)(15,35)(16,9)(7,2)(17)(42,18)(6,11)(8,27)(24)(19)'),
          [[33,29,50], [4,25], [37], [15,35], [16,9], [7,2], [17], [42,18], [6,11], [8,27], [24], [19]];

is_deeply [Bouwkamp::sort_points([3, 4, undef], [2, 3, undef])],
          [[2, 3, undef], [3, 4, undef]];

is_deeply [Bouwkamp::parse_bouwkamp [[2, 2, 2], [3, 3]]],
          [[[0, 0, 2], [2, 0, 2], [4, 0, 2], [0, 2, 3], [3, 2, 3]], 6, 5];

is_deeply Bouwkamp::deparse_bouwkamp [[0, 0, 2], [2, 0, 2], [4, 0, 2], [0, 2, 3], [3, 2, 3]],
          [[2, 2, 2], [3, 3]];

is_deeply Bouwkamp::rotate_point(6, 5, [0, 0, 3]),
          [3, 0, 3];

done_testing;
__END__
0  4:(55,44,48)(40,4)(52)(26,29)(23,3)(20,31,21)(5,47)(43)(9,17)(1,8)(32)(25)  (32,31) リンク
1  6:(33,29,50)(4,25)(37)(15,35)(16,9)(7,2)(17)(42,18)(6,11)(8,27)(24)(19) (6,17,2)    リンク
2  7:(60,50)(23,27)(24,22,14)(7,16)(8,6)(12,15)(13)(2,28)(26)(4,21,3)(18)(17)  (4,16)  リンク
3  6:(99,73,56)(17,39)(68,22)(36,25)(57,42)(9,16)(2,7)(10,28)(23)(15,87,18)(72)(69)    (10,36,22)  リンク
4  7:(79,49,66,63)(32,17)(3,60)(29,57)(55,22,2)(20,14)(15,28)(33,9)(24)(11,134)(123)   (29,17) リンク
5  7:(159,129)(57,72)(56,39,25,16,23)(9,7)(2,28)(36)(42,15)(17,22)(87)(10,18)(73)(68)(60)  (10,28) リンク
6  14:(113,71,68)(32,36)(42,29)(13,44,4)(40)(62,37,38,31)(7,108)(25,12)(11,34)(23)(77,10)(67)  (36)    リンク
7  8:(145,125)(53,72)(45,11,9,16,31,33)(2,7)(13)(8,15)(21)(14,32)(30,37,19)(80)(18,73)(62)(55) (31)    リンク
8  1:(175,140,164)(35,29,52,24)(28,160)(6,23)(130,86)(43,60)(26,17)(77)(44,68)(174)(5,155)(150)    (174,130,175)   リンク
9  6:(240,168,187)(149,19)(206)(163,77)(86,82,58)(40,18)(22,202)(4,78)(192,61)(62)(48,13)(35,118)(83)  (4,82)  リンク
10 5:(100,73,59)(14,45)(56,31)(58,42)(25,51)(19,36,26)(16,18,8)(2,17)(10)(77)(74)(28)(23,30)(44,7)(37) (2,19,56,73)    リンク
11 8:(100,88,76)(27,49)(12,19,39,18)(95,11,6)(3,24)(5,1)(21)(20)(16)(2,47)(77,45)(92)(69,26)(17,60)(43)    (19)    リンク
12 11:(262,196,203)(83,106,7)(210)(161,84,17)(36,41,23)(18,111)(31,5)(64)(77,38)(102)(73,248)(238)(175)    (248)   リンク
13 9:(117,79,74)(5,69)(84)(71,46)(20,49)(25,39,57,29)(82,14)(78)(35,18)(13,12,50)(1,11)(4,10)(33,6)(27)    (1,12)  リンク
14 7:(163,95,78)(17,61)(68,44)(24,81)(89,94,72)(15,66)(42,45)(84,5)(79,20)(59,3)(26,22)(16,50)(4,34)(30)   (30,26,3)   リンク
15 10:(100,95,69)(26,43)(11,16,77,17)(88,12)(6,5)(1,20)(19)(60)(39)(18,21)(45,92)(76,27,3)(24)(49,2)(47)   (47,2)  リンク
16 8:(120,78,102)(34,20,24)(14,6)(2,10,23,91)(8)(53,13)(75,45)(36)(4,32)(30,47,25)(22,3)(19,107)(105)(88)  (4,36,13)   リンク
17 13:(119,124,96)(28,68)(114,5)(117,40)(56,52)(4,48)(21,15,24)(106,8)(6,9)(98,38,16)(13,20)(22,7)(75)(60) (20)    リンク
18 2:(302,277,246)(57,189)(25,117,109,26)(235,92)(83)(8,135,49)(90,127)(81,157)(53,37)(5,76)(304)(288)(233)    (53,90,92)  リンク
19 13:(158,109,240)(49,60)(114,82,11)(71)(126,267)(62,52)(10,42)(40,32)(8,51,141)(48)(14,37)(39,9)(23)(7,53)(46)   (60)    リンク
20 8:(132,113,251)(19,36,58)(107,27,17)(10,21,22)(25,12)(1,20)(13)(80)(32,6)(26)(23,35)(130)(121,245)(127,3)(124)  (26,6)  リンク
21 4:(187,127,194)(60,67)(131,76,40)(33,72,156)(44,29)(19,10)(55,21)(82)(13,27,4)(23)(34)(50)(190,30)(160,2)(158)  (60,127)    リンク
22 12:(239,245)(132,107)(124,121)(27,25,32,23)(3,118)(35,115)(113,19)(12,13)(17,10)(6,26)(21,1)(20)(36)(22,80)(58) (124,245)   リンク
23 3:(176,152)(24,38,90)(80,63,43,14)(52)(20,23)(17,26,40)(37,42,86)(72,25)(16,10)(6,4)(49,19,8,5)(47)(3,44)(11)(30)   (17,63) リンク
24 15:(171,181)(95,76)(93,88)(19,30,27)(42,40,32)(5,83)(3,15,29,78)(21,12)(9,18)(8,54)(4,25)(2,46)(22)(44)(1,24)(23)   (93,181)    リンク
25 13:(152,88,112)(64,24)(44,92)(200,12,4)(8,7,33)(1,6)(16,5)(11)(27)(18,15)(3,31,73)(29,19)(10,9)(40)(39)(77,2)(75)   (3,15)  リンク
26 13:(484,316,379)(108,145,63)(82,360)(42,66)(29,198)(18,24)(308,194)(119)(69,50)(168,80)(114,149)(440)(387,35)(352)  (379)   リンク
27 12:(181,191)(93,88)(24,23,54,46,44)(1,22)(25)(2,42)(4,18)(8,40)(29)(9,21,32)(15,12)(3,30)(5,103,27)(98)(19,95)(76)  (21)    リンク
28 18:(83,79,123)(35,44)(52,31)(21,36,9)(27,60,89)(48,25)(10,20,33)(23,12)(2,5,13)(11,3)(8)(102,49,45)(16,73)(4,57)(53)    (123)   リンク
29 4:(649,439,456)(214,208,17)(473)(6,55,147)(385,260,4)(175,49)(104)(12,135)(116)(81,94)(125,216)(203,7)(615)(510)(419)   (81,175,4)  リンク
30 17:(100,114,48,53)(43,5)(58)(23,20)(61,25,14)(3,75)(11,47,96)(36)(95,49)(24,51)(37,105,27)(78)(9,28)(59,26,19)(7,40)(33)    (58,5)  リンク

1時間経過次点で残ってしまった問題は以下。それぞれ15分くらいかかった。

  • 左上の頂点のみ保持する方針だったが、90度回転すると左上の頂点も変わるのだった
    • しかもその計算に辺の長さが必要なので、頂点と辺の長さを両方持つ必要があった
  • 次のグループの座標候補 @next_points が、正方形の配置時に辺と重なったら消さねばならない
    • 気がついていたけど、放置してもテスト一個くらい通るだろうと思ったら、一個も通らず

テストを真面目に書いたせいで時間を費やしたってのもあるけど、テストで手を抜いていたら後半のリカバリ時にかなりきつかった気がする。

Re: 不良セクタの隣〜 横へな 2014.3.7 問題

不良セクタの隣 の問題で肩慣らし。

以下はとりあえずテスト通しただけの状態そのままのコード。単体テスト含めてフルスクラッチからで45分くらい。場当たり的にモジュール化したので、無駄が多い。

初め「複数の」を見落としてて、最後に方針転換したけど、適度にモジュール化されてたのでなんとかなった。

Cloud9上で解いたので、キーバインドでも苦しんだ。Ctrl-s 押し忘れてセーブされてなかったり Ctrl-k 押してよくわからなくなったり。あと、45分の時間には含んでないけど、 コアモジュールのインストールも必要

package Sector;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw(solve);

my @all_sectors = (
    [100 .. 107],
    [200 .. 215],
    [300 .. 323],
    [400 .. 431],
);

sub degrees ($$) {
    my ($sectors, $sector) = @_;
    my $deg = 360 / @$sectors;
    my $sec_num = $sector - $sectors->[0];
    if ($sec_num == 0) {
        ([360 - $deg / 2, 360], [0, $deg / 2]);
    } else {
        ([($sec_num - .5) * $deg, ($sec_num + .5) * $deg]);
    }
}

sub sectors_in_degrees ($$$) {
    my ($sectors, $deg1, $deg2) = @_;
    my %result;
    for my $sec (@$sectors) {
        my @ds = degrees($sectors, $sec);
        for (@ds) {
            my ($d1, $d2) = @$_;
            $result{$sec}++ unless ($d1 <= $deg1 && $d2 <= $deg1) || ($deg2 <= $d1 && $deg2 <= $d2);
        }
    }
    return keys %result;
}

sub warning_sectors ($$) {
    my ($all_sectors, $sector) = @_;
    my $ringnum = int($sector / 100) - 1;
    my @degrees = degrees($all_sectors->[$ringnum], $sector);

    my %result;
    
    my $localnum = $sector % 100;
    my $localmax = @{$all_sectors->[$ringnum]};
    $result{$all_sectors->[$ringnum][($localnum + 1) % $localmax]}++;
    $result{$all_sectors->[$ringnum][($localnum + $localmax - 1) % $localmax]}++;

    if (0 < $ringnum) {
        my @secs = map { sectors_in_degrees($all_sectors->[$ringnum - 1], $_->[0], $_->[1]) } @degrees;
        $result{$_}++ for @secs;
    }
    if ($ringnum < $#$all_sectors) {
        my @secs = map { sectors_in_degrees($all_sectors->[$ringnum + 1], $_->[0], $_->[1]) } @degrees;
        $result{$_}++ for @secs;
    }
    
    keys %result;
}

sub _diff ($$) {
    my ($ar1, $ar2) = @_;
    my %ar2 = map { $_ => 1 } @$ar2;
    grep { ! $ar2{$_} } @$ar1;
}

sub _uniq ($) {
    my $ar = shift;
    my %ar = map { $_ => 1} @$ar;
    keys %ar;
}

sub solve ($) {
    my @broken = split /,/, $_[0];
    my %warnings;
    for (map { warning_sectors(\@all_sectors, $_) } @broken) {
        $warnings{$_}++;
    }
    my @many_warnings = grep { $warnings{$_} >= 2} keys %warnings;
    my @pure_warnings = _uniq [_diff(\@many_warnings, \@broken)];
    join ',', sort @pure_warnings or 'none';
}
use strict;
use warnings;
use Test::More import => [qw(done_testing is)];
use Sector qw(solve);

while (<DATA>) {
    tr/\r\n//d;
    my ($num, $in, $out) = split /\s+/, $_;
    is solve($in), $out, "TEST#$num";
}

done_testing;
__END__
0  400,401,302 300,301,402
1  105,100,306,414 none
2  100 none
3  211 none
4  317 none
5  414 none
6  100,106 107
7  205,203 102,204
8  303,305 304
9  407,409 306,408
10 104,103 207
11 204,203 102,305
12 313,314 209,418
13 419,418 314
14 100,102,101 201,203
15 103,206,309 205,207,308,310
16 414,310,309 206,311,413
17 104,102,206,307,102,202 101,103,203,204,205,207,308
18 104,206,308,409,407 103,205,207,306,307,309,408,410
19 313,406,213,301,409,422,412,102,428 none
20 101,300,210,308,423,321,403,408,415 none
21 304,316,307,207,427,402,107,431,412,418,424 none
22 205,408,210,215,425,302,311,400,428,412 none
23 200,311,306,412,403,318,427,105,420 none
24 105,305,407,408,309,208,427 104,209,306,406
25 311,304,322,404,429,305,316 203,303,321,405,406,430
26 210,401,316,425,101 211,315
27 414,403,404,416,428,421 303,415
28 207,300,103,211,428 104,206
29 322,314,310 none
30 427,200,215 100,323
31 311,402,424,307,318,430,323,305,201 200,204,301,302,306,322,423,425,431
32 425,430,408 none
33 202,320,209,426 319,427
34 430,209,302,310,304,431,320 202,303,323
35 208,206,406,424,213,312 207,311,313
36 420,302,313,413,317,402 301,403
37 319,306,309,418,204,411 305,307,308,412
38 400,308,105,430,203,428,209 104,210,429,431
39 200,305,214 215
40 214,408,410,407,317,422 306,316,409,423

毎回そうなんだけど、実装中、部品の単体テストをしてないので、最後にテスト全部通るかは神のみぞ知る。とはいえ、1時間で部品の単体テストまで書くのは、時間配分的に現実的ではないと思う。

追記: 3年前の自分の回答 。今書いたコードより整ってるようには見えるけど、 $D ってなんだっけ・・・。

GHCi上でpiと打てば円周率が表示されるという話

裏書きに残りっぱなしになってたのを書いておく。GHCI上で pi と打つと、πの値が表示できる。

Prelude> pi
3.141592653589793

Pythonだとそうはいかない。

>>> pi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'pi' is not defined
>>> from math import pi
>>> pi
3.141592653589793

つまり、Haskellではグローバルな名前空間pi が出ているように見えるということ。これはなかなか気持ち悪い。

pi の正体を見てみよう。

Prelude> :i pi
class Fractional a => Floating a where
  pi :: a
  ...
        -- Defined in ‘GHC.Float’

Floating 型クラスに定義されているようだ。 Floating 型クラスは Prelude モジュールに含まれているので、 import などしなくても名前にアクセスできる。なるほど。

さて、拙著 Haskell入門 にも書いたが、この型クラスは浮動小数点数を意味する。この型クラスには pi 以外に、一般的な数値計算で必要な explogsin などの関数も入っている。代表的なインスタンスDoubleFloat

Prelude> :i Floating
class Fractional a => Floating a where
  pi :: a
  exp :: a -> a
  log :: a -> a
  sqrt :: a -> a
  (**) :: a -> a -> a
  logBase :: a -> a -> a
  sin :: a -> a
  cos :: a -> a
  tan :: a -> a
  asin :: a -> a
  acos :: a -> a
  atan :: a -> a
  sinh :: a -> a
  cosh :: a -> a
  tanh :: a -> a
  asinh :: a -> a
  acosh :: a -> a
  atanh :: a -> a
  GHC.Float.log1p :: a -> a
  GHC.Float.expm1 :: a -> a
  GHC.Float.log1pexp :: a -> a
  GHC.Float.log1mexp :: a -> a
  {-# MINIMAL pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh,
              asinh, acosh, atanh #-}
        -- Defined in ‘GHC.Float’
instance Floating Float -- Defined in ‘GHC.Float’
instance Floating Double -- Defined in ‘GHC.Float’

pi が定数ではなく型クラスのメソッドになっているのは、恐らく FloatDouble で多相的に扱いたいからであろう。 GHCの実際の定義 は、以下。お、おう、といった感想。浮動小数点数だから、リテラルに小数点を何桁まで書いたかは精度とは関係ないってことだろう。結局は親の型クラスが持つ fromRational を使って処理される。

instance  Floating Float  where
    pi                  =  3.141592653589793238

instance  Floating Double  where
    pi                  =  3.141592653589793238

一応、ghci上で違いを見ておく。

Prelude> pi :: Double
3.141592653589793
Prelude> pi :: Float
3.1415927

もう一つ疑問が残るのは、数ある数値系の型クラスの中で、なぜ Floating というより具体性の高い型クラスに pi が定義されたのか。 Floating に定義されている関数は、実数値上に定義される関数である。Haskellでは、有理数Fractional 型クラスで表現されている。しかし、当然ながら実数値を正確に表現できる型はない。そこで、コンピュータが一般的に用いる浮動小数点数にこれらの関数を定義した、ということだろう。

最後にまとめると、 pi については以下となる。

  • Prelude に定義されているので、識別子 pi でアクセスできる
  • メソッドになっているため、 FloatDouble で多相的に扱える
  • 実数という型がないので、浮動小数点数上の演算としているのだろう