kmizuの日記

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

大学の時に苦手/得意だった講義

 そろそろいい歳したおっさんになった私としては、たまにはこういう思い出話を書くのもいいかなと。あと、なんか私がCSわかってる人みたいに思ってる人Twitterのフォロワーさんに意外と多そうな気がしますが、そうでもないですよ、みたいな話としても、

苦手だった科目ベスト5

1位:解析学

今ではそうでもないですが、大学の時滅茶苦茶苦手でした。どれくらい苦手かと言うと、

  • 一年の時に解析学ⅠとⅡとⅢの単位を落とし
  • 二年の時に解析学Ⅱの単位を取得し
  • 三年の時に解析学Ⅲの単位を取得し
  • 四年の時に解析学Ⅰの単位を取得し

たくらいには苦手です。今もって、なんで最後に解析学Ⅰ、つまり一番基本的なところの単位取得してるのかさっぱりわからないのですが(単に不真面目だったせいかもしれない)、ともあれ、苦手意識を植え付けた一番大きな原因は間違いなくε-δ論法です。先に論理における量化子(∀と∃)を教えてくれればここまで手こずらなかった気がするんですが、というのは逆恨みですが。しかし、私と同じようにAC入試で入った組は総じて、離散値を扱うのは得意でも連続値を扱うのは不得意だった傾向がある気がします。

プログラマでも同じように離散値扱うのは得意でも……という人は多そうな気がしますが、一体なんでなんでしょうね?加算無限は簡単に扱えても、非加算無限はどうにも苦手みたいな感覚とか。

2位:自然言語処理

特に自然言語処理の授業は実習がセットであって、しかもその点数を競い合うみたいな奴だったのですが、私が作ったモデルはどうにも過学習になりがちで、たしか全然高得点残せませんでした。苦手とはいえ、一応実習あり、つまりプログラム書く部分があって救われた感があります。

3位:論理と形式化

平たく言うと、自然演繹による証明法を習う授業です。これは不思議なのですが、今は割と自分の武器として普通に使えるものの、当時はめちゃくちゃ苦手でした。証明図とかもわかるようでわからんみたいな。確か単位は取れたと記憶しているのですが。

4位:関係代数

これも今となってはなんで苦戦してたのかさっぱりわからないのですが、正規形とかタプルとかあんましよくわかってなかった感が強いです。当時はデータベースにあんまり興味がなかったせいでしょうか。

5位:電磁気学

割と大学時代のトラウマになった授業の一つでもあります。今やりなおせばそうでもない気がするんですが、ともあれ、大学生の時は全般的に連続値を扱う学問が苦手だったのは確かです。

得意だった科目ベスト5

大体、苦手科目のところに書いた「離散値は扱えるけど、連続値を扱うのが苦手」みたいな傾向がそっくり現れた感じですが。

1位:データ構造とアルゴリズム

この辺は大学入る前に自習始めてたというのもありますが、あんまり苦労した覚えがありません。実習でもさっさと終わらせた余裕ぶっこいてた気がします。

2位:プログラミング言語関係の講義全般

JavaでもCでもプログラミング言語概論的なのもそうですが、ほぼ勉強した覚えがないくらいには得意でした。確か、プログラミング言語概論なんかは、期末試験に寝坊して「やばっ」と慌てて連絡したものの、「試験に寝坊したのが悪い」というど正論を担当の教員に突きつけられ、あー、単位落としたかなと思っていたのですが、それまでのレポートとか中間試験の成績だけで、一応Cは来てました(A/B/Cまでが単位取得)。

3位:システムプログラミング

システムコール使って、適当にプログラム書く系の授業ですね。なんか、当時の先輩に相談されたことがあった気がしますが、write/readとかのシステムコール適当に呼び出すだけで良かった記憶です。

4位:オートマトン形式言語

この辺はどう考えても今に繋がってるなーと思うのですが、一つには当時の担当教員がそもそもこっち方面専門だったのでわかりやすい講義を受けられたのも大きい気がします。さすがに形式言語専門の人に比べれば全然ですが、まーなんか言語階層の話をおおざっぱにつかめたのは良かったなーと感じてます。あと、この講義を受けてたおかげで大学院で一応まがりなりにも研究出来た気がします。

5位:コンパイラ系の講義

なんか、講義を無視して自分で一から処理系書き直してました。確か、tiny-cという名前のCのすっごい小さいサブセットを作る講義だったのですが、lex/yacc使うのが個人的に気に食わなくて、担当教員に「JavaCCとか使っていいですか?」と聞いて、OKもらったのでJavaで書き直した思い出。

というわけで、とりとめもなく、適当に苦手だった講義と(比較的)得意だった講義とか書いてみました。もうちょっと勤勉な学生だったら良かったのではと時々後悔することがあります(特に、解析学とか不真面目過ぎたし、自然言語処理ももうちょっと真面目にやれば良かった)。

補足:「世の中に一言言ってやりたい」病

 本日の記事、

kmizu.hatenablog.com

 ですが予想外に大きな反響があったようで、Twitterあるいははてなブックマークで賛同あるいは懐疑的な意見をいくつかいただきました。元々は上記記事は、私自身のここ数年の振る舞いを振り返って、「私はちょくちょく世の中の問題にツイートしてたけど、それがしばしば多数Likeされたりして、何かいい気になってるんじゃないの?」という自己批判が元になって書かれたものです。

 ただ、同じような心情に陥った人はそこそこいるんじゃないかと思って、その心情を「世の中に一言言ってやりたい」病として表現したのでした。

 全体的には趣旨を汲んでくださった反応が多いものの、一部、記事の趣旨が誤解されているなというものがあったのでここで補足させていただきます。反応については、原文ママにしますが、特に攻撃的な意図があるわけではないので、気を悪くされないでいただければありがたいです。

