kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

エアリプをやめよう

 こんばんは、kmizuです。表題の件についてですが、少し前の私への自戒として、あるいは、エアリプ多めの方々に対して思うことです。

 まず、最初に、エアリプについて。特に、ツイッターでよく使われる言葉で、特定の相手に対して反応する意図がありながら、メンションないしは、特定が可能なワードを用いずに(あるいは、特定が不可能なように)、その相手について言及する行為を指す事が多いようです。ここでは、"kmizuさんがうんぬん"みたいに、@つけないだけで、読んだ人が、誰を指しているのか特定可能なツイートは、エアリプには含めません。また、リアルタイムでの時事問題への言及とかその辺も、エアリプに含めるのはおそらく適当ではないでしょう。

 これ、振り返ると、一年くらい前の(あるいは数ヶ月前も、かも)自分とかちょくちょくやってたんですが、特に人間関係的な意味でよくないよな、という事を最近思います。もちろん、エアリプが常に否定的な場面で使われるわけではないのですが、観測する限り、否定的に、あるいは批判的な場面で使われる事が多いように思います。

 否定的なエアリプがたまに、ならいいんですが、常態化すると、特にその人をフォローしていたり、ウォッチしていたりする人は、「あ、これ、ひょっとして、自分の事指しているのかも」とか、グサっと来たりすることがあるわけです(あくまで、私はそう感じるというだけですが、特殊な感性でないのなら、エアリプで論じられている内容に自分が思い当たるところがあれば「自分のこと指しているのでは」と思う心情自体は自然だと思います。特に、内省的なタイプならなおさら)。エアリプをした当人は、そのつもりが毛頭なくても、受け取り手はわかりようがないですし。

 そして、エアリプを頻繁にする人は、相手もそうであると想定してしまいがちなきらいがあるようで(※自戒を込めています)、あくまで一般論として書いたものを、エアリプであると読解してしまう事がどんどん増えていくようです。で、その結果、何が生まれるかというと、一言でいうと「相互不信」でしょうか。エアリプが一般論として取られる分にはいいのですが、一般論をエアリプであるのではないかと考えたり、あるいは相互に決めつけあったりするのは、何もいいことがないですよね。

 あと、それより何より、ツイッターは(鍵垢でない限り)、全世界から見られる、一種の「公共空間」なので(最近は、そういう認知がない人が増えていますが、数々の炎上事件を考えても、プライベートルームで言ったつもりの陰口が……って事は多いですよね)、そこで特定の相手を実質的に指しながら、明示しないのは、「相手に伝わる事を想定した陰口」(しかも、陰口を言われた当人は、そのエアリプに対して、メンションでは反論しづらい)」であって、相手に伝わらないようにする陰口よりも、むしろ趣味悪いのでは、と思うのです(※自戒を込めてます)。

 もちろん、特定の相手の言説への(内心での)批判から始まって、一般論に昇華したいって事はあると思いますし、そういうのはエアリプとはまた別ですが、エアリプは最小限にした方が、妙な相互不信が生まれることも減り、ツイッターランドがもうちょい平和になるのではないか、と思うのです。

 くどい程になりますけど、この辺は、たぶんに自戒を込めてます。というわけで、エアリプについて思った事でした。  

心因性発熱という病気

 私をご存知の方は、ひょっとしたら心当たりがあるかもしれませんが、私は昔から、高いストレスがかかるときに、しばしば熱を出していました。知恵熱とかいうのではなく、実際に37.5℃~38.2℃くらいの熱が出ます。特に、社会人になってから、体調不良で会社お休みしたり、イベントに出られなくなったりといった事の、たぶん、半分近くがこれ起因ではないかと思います。ちなみに、元をたどると小学校の時からこのような症状があったので、たぶんに生まれつきなのでは、という気もしてます。

 で、この「よくわからないけど、熱だけが出る」というの、今まで「体質」という言葉で済ませていたのですが、昨日調べていたところ、「心因性発熱」という名前がついていたことを知りました。概要は以下のページにある通りで、私の症状もだいたいこんな感じです。

medicalnote.jp

 私のタイプは、記事にある急性型の方で、

