あざらしとペンギンの問題

主に漫画、数値計算、幾何計算、TCS、一鰭旅、水族館、鰭脚類のことを書きます。

城の崎にて、あざらしを観てきた

Twitter の陰謀によって突如としてキビヤックにされたくろは先生が掘り起こされて新たな生を始められたということで、もうこれはイースターと言っても過言ではないですね!あざらしの卵を用意しなければ!

とはいえ、聖遺物であった『アザラシック・ワールド』のツイートが失われてしまったことは大変悲しむべきことです。我々はこの事件を深く心に刻み、いざとなれば訴訟を起こす構えを示さなければなりません。本件において仮に Twitter 社が凍結されたアカウントをすべて復活させて事件をなかったことにしようとしても、既に大量の証拠が残されてしまっている現状では、それも不可能でしょう。ただ、こちらとしても実際に訴訟に持ち込むことは現実性がないでしょう。利用規約において、Twitter は利用者に対して理由の如何を問わず無制限にアカウントを停止する権限を有するとされています。現行の規約ではアカウントの停止に対する異議を唱えることができるとされていますが、Twitter がそれに応じるかどうかについては何も定められていません。というか読んでみると整合性がアレでこの会社マジでダメなんじゃないかと

https://twitter.com/en/tos

いつもの長いアバンはこれまでとして、タイトルの通り、城崎温泉に行ってきました。私の誕生日が9月1日だったので、その記念と18きっぷの使用を兼ねて。残念なことに出遅れて特急を使うことになりましたが……私にはお金がないので痛い出費でした。到着が21時と遅くなったので、その日は旅館に泊まっただけで、観光したのは翌日です。

さて、城崎温泉といえば、志賀直哉の『城の崎にて』が有名です。1913年8月、東京を訪れた志賀は、山手線にはねられて重傷を負いました。命は助かったものの、しばらくの療養のために彼が訪れたのが城崎温泉です。生々しく死を突きつけられた体験は、彼の頭に深く刻まれたのでしょう。1917年、志賀は同人誌『白樺』に短編『城の崎にて』を発表されました。それは彼の代表作のひとつとなり、現在までその名をとどめています。

文学史上に現れる鉄道人身事故では、レフ・トルストイの『アンナ・カレーニナ』もよく知られます。本作の主人公であるアンナは、列車に身を投げて自殺しました。先の志賀直哉は本作を高く評価しており、近代小説の教科書とまで言ったようです。それだけ志賀にも影響を与えたということでしょう。彼が列車にはねられたのはもちろん偶然ですが、それがもとで傑作が生まれたことには、不思議な縁を感じるところがあります。

さて、私の話に戻りますが、城崎温泉へ行こうと思ったことの決め手は、『帰宅部活動記録』(作:くろは)の文学回(アニメでは5話)に登場する次のあざらしでした。

f:id:azapen6:20170916055331j:plain

つまりは『城の崎にて』からの縁で私は城崎温泉を訪れたのです!もちろん作品はちゃんと読んでいますよ。現地に持っていって読めればよかったのですが、出発からドタバタして結局なりませんでした。

城崎温泉の旅館に泊まると、外湯巡りのフリーパスがもらえます。これは外湯の受付でバーコードをかざせば無料で入浴できるというものです。泉質は駅前の飲泉所で飲んでみればわかる通り食塩泉です。城崎では浴衣に下駄が正装というように、旅館で借りた浴衣と下駄の姿で外を出歩くことができます。下駄の音を鳴らして歩くなんて風情があって良いなぁ、と思ったのは最初のうちで、鼻緒を挟んでいる指の間が痛くなったのを我慢して歩きました。旅館に戻った頃には指の間から血が滲んでいて、優雅を気取るのも楽ではないな、と思いました。

旅館を出た後は温泉街を観光した後、近くにある水族館『城崎マリンワールド』に行きました。もちろんここを訪れることも旅の目標でした。それまで持っているだけだった障害者手帳がここで初めて役に立ちました。水族館の情報はウェブサイトを観てください。

marineworld.hiyoriyama.co.jp