難しい話し。ある人にとってはそれが他愛もない一言であるものの、別な人にとってはそれがとても看過できない一言である事もある。結局昔の雰囲気というのは参加者が限定されている事により醸成されたものに過ぎない

 この点については最後に初期Twitterへの懐古みたいなものを書いたのが良くなかったかなと。この意見は、どんなに配慮しても他の人にとってはそれが気に障ることがあるというコミュニケーション一般の問題について論じたものかと思うのですが、そもそも上記記事ではそのような問題を論じていません。

 もし、「世の中に一言言ってやりたい」という欲望に振り回されているなら、すぐにレスしたいという衝動を抑えて、本当に(あえて)書くべきことかを少し時間をとって考えてみるのはどうだろうか、という提案です。「世の中に物を言うな」がポイントではなくて、「世の中に一言言ってやりたい」という欲望に基づいて、「即」レスしたくなる衝動について書いたというのがポイントです。Twitterで「世の中に物を言う」ことそのものを問題視したエントリではないのです。

居酒屋でおだをあげてるよりは、世の中に対する影響があるんじゃないの。

 これはむしろ私が危惧しているタイプの意見で、「Twitterで何事か「世の中に物を言う」」ことは、内輪で愚痴ってるよりいくらかマシという事かと思いますが、私は内輪で愚痴ってるレベルのを「世の中に影響があるんじゃないの」という意識で書き続けるのは、「ツイートするだけで何かをした気になる」という(ほとんどの場合誤りである)意識を定着させて、ひいてはそういう行為が癖になってしまうのでは、と思っています。これ、ある種の中毒症状で本人には自覚がないことが性質が悪いのだと感じています(繰り返しますが、これは自己批判でもあります)。むしろ、内容のレベルによっては居酒屋でおだをあげてる方がマシ、とすら思っています。

あまり同意しないなあ。民主制社会の市民である以上意見表明それ自体は基本的に結構なことであるはず。それで社会運動をしたというような過剰な自負を持つようなことがなければ良いのでは。

 申し訳ないですが、これは典型的な誤読かと。本文でも書いていますが、意見表明そのものは全く掣肘していません。最初の段落でも、「もうちょっと考えをまとめるのが普通だった」という記事がある通り、意見表明は基本的に結構であるという点で全く同意見です。「社会運動をしたというような過剰な自負を持つようなことがなければ」がポイントで、私を含め、多くの人は、社会運動をしたとまではたぶん思っていないでしょう。しかし、それでも、「いくらかは良いことをしている」と無意識に考えてる人は多くいるのではと感じています。しかし、その前提は疑った方がいいよね、と。

上の階層だけで話が終始しそうだと、下流から下から目線で一刺ししたくなる。病だと罵られようが、やめるつもりはない。

 最終的には当然ながら個人の自由だと思いますが、「下流から下から目線で一刺し」が癖になって、中毒に陥ってないかというのはチェックした方がいいのかなと。

 いずれにしても、「自分はそういう衝動に突き動かされていたわけじゃない」と自信が持てるならきっと大丈夫だと思いますし、「Twitterでの意見表明そのものが良くない」と取れることを書いたのは失敗だったかもしれません。

「世の中に一言言ってやりたい」病

 おはようございます。これまでの自分を振り返って、あるいはネット言説を見て、ふと思ったのですよ。なんか、ネットに流れる記事に即レスで「一言言ってやりたい」人が多すぎませんか?と。これは思いっきり私自身を刺す言葉でもあります。たぶんTwitterや雑誌記事のコメント欄をはじめとして短文で気軽に意見を言えるようになった副作用だと思うのですが、以前のブログ主流(あるいはWeb日記主流)時代だと、思考をきちんとまとめる必要があるので「世の中に一言言う」にももうちょっと考えをまとめるのが普通だったと思うのです(これは過去を美化しているかも)。

 政治に対して、Twitter上に飛び交う言説に対して、その他色々についてひとこと言いたい。あるいは、愚かしい(と主観的に判断した)言説にため息をつきたくなる気持ちも、行政の不甲斐ない対応に憤る気持ちも、腐ったシステムへの苦情も。今の自分にもそういう気持ちがあるから凄くわかるのですが、ちょっとセーブしてみてもいいんじゃないかなと。あまりにTwitterでお気軽に「世の中に一言言う」癖がつくと、でもって、それがLikeされたりRTされたりすると、「一言言う自分」が肯定された気がして、ますます一言言いたくなるというループに陥りがちな気がしています。

 じゃあ、だからどうするかというと難しいところですけど、「一言言う」前に「これ、わざわざつぶやく意味ある?」と多少検討する時間をおいてもいいんじゃないかな?と。検討する時間をおいても、なお言った方がいいことはあると思えば言えばいいですけど、タイムリーに「一言言ってやりたい」欲求をセーブするのは、精神衛生上も、Twitter言論空間をより平和にするためにも重要なんじゃないかと最近は真剣に思っています。そもそも、Twitterは文字数制限がある上に、現状のUIだと議論の流れが凄くわかりにくいので根本的に立ち位置が異なる人たちと議論するのに向いてない(というのは私自身も含め、以前からわかってたと思いますが)わけで、不毛に時間を消耗するのもどうなのだろうかと思うのですよ。

 あと、別の観点として、凄く頭のいい人たちがツイッターで消耗している時間を考えると、トータルで見てもよろしくないのでは?とも思います。

 原初のなうなう言ってたTwitter黎明期に戻って、もっとくだらない、毒にも薬にもならない事を共有しあうのがメインの場でいいんじゃないかなーと。そう思った2021年9月2日(木)の朝でした。

エアリプをやめよう

 こんばんは、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のような形で「より簡単に書ける」は真かもしれませんが)。