特定のイベントに反応して高熱(40度近くになることもある)がでる場合があります。高熱が急に出るタイプの心因性発熱の場合、そのストレッサー(ストレスを与える要因)が明確で、 たとえば学校に登校した途端に高熱が出て、帰宅後はすぐ平熱になる場合があります。

 という感じです。これが厄介なところは

  • 解熱剤は効かない
  • ストレッサーから距離を取るとか、原因となるイベントから解放されると、速やかに回復する

 辺りでしょうか。当時は自覚していなかったのですが、思えば、そういう熱が出る時は、自分にプレッシャーがかかっている時期だったように思います(博士後期課程の追い込み時期に、お腹がほてって発熱してたのも、この亜種な気がします)。

 今朝も、仕事とは別の原因で、37.5℃くらいの熱が出ている状態でして(プライベート寄りの原因です)、他の症状がない辺りからすると、この心因性発熱っぽいです。ちなみに、「熱だけ」が出るとはいえ、通常の風邪のように倦怠感などの症状が生じるのがやっかいなところで、この状況下で仕事するのは事実辛みがあったりします。

 京都に帰ってきてからは、ストレスは貯まりにくくなっているものの、やはり急に高いレベルのストレスがかかった時に、熱が出るのは変わらないようでなんとも厄介なものです。これまで、この心因性発熱のせいで色々な方にご迷惑おかけしていたかと思うと、なおさら申し訳ないです。しかも、調べた限り、高い確率で睡眠障害もコンボで現れるようで、確かに、そのような時は前日寝られなかったりしたものです。    ともあれ、これまで「体質」と片付けていたものに、ラベルがついたことで、「心因性発熱を克服する」という方向性は見えたので、良い方向ではある気がします。病院で診療を受ける以外にも、自分で可能な訓練法もあるようですし。

 もちろん、ご迷惑をおかけしてきた方々(あるいは、今後も、時折、ご迷惑をおかけするかもしれない方々)に配慮して欲しい、という話ではなく、「体調不良」って言葉だけでなんとなくごまかすのは、自分にとっても「あれは体調不良だったんだ」って認識を植え付けてしまって、よくなかろうということで、こうして書き留めておくことにしたのでした。

メモリ使用量の削減のために遅延リストを使うのは(多くの場合)アンチパターン

 こんばんは。ちょっと久しぶりにScalaの記事を書いてみようと思い立って、こうやって記事を書くことにしました。

 といっても、タイトルがほとんど全てを表しているのですけど。

 最近のプログラミング言語のいくつかは、俗に遅延リストと称される機能を持っています。HaskellListがデフォルトで非正格(というか、評価が非正格なので、Listもそうなる)のは有名ですし、Clojureにも標準で遅延シーケンスがあります。そして、今回ネタにするScalaも、遅延リスト相当の型が標準ライブラリで提供されています(Scala 2.12ではStream、2.13からはLazyListに改名)。以下では、Scala 2.13の場合に、LazyListをメモリ節約のために使って、ハマる例を紹介します(実際のプロダクションコードで見たのを元に、minimalにしたものです)。

def doHeavyJob(queue: JobQueue): JobResult = {
  val jobs: LazyList[Job] = queue.dequeueAllJobs()// JobQueueからLazyListとしてJobのシーケンスを取得
  val results: LazyList[JobResult] = jobs.map(doJob) //  各々のJobに対してdoJobを実行 
  results.foldLeft(emptyJob)(composeJobs) // JobResultのシーケンスから、結果を計算
}

 ここで、引数queue 自体が保持している(かもしれない)LazyListについては一端考えないことにします。さて、ここで、コードの作成者がわざわざJobのシーケンスをLazyListで表現したのには訳があって、一言でいうと、LazyListで保持することによって、jobs.size に比例する量のメモリを使用しないで良いと考えたようです。

 この誤解は遅延リストの初心者にしばしば見られるものなのですが、一言で言うと大抵の場合それはアンチパターンなので止めよう、ということです。この場合ですと、もちろん、doHeavyJobの呼び出しが終われば、LazyListのために利用したメモリはGC対象になるのですが、foldLeft内でLazyListの中身が評価されると、jobsのサイズに比例した量のメモリが必要になります。jobs.sizeの値が小さければ(あるいはjobsの保持する個々のJobのサイズが小さければ)問題ないのですが、そうでない場合、コード作成者の目論見は大外れに終わり、場合によっては予期しないOutOfMemoryErrorに遭遇する羽目になります。

 そして、このような場合、同じ遅延リストは二度以上再利用されないので、以下のようにIteratorを用いたコードに書き直す事ができます。