もちろん私が観に行きたかったのはあざらしとペンギンがいる外の水槽なわけで。あざらしの他にもアシカ、トド、セイウチが出揃っていて、アシカ・イルカショーもアシカの方に重きが置かれ、セイウチがボスとして出てくるといったように、かなり鰭脚度の高い水族館でした。アジを釣って天ぷらにするという他にはないアトラクションもありました。「美味しい天ぷらを、釣りましょう」という見も蓋もない文句、好きです。とはいえ時間と金の関係でパスして、水族館を後にしました。

今回は旅の話を書きましたが、実際のところ私にバリバリ旅をしたりイベントに出たりする余裕などはありません。保険・障害関係の手続きは1回あたり2-3ヶ月待たされるのが普通なので、尿と吐瀉物にまみれた東京という土地から名実ともに去ることは、近いうちには難しいでしょう。

最後に行った大きなイベントは、7月にあった『うらら迷路帖』アニメのイベントでした。一言で言えば、最高、でした。『うらら迷路帖』は原作から好きになった作品ですが、アニメ7、8占は合わせて5回泣けるほど濃い話数でした。どうでもいい話ですが、私が前回の『よんこま小町』に出すつもりで作っていた同人誌『迷路町通信・第壹號』のデザインは、上で言及した文学同人誌『白樺』を参考にして考えました。変なところで話はつながるものですね。

うららイベントでは、それまでとは一転して、先輩としてちゃんと進行をされていた諏訪彩花さんが登壇される『SSちゃんねる』のイベントは、昨年は4公演全通しましたが、今年は行けそうにありません。

昨年といえば春の『あどりぶ』のイベントにも当選して行きました。最前列でゆっこさんを直前に拝める神席でしたが、むしろスカートの中が見えそうで鰭が鰭でなかったのは良い?思い出です。

1日早いですが巽悠衣子さん、お誕生日おめでとうございます。『下向き』は毎回神回です。これが 324 円払えば実質無料だなんて信じられません。『けいおん!』から『kiss×sis』を知って、この強そうな名前の美人は誰だ?けいおん!にも出ていたのか、いうところから随分と年月が経ちました。ずっと応援していたとはとても言えませんが、これからも応援しています。金の続く限り。

後は辞世の句でも詠めば締められるでしょうか。残念ながら私はいきなり何も考えることなく死ぬのが理想なので、そういうことはしません。

では、本当に最近の話をしましょう。最近は絵もあまり描けていなくて、1日の記憶があまりありません。

まず結構大きかったのは、sbt がメジャーアップデートされ、遂に Version 1.0 がリリースされたことがあります。『天之御船学園放送部♪』@anhpmsbot は現時点では sbt 上で動いているのですが、それまで長期間走らせておくと再起動時に変なエラーで落ちていたのが無くなって、安定した運用ができるようになりました。これで Scala での開発が捗り、30億のデバイスで走るようになればいいと思います。

今のところの主力開発言語はこの通り Scala ですが、同時に OCaml も主力に加えようとしています。理由は、速度面で有利であること、また、F# の中核をなす言語であることから、ウィンドーズで何かを作らなければならなくなったときにも対応しやすいことなどがあります。まぁ、プログラム言語はあくまで道具ですので、覚えた数など問題ではなく、しかし使える武器が多いことに越したことはないといったところです。

『天之御船学園新聞部♪』anhpms.org はまだ稼働していません。ドメインを取ってからもう随分経つのに実に勿体無いですね。ドメインは namecheap というレジストラで取っています。anhpms.orgWHOIS protection を含めて1300円/年くらいです。何はともあれ早くページを作らないのは馬鹿です。

Domain Names - Cheap Domain Names | Namecheap.Com

体の方では、少し前に『クエチアピン』なる薬が出ました。先発薬が『セロクエル』の名前で通っている有名な薬です。この薬を飲んでからでしょうか、妙に甘いものを欲するようになり、一方で塩気のあるものをあまり欲しなくなりました。普段の食欲が落ちた代わりに、甘いものが無性に食べたくなります。この薬は血糖値を上昇させるため。使用には注意が必要ということです。

『ららマジ』なるゲームをリリース直後から続けてやっているのですが、現時点でやれることをほとんどやり尽くしてしまいましたので、基本的にイベントドリブンです。イベントシナリオでは器学部員達の掛け合いが増えてより楽しくなりました。しかし、今望んでいるのは本編ストーリーの追加と絆レベルの上限解放です。第7幕『マランドリーノ』の完璧なストーリーを観てしまうと、期待しすぎてしまうところはあります。あとは私のこととして、『ららマジ』の絵も描きたいのですけどね。一応フレンドIDを書いておきます。

名前:とっかりーん
フレンドID:RvZA29mz3f

色々なことを詰め込んでまとまりのない話になってしまいましたが、今回はこれで締めることにします。では。

azapen6

Lazy Days

くろは先生が和製キビヤックになられたことについて、謹んでお悔やみ申し上げます。

どうも、最近絵を描けていない azapen6 です。久しぶりに昨日の『あんハピ♪』ワンドロで描いたものを貼っておきます。


【あんハピ♪】「あんハピ♪ OHD 8/26『クロスオーバー』」イラスト/あざペン [pixiv]


【あんハピ♪】「あんハピ♪ OHD 8/27『朝』」イラスト/あざペン [pixiv]

元より絵を素早く描ける方ではないのですが、しばらく描かないうちに手が忘れてしまったようで、どうにもこうにもという感じです。1枚目の『あんハピ♪』と『うらら迷路帖』の掛け合わせはずっと前から考えていたことで、『迷路町通信』第壹號に乗せる予定だったのですが、諸事情によって流れてしまっていました。

余計な話はここまでにして、今回の本題は『遅延をスケジュールする』話です。

まずこの題目からして意味が解らないでしょう。遅延とは設定されたスケジュールから遅れることを言うので、まず言葉として矛盾しています。

一体何の話かというと、Scala における値の遅延評価をスケジュールとして設定してやるという話です。

動機

そもそもの動機は Twitter で『天之御船学園放送部♪』@anhpms として運営している bot を書くにあたって、過去のある時点に設定されたスケジュールを実行して得た結果を後から参照したいということでした。例えば、次のようなものです。

  1. 15:00 にはなこが「待って〜、猫ちゃ〜ん!」と言って走り出す。
  2. 15:30 にヒバリがはなこを見つけて「何、してるの!?」と声をかける。

はなこに何があったかはこの際置いておきます。これを実際にプログラムとして書くためには、次のような手順が必要になるでしょう。

  1. 次の手続きを 15:00 に行うように設定する:Twitter API の statuses/update を実行し、「待って〜、猫ちゃ〜ん!」とツイートする。結果としてそのツイートを表すオブジェクト status1 を受け取る。
  2. 次の手続きを 15:30 に行うように設定する:statuses/update を status1 へのリプライとして実行し、「何、してるの!?」とツイートする。
  3. 15:00 になり、1の手続きが実行される。
  4. 15:30 になり、2の手続きが実行される。

ここで重要なのは、2の手続きを実行するに当たって、1の結果が必要になるということです。リプライの対象とするツイートが必要なのです。

とりあえずのコード例

このようなプログラムを、例えば Groovy を用いて書くのは簡単です。以下、schedule(hh, mm) { proc } の記法によって、hh:mm にクロージャとして定義された手続き proc を実行するように設定することを表します。

Groovy

Groovy では次のように書くと望み通りのことができます。以下、Twitter の操作は twitter4j を利用して行うものとします。

def status1 // デフォルトの初期値は null

// 1の手続き
schedule(15, 0) {
    status1 = twitter.updateStatus("待って〜、猫ちゃ〜ん!")
}

// 2の手続き
schedule(15, 30) {
    su = new StatusUpdate("何、してるの!?")
    su.setInReplyToStatusId(status1.getId()) // ここで status1 が必要
    status2 = twitter.updateStatus(su)
}

ここで注意すべきは、ふたつのクロージャが変数のバインディングを共有しているということです。まず、スケジュールが設定された時点では、status1 には null が入っています。それぞれのクロージャこの status1 に対して読み書きを行うことができます。つまり、最初のクロージャで代入する変数 status1 は、その括弧の中だけに束縛された変数ではありません。よって、そこで代入を受けた status1 の値は2番目のクロージャで使うことができます。

1の手続きで Twitter への投稿が失敗した場合は twitter4j から例外が投げられます。ここでは何の例外処理もしていないので、そのままプログラムが落ちて2の手続きは実行されないでしょう。