def doHeavyJob(queue: JobList): JobResult = {
  val jobs: Iterator[Job] = queue.dequeueAllJobs.iterator // JobQueueからLazyListとしてJobのシーケンスを取得
  val results: Iterator[JobResult] = jobs.map(doJob) //  各々のJobに対してdoJobを実行 
  results.foldLeft(emptyJob)(composeJobs) // JobResultのシーケンスから、結果を計算
}

 こうしてあげれば、doHeavyJobの実行途中でも、jobsのサイズに比例した量のメモリは必要になりません(というのは、Iteratorだと一度しかたどられないので、foldLeftでも一定のメモリしか必要としないわけです)。

 もちろん、すべての場合に遅延リストを使うべきでない、とまでは言えませんが、メモリ使用量が多そうな処理を遅延リスト(ScalaだとLazyList)にすることで、改善しようとするのは危ういというのは言えるかと思います。

パーサコンビネータとPEGの違いについて

ちょっとTwitterの某所で議論を見かけたので、この辺の用語についてまとめておきたい気分です。

まず、パーサコンビネータ(Parser Combinator)というのは、パーサをオブジェクトないし関数ととらえて、パーサを組み合わせて複雑なパーザを組み合わせる技法の総称ってのが私の認識です。最もなナイーヴなパーサコンビネータを作ると、自然にPEG的な挙動になりますが、GLL Combinators なんてのもありますし、HaskellのParsecにしても、try使わないとPEG的な動作をするわけではないので、実用的にも理論的(?)にも、パーサコンビネータかPEGかは独立です。

次に、PEGが何かというと、「文法の表記法」と捉えられることが多いものの、これは一面でしかなくて、Parsing Expression Language(PEL)であるような言語のための形式文法と捉える方がシンプルかなと思います。BNFと違うのは、文脈自由文法という概念と、BNFという具体的な表記法(追記:この言い方は、厳密ではないです。正確には、文脈自由言語を表現可能な具体的な表記法と言うべきでしょうか)が別にあるのに対して、PEGは表記法でありそれ自体でも形式文法である(追記:PEGという名称が、形式文法としても、形式文法の表記法の意味でも用いられることがある、程度の意味です)、という点でしょうか。

ちなみに、BNFとPEGの違を端的に表す例としては、よくあるものだと、

a / ab

の受理言語は{a}になる(後の選択肢は試されない)なんてのがあります。BNFにおける a | ab だと、受理言語が {a, ab} になるのが、違う点です。

メモ書き程度の話で、あんまり当該文脈見てない人にわかりやすいように書いてないですが、参考になれば幸いです。

ついでに、その議論においてPrologが出てきましたが、Prologを使えば色々なパーザ書けるよってのは、自明なことであり、改めて論じるまでもないことな気がします(というのは、Prologプログラミング言語であって、計算能力はチューリング完全なので。もちろん、Definite Clause Grammarのような形で「より簡単に書ける」は真かもしれませんが)。

ウォーキング習慣さらに強化中

 最近、ほとんど毎日(例外は、雨の日と、朝早めに予定がある時くらい)、1時間約6km程度のウォーキングを実践しています。2月になってからでいうと、18日中15日はウォーキングしてるので、週5日以上歩いてる感じですね。

 1月よりさらにウォーキングの継続度合いが上がったわけですが(ちなみに、寒い時はウォーキング前にお風呂、寒くなくてもウォーキング後にお風呂は必ず入ってます)、主観的にはかなり色々な効用があるなと感じています。ざっと列挙するとこんな感じでしょうか。

生活が自然に朝型になる

 以前、朝型生活を意識しようとしてなかなか出来ませんでしたが、ほとんど毎日のようにウォーキングしていたら、自然と朝型生活になっていました。1月は、午前3時くらいまで起きていたい気分になる事が多かったのですが、今は、24時になると「あ、そろそろ寝る準備しないとなー」と自然と思考が切り替わる感じです。朝型生活を意識しようとしても出来ない人は(というのがまさに自分だったのですが)、歩く習慣から初めてみるといいのかもしれません。とはいえ、これが何ヶ月前からの蓄積か不明なので、すぐ成果が出るとは限らないですけど。

気分の変動幅が減る

 1月は家庭の事情が色々あり、あと、2月上旬もちょっと色々あったのですが、ここ1週間くらいは、割と日中も夜も含め、気分が穏やかで過ごせる日が明らかに増えました(というか、それ以前がそうでなかったので、冬季うつに近い状態だったのかも)。まあ、寝られても熟睡感が薄い日とかは時々あるんですが、そういうときでも「朝起きたら歩く」を実践してると、歩いた後には、割とへっちゃらになってたりします(もちろん、本当に睡眠不足の日は、結局、夜に疲労が押し寄せて来ますが)。

仕事やその他の活動のパフォーマンスが上がる(超主観的)

 1時間ウォーキングに費やすのは、クールダウンやお風呂タイムを含めると、1時間30くらいはそれにかけてる計算になりますけど、トータルで見ると日中の集中力はどう考えても上がってるし、「だるいから、この仕事、明日に回そう」と考えることが減ってて、トータルで見ると一日でこなせる仕事量は明らかに増えてます。

SNSをあんまり見なくなった

 これは気分良く過ごせる日が増えたのと関係してるのかもしれませんが、必要な情報を探す時はともかくとして、あんまりツイッター見てもしゃあないなあという気分に最近はなってて、それによって、ツイッターで変に暗い気分にならないという好循環が生まれてる感じです。だんだん、ツイッターに費やす時間は減っていると思うのですが、一方で「仲良く交流するツール」というツイッターの原初に立ち返った使い方が出来るようになったように感じています。

 というわけで、ウォーキングをひたすら繰り返している内に起きた変化について、ざっと書いてみました。1時間ってのは、人によってはやりすぎかもですが、(特に冬場は)歩いて悪いことはないと思うので、歩けてないなーって人は少しずつ歩いていくといい気がします。「運動しなきゃ……でも、運動出来てないな」って人の参考になればと思います。

 冬場は寒いので、屋外での運動はしづらいと思うのですが、朝方に氷点下行くような地域はともかくとして、そうでなければ、ウォーキング前にお風呂に入る、ご飯食べる、などなどして、身体の暖機運転を開始してから始めると、スムーズにウォーキング出来るってのが個人的な実感です。

最近のウォーキング

 去年の春ごろに、

kmizu.hatenablog.com

 なんてエントリを書いたりしましたが、それから約半年程度経ってどうなったかというと……とりあえず、習慣として歩くのが定着するようになりました。

 さすがに、毎日必ずウォーキングをする(例外は認めない)の部分は無理がありましたが、最近の実績で言えば、少ない時でも週に1回、多い時は週に4回程度は程度は歩くようになった感じです。

 特に冬場になってからというもの、日射量が減っているということもあるので、天気のいい日は出来るだけ歩くようにしています。やっぱり人間も動物というか、きちんと日の光を浴びて運動しないと不健康になるな、というのを最近実感しています。

ジョグ&ウォーク記録(2020/10/21)

今日は、目標として - いつもと全く違うコースを走る - ジョギングのみ(歩かない)

というのを掲げて走ってみましたが、気がついたらあっちこっち走ってて、10kmいってました。後半は完全にランナーズハイの勢いで走ってました。明日はさすがに足を休めようかなと思います。

ちょっと無茶だったと思うのですが(まだまだ運動不足なので)、おかげで、ジョギング時の姿勢や走法、呼吸の重要さを思い出した気がします。あと、運動を終えたあとの充実感もいつもと段違いですね(ランナーズハイの影響かも)。