もし何かの間違いで手続きの実行順序が逆転してしまった場合、status1 = null のまま2の手続きが実行されてしまい、status1.getId() で例外が発生して落ちます。

これらのようなエラーが発生しなければ、上のプログラムはスケジュールに従って正しく実行されるはずです。

Scala

さて、ここからが本番です。実を言うと、Scala でも大体は上と同じようにコードを書くことができます。しかし、そうしたくない理由があります。それは、『変数への代入』です。

変数への代入は関数型プログラミングにおいては原則としてすべきないと見なされています。なぜなら、代入の副作用によって関数の評価値が変わってしまう場合があるためです。関数型プログラミングでは、関数の同じ引数に対する評価値は常に同じであるべきです(参照透過性)。副作用は可能な限り避けることが望ましいので、上のような書き方は宜しくないというわけです。

変数への値の代入を最初の一度だけ行うことにすれば、そのような副作用が起こることはありません。この場合、変数はもはや変数ではなく、決まった値に対する名前でしかありません。変数への代入は名前への関連付け、アサインメントというものになります。

さて、Scala で値を宣言するキーワードは val でした。値の実質的な代入は前の groovy のコードでも一度しか行われていないので、次のように書けると思われるかもしれません。しかし実際は失敗します。なぜなら、Scala では val にしろ var にしろ宣言時に値のアサインメントを行う必要があるからです。

val status1: Status // エラー

// 1の手続き
schedule(15, 0) {
    status1 = twitter.updateStatus("待って〜、猫ちゃ〜ん!")
}

// 2の手続き
schedule(15, 30) {
    val su = new StatusUpdate("何、してるの!?")
    su.setInReplyToStatusId(status1.getId()) // ここで status1 が必要
    val status2 = twitter.updateStatus(su)
}

もし Groovy のコードと同じように書きたいならば、1行目を var status1 : Status = null と書けばよいでしょう。しかし、これでは実質的に値が変わらないのに変数を使うことになって、Scala 的にはあまりいい気がしません。

括弧の中の status1val をつけるのもダメです。スコープが括弧の中に限定されてしまい、必要となる場所から見えないということになります。

さて、どうしたものか。

遅延評価

以上のことをサクッと解決するのが値の遅延評価です。遅延評価というのは、その値が実際に使われるときになって初めて式を評価するということです。Scala では、遅延評価する値を lazy val で宣言します。怠惰ですね。

理解するには実際に見た方が早いと思うので、簡単なコード例を示します。まず、lazy をつけない場合:

def f(x: Int) = { println("here"); x }
val a = f(1)
println("hello")
println(a)
println("bye")

これを実行すると、次のように出力されるはずです。

here
hello
1
bye

この場合では、val a = f(1) の時点で右辺が評価されるので、here は hello の前に来ます。これを遅延評価に対して先行評価と言います。

一方、lazy をつけた場合:

def f(x: Int) = { println("here"); x }
lazy val a = f(1)
println("hello")
println(a)
println("bye")

これを実行すると、次のように出力されるはずです。

hello
here
1
bye

これは、lazy val a = f(1) の時点では右辺が評価されず、後に println(a)a が要求されたときに初めて右辺が評価されて実際のアサインメントが行われます。だから here は hello の後に来るのです。

遅延評価というのはおよそこのようなものです。遅延評価という用語は、特定の言語が備える特定の機能を指すものではなく、プログラムの実行順序を決めるスキームの一種を指すものです。他にも様々な形で遅延評価を提供するものが数々のプログラム言語にあります。

遅延をスケジュールする

武器は手にしたので、あとは上の Scala のプログラムをサクッと通るようにしてやりましょう。

// 1の手続き
lazy val status1 = twitter.updateStatus("待って〜、猫ちゃ〜ん!")

schedule(15, 0) {
    status1
}

// 2の手続き
lazy val status2 = {
    val su = new StatusUpdate("何、してるの!?")
    su.setInReplyToStatusId(status1.getId()) // ここで status1 が必要
    twitter.updateStatus(su)
}

schedule(15, 30) {
    status2
}

はい。lazy val の評価自体が手続きに含まれるため、コメントの位置を移動させました。見た目上はスケジュールの設定のところで関数を呼んでいるような感じです。

Groovy の場合と同様に、1の手続きが実際に行われる status1 の右辺の評価で Twitter への投稿が失敗した場合は twitter4j から例外が投げられます。当然、そのままプログラムが落ちて2の手続きは実行されないでしょう。

しかし、もし何かの間違いで手続きの実行順序が逆転してしまった場合は Groovy の場合とは違ったことが起こります。status2 が要求されたとき、その中で status1 を要求しているので、その時点で2つのツイートが正しい順序で投稿されるのです。当然ながらスケジュールは狂います。

まとめ

今回は、Twitterbot でスケジュールを実行するとき、既に実行が済んでいる手続きの結果が欲しいという議題について、値の遅延評価を宣言する lazy val を使って『遅延をスケジュールする』ことによる解決方法を示しました。

簡単なスケジュールを実行させるサンプルコードと、『天之御船学園放送部♪』@anhpmsbot のコードは以下にあります。放送部♪の機能はこれからも追加していく予定です。更新履歴は GitHub を見てください。

GitHub - azapen6/SimpleTwitterScheduler: An example code of Twitter scheduler using twitter4j and akka
GitHub - azapen6/AnhpMSBot: Anne Happy (Unhappy Go Lucky) Mifune School twitter bot

はてさて、今回の題名は一体何のことやら。

やはり\アッカリーン/しようかな

やはり twitter4j でやるには厳しいものがあることのは感じていたのです。

特に、interface StreamListener がなぜか Scala から見えない(なぜかプライベート扱いになっていて、コンパイル時にアクセス権がないと怒られる。interface のくせに。Groovy からは普通に見えるしアクセスもできる)ので、解決のために少しの Java コードを書かなければいけなかったのは、私としては美しくないと思いました。(sbt では Java ファイルも一緒にコンパイルしてくれるので実用上の問題はありませんでしたが。)

bot のタスクマネージャに akka を使ったのは、特定の日時にイベントを発生させるものとして他に使えるのが java.util.cuncurrent.ScheduledThreadPoolExecutor とかいうやたら長い名前のクラスしかなかったことがあります。これを用いた場合、ストリームを開いている間はスレッドセーフでない*1可能性がありました。そもそも Scala 自体には並列処理を直接扱う API が少ないのです。元々は akka が提供するようなアクターモデルを並行処理の中核としていたのですが、Scala 本体の実装は既に非推奨となっていて、akka を使うようにと公式ドキュメントに書いてありました。そんなこんなで akka を採用することを避けられざる形で決定しました。

twitter4j が比較的元の Twitter API に忠実で、Java 用のライブラリとして標準的な設計であること(良いとは言わないが妥当ではある)は評価できるのですが、Java 8 で非推奨となった API が使われていたり、media/upload (chunked) をサポートしていなかったりと、既に古くなった印象は否めません。なにしろ最初に作られたのが10年も前、Twitter が日本でそれほど普及していなかった頃です。また、少なくない設計が私の好みでなかったこと(そりゃまぁ Java 用のライブラリなので)もあります。その上で、開発版の更新が半年前から止まっていることを見て、これ以上を望むのは厳しいと思いました。*2

その辺りを考慮すると、Scala でそれらの問題を解決するためには、やはり新しいライブラリを書く必要があるという結論に至らざるを得ないようです。もちろん検索はしてみましたが、わざわざ Scala でそんなことをやろうとしている人はいないようでした。

先程から名前を挙げている akka は、アクターモデルの実装を中核として、並行・分散処理に長けた複数のライブラリを提供しています。その中には HTTP タスク処理用のライブラリなども含んでいます。ドキュメントを読んだ限り、SNS や Web クライアント/サーバでの使用が最初から意図されているように思われます。当然 Java 本体の API でも同じようなことはできますが、多くの Scala ライブラリと同様に、スレッドセーフに並行処理を行うことが容易になっています。

というわけで、書く書く詐欺に終わらなければ、akka を用いた純粋な Scala での Twitter ライブラリを作ることにしました。

名前は、akka にちなんで\アッカリーン/とか、まぁそれでは Twitter との関係性が見えないですが、プロジェクトを作ってから決めます。

*1:複数のスレッドが同じデータを同時に改変する場合が起こりうる。参照透過性を参照

*2:ビルド時に mvn のテストに落ちるのでスキップ必須なのはさすがにどうかと