octahedron

LemとSKKとCommon Lispでたたかうプログラマのブログ

Hysteresis --- 値の歴史を遺す

 この記事はNextremer Advent Calendar 2019の25日目の記事です。ハッピーニューイヤー!(開き直り)

Hysteresis

 2019年の暮れに、ひとつCommon Lispのライブラリをつくりました。これは、このライブラリのもつ値設定マクロsetfを通してシンボルに値を設定するとそのシンボルの値の履歴が残るようになる、というものです。

github.com

動機

 ゲーム(cl-sdl2)とかジェネラティブアート(sketch)とかをCommon Lispでやっていると、Common Lisp単体では画面への描画ができないためCFFIを通して描画スレッドにコールバック関数を渡してやることがよくあります。REPLからコールバック関数を変更できたりして便利なのですが、lambda式なんかを渡していると、エラー時に差し替えようがなくてけっきょくCL処理系のスレッドごと落としてロードしなおすことになります。あまりREPL駆動のよさみを受けていない。

 この問題の解決方法のひとつとして、名前 (Common Lispのbindings)を通じて関数を渡すようにすればいいなあーと2019年12月ごろにおもいつきました。そのついでに、エラーが起こった瞬間に値を巻き戻す的な操作があると便利かもしれない、と思ったため実装しました。

 想定利用ケースとして以下のようなものを想定しています:

  • おもむろにシンボルの束縛していた過去の値にもどしたい
  • エラー時、自動で1個前の値にもどしたい

概要

 Hysteresisではシンボルに履歴というものを導入します。この履歴つきシンボルをhistorized symbolsとこのライブラリでは呼ぶことにしました(historizedって「歴史に名を残す」という意味らしいですがライブラリの内容的に洒落っぽくていいなと思いました)。まあ履歴つきシンボルとかおおげさな胡椒ですが、専用の構造体にリングバッファがついていて、リングバッファの最大長以内であれば値が保持されるシンボルがある、というていどのものです。

使いかたの基本

値の設定と取得はhysteresis:set-valuehysteresis:get-valueを用います。シンボルをキーとして内部のハッシュテーブルに保持された値履歴に書き込んだり取得したりします。

CL-USER> (hysteresis:set-value 'foo 42)
42
#(HYSTERESIS:HISTORIZED-SYMBOL ...)
CL-USER> (hysteresis:get-value 'foo)
42

でもこれは内部API的であまりユーザに利用されることはあまり想定していません。hysteresis:hsetqというsetqもどきを通じて、シンボルマクロを定義するのが便利です。利用感はこんなかんじです。

CL-USER> (hysteresis:hsetq hoge 42)
42
CL-USER> (setf hoge 42)
42
CL-USER> hoge
42

歴史を戻したり現在に戻ったりする

 履歴つきシンボルは、もちろん過去の値に遡ってシンボルの値を戻すことができます。まずは値を設定しておきます。

;; 履歴に値を設定する (きっとミュージックプレイヤーの現在の曲とかで利用している?)
CL-USER> (hysteresis:setq song "Walking In The Sun")
"Walking In The Sun"
CL-USER> (hysteresis:setq song "My Hero")
"My Hero"
CL-USER> (hysteresis:setq song "Half The Man")
"Half The Man"

このとき、現在の値は"Half The Man"です。これを唐突に2つ前の値"Walking In The Sun"に戻したいと思いました。こんなときhysteresis:revertを用いると、全ての履歴つきシンボルの値を2つもどすことができます。

CL-USER> song
"Half The Man"
CL-USER> (hysteresis:revert 2)
NIL
CL-USER> song
"Walking In The Sun"

わーお。でも、やっぱり最新の値に全てもどしたい場合もありますよね(たとえば過去の値もヤバかった的な)。そんなときはhystresis:presentを用いると、最新の(現在の)値に戻ります。

CL-USER> (hysteresis:present)
NIL
CL-USER> @song
"Half The Man"

値ったら最強ね(9)。

履歴つき関数

さて、冒頭ではゲームとかでコールバック関数がうんぬんという話がでてきました。もちろんこのライブラリでは関数も履歴つきにすることができます。hysteresis:hdefunの登場です。

CL-USER> (hysteresis:hdefun foo (x)
           x)
#<COMPILED-LEXICAL-CLOSURE #x302000DB1C8F>
CL-USER> (foo 42)
42
CL-USER> (foo 'hoge)
HOGE
CL-USER> (foo "wow")
"wow"

そしてもちろん履歴を辿りたい場合がある(はず)です。そんなときにはhysteresis:revertを用いることができます。

CL-USER> (hysteresis:hdefun foo (x)
           (list x x))
#<COMPILED-LEXICAL-CLOSURE #x302000E7309F>
CL-USER> (hysteresis:hdefun foo (x)
           (format nil "+~a+" x))
#<COMPILED-LEXICAL-CLOSURE #x302000ED2D6F>
CL-USER> (foo 'hoge)
"+HOGE+"
CL-USER> (hysteresis:revert 1)
NIL
CL-USER> (foo 'hoge)
(HOGE HOGE)

 こんな感じで、履歴を遡ったり現在側にもどってきたりできます。機能としてはとても単純なので、このような感じです。

 改良できそうな部分は多々あれど、これは使えるライブラリなのかしら…?? …うーん、なぞ。

今後の展望

  • hysteresis:hunintern
    • 通常のcl:uninternすると、内部ハッシュテーブルから値が消えないため
  • テストを書きたい
    • リングバッファが手書きなので、まじでみっちりと!!
  • REPL操作を意識した情報取得や一覧表示?
    • 履歴の内容確認
    • 履歴つきシンボル一覧
  • 実用する(ゲームとかで?)

まとめ

シンボルに履歴を持たせるライブラリをつくりました。しかし、このアイデアに需要があるのか、ほんとうに問題(動機)の解になっているのか、などが謎なのでこれから実用してみて判断を下す段階のものだと思います。

CORBIT/BOOPS: プロトタイプベースオブジェクト指向を実現する拡張ライブラリ

 ひょんなことから見つけた*1のでちょっと遊んでみました。

CORBITとBOOPSについて

 CORBIT (CommonORBIT) はCommon Lisp用のプロトタイプベースのオブジェクト指向ライブラリです。

Package: lang/lisp/oop/non_clos/corbit/

 どうも、Orbitというプロトタイプベースのオブジェクト指向のシステムがあり*2、それをCommon Lisp向けに移植・再実装したものがCORBITであるようです。また、BOOPS (Beginner's Object-Oriented Programming System) はふるまいや仕組みが初学者にもわかりやすいようCORBITのコア要素を抽出し、小さくしたものです。

 CLOSと異なる点は、CLOSがクラスベースのオブジェクトシステムであるのに対し、CORBIT/BOOPSはプロトタイプベースのオブジェクトシステムであるところです。クラスベースのオブジェクトシステムでは各オブジェクトがクラスに属し、多態性が必要な際はクラス間の関係に従ってふるまいを決定します。この「クラス」は、実行時には変更できない言語(JavaとかC++とか)と実行時に変更できる言語(SmalltalkCommon Lisp)がありますが、いずれせよある時点ではなにか「クラス」に属している点では共通しています。一方プロトタイプベースのオブジェクトシステムでは、あるオブジェクトが多態的にふるまう際に「オブジェクトの属するクラス」ではなく、関係する別のオブジェクトに処理を移譲する点が異なります。 (え、これ、あってるのかしら…。書いてみたら理解が雑だった…。)

 現在のCLOSになるまでにいくつかのオブジェクト指向実装があったというのは聞いていましたが、CLOSっぽいものだけでなく、JavaScriptのようなプロトタイプベースのオブジェクト指向システムも存在したんですね。

 ちなみにCommon Lispに詳しくない人のために説明すると、CORBITはCommon LispANSI仕様に含まれているCLOS (Common Lisp Object System)とは別物です。CORBITはCLOSを用いずライブラリとして実現されています。

BOOPS使ってみる

 BOOPSは1ファイルにパッケージが1つ定義されているだけのとても小さな実装です。使うにはお手元のCommon Lisp処理系でファイルをロードするだけで済みます。BOOPSパッケージ内で作業すると若干楽です。

CL-USER> (in-package :boops)
#<Package "BOOPS">

 BOOPSではオブジェクトはシンボルで表現し、アスペクト (aspect; 側面? CLOSでいうスロット、Javaとかだとメンバ/フィールド)をリストで表現します。アスペクトには:value:functionの種別があり、:functionだとオブジェクトが引数に渡されてきます。

 まずはとりあえず、哺乳類をいくつか定義して鳴かせてみます。

;; 哺乳類を定義 (鳴くコマンドつき)
BOOPS> (defobject marmal object
           (toot :function (o) (format t "toot!~%")))
;Compiler warnings :
;   In an anonymous lambda form: Unused lexical variable O
MARMAL

;; わんこを定義 (鳴くコマンド上書き)
BOOPS> (defobject doggo marmal
         (toot :function (o)
               (format t "bow wow!~%")))
;Compiler warnings :
;   In an anonymous lambda form: Unused lexical variable O
DOGGO

;; 鳴く
BOOPS> (message 'marmal 'toot)
toot!
NIL
BOOPS> (message 'doggo 'toot)
bow wow!
NIL

 継承できた!

 次に、アドホックにメソッドを追加してみます。これには匿名オブジェクト(というかgensym)を作ります。このように。

;; 匿名オブジェクトとしてジョンを定義しアスペクトを追加を追加
BOOPS> (let ((john (a doggo (introduce :function (o)
                                       (format t "I'm John! ")
                                       (message o 'toot)))))
         (message john 'introduce))
I'm John! bow wow!
NIL

 静的なクラスに依存しないので、このようなこともできます(ただしCLOSは静的ではないので同じようなことできそう)。

BOOPS> (defobject human marmal
         (toot :function (o)
               (format t "hello!~%")))
;Compiler warnings :
;   In an anonymous lambda form: Unused lexical variable O
HUMAN
BOOPS> (let ((tom (a human (introduce :function (o)
                                      (format t "I'm Tom! ")
                                      (message o 'toot)))))
         (message tom 'introduce))
I'm Tom! hello!
NIL

 これでアドホックなインターフェースっぽいサムシングが実現できました。

中身について

 だいぶシンプルです。BOOPSではオブジェクトはシンボルだと言いました。BOOPSの内部的には、オブジェクトのアスペクトは、シンボルのpslitスロットにplistの形で入っています。

;; BOOPSで定義されているオブジェクトのアスペクト
BOOPS> (symbol-plist 'object)
(ASPECTS ((SET-VALUE :FUNCTION . #<Anonymous Function #x302000C8FF7F>) (SHOW :FUNCTION . #<Anonymous Function #x302000C900DF>)) ISA NIL)

;; わんこのアスペクト
BOOPS> (symbol-plist 'doggo)
(ASPECTS ((TOOT :FUNCTION . #<Anonymous Function #x302000C9835F>)) ISA MARMAL)
BOOPS>

 匿名オブジェクトもgensymであるただのシンボルなのでplistスロットがあり、おなじようにアスペクトが入っています。

BOOPS> (let ((tom (a human (introduce :function (o)
                                      (format t "I'm Tom! ")
                                      (message o 'toot)))))
         (symbol-plist tom))
(ASPECTS ((INTRODUCE :FUNCTION . #<Anonymous Function #x302000E3B3AF>)) ISA HUMAN)

 おしまい。

*1:Common Lisp Musicについて調べていたら"Compact LISP Machine"なる論文を見つけ、論文に出てくるTexas InstrumentsExplorerというLISPマシンを調べていたらCMUアーカイブに流れ着き、そこを眺めていたらあった。

*2:論文情報はこちら(読めぬ): https://ai.vub.ac.be/publications/236 。著者の方はGuy. L. SteeleさんではなくファーストネームがSteelsさん。

Linux版OneShotの終盤でファイルが開けない(ネタバレ注意)

 この記事は以下の記事の続きです。

octahedron.hatenablog.jp

 上の記事の内容も利用しているので、適宜参照ください。

 ストーリー終盤のギミックについての不具合対処記事なのでネタバレを含みます。ご注意を!!

続きを読む

OneShot (ゲーム)をUbuntu 18.04で動かす

あらまし

 先日、チャンスは一度きりなゲームOneShotGNU/Linux版がリリースされました。ふだんUbuntuのLTS版を利用しているので、いままでWindows 10に切り替えなければ遊べなかったOneShotをいつもの環境で起動できるのはとてもうれしいです。

steamcommunity.com

 しかし要求してくるライブラリのバージョンがとても新しく、LTS版では起動できませんでした。この記事ではそれをなんとかしてみます。

発生現象

 Steam GNU/Linux版でOneShotをダウンロード後に起動しようとすると起動中っぽい動きを一瞬したあとそのままなにも起こりません。これはプログラムの起動に失敗して無言で落ちているためであり、ターミナルで直に起動しようとしてみると以下のような出力が得られます。

# OneShotがインストールされたディレクトリに移動
$ cd ~/.local/share/Steam/steamapps/common/OneShot
# 起動 (失敗)
$ ./oneshot
./oneshot: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /home/grey/.local/share/Steam/steamapps/common/OneShot/libgio-2.0.so.0)
./oneshot: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /home/grey/.local/share/Steam/steamapps/common/OneShot/libglib-2.0.so.0)
./oneshot: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /home/grey/.local/share/Steam/steamapps/common/OneShot/libsystemd.so.0)

 これは、システムにインストールされたglibcライブラリのバージョンが古いと言っています。システムの情報とglibcのバージョンを確認してみると、以下のようでした。

# 環境情報の確認
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"
grey@timberwolf:~$ /lib/x86_64-linux-gnu/libc
libc-2.27.so  libc.so.6

# glibcのバージョン確認
$ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

 おしい! 1つだけ足りない! それにしてもglibclsといった各コマンド自体も依存している動的ライブラリですので、ディストリビューションが提供するアップデートなしには容易に差し替えられません。ちなみに「OneShot用のglibcコンパイルしてゲーム本体のディレクトリに置けばいいじゃん」とやってみたところダメでした。差し替えたglibcを向くようにLD_PRELOAD環境変数で指定しても.so中のオブジェクト等の位置が異なるのかセグフォになります。むずかしいですね。

対処方法

 このスレッドにも書かれているのですが、glibcを差し替えて解決するのではなく新しいglibcに依存しているライブラリを使わないようにするのが楽かつスマートです。

steamcommunity.com

 つまり、OneShotディレクトリにある新しいglibcに依存する.soは削除もしくはリネームしてしまい、システムにapt等で対応するライブラリを入れましょう。

$ cd ~/.local/share/Steam/steamapps/common/OneShot
$ rm libgio-2.0.so.0 libglib-2.0.so.0 libsystemd.so.0

 これでglibcのエラーはでなくなります。

そのあと

 ぼくの環境では、この後Error initializing SDL: Could not initialize UDEVというエラーがでてまだ起動できませんでした。udevとはユーザ空間でデバイスドライバを動的にロードしたりするライブラリっぽいのですが、きっとシステムのものではないudevを用いているせいかもしれない(libudev.so.1が実際ある)のでこれも消してしまいましょう。

 そうするとエラーの内容がCould not initialize Steamworks APIに変わります。きっとStreamが起動していて認証されてないと動かないのでしょうね。そこで、今まで./oneshotを起動していたのですが同じディレクトリにある./steamshimを起動してみましょう。Steamへの認証っぽいログがでたあと…。

f:id:t-sin:20190502000343p:plain
OneShot起動成功!

 やったー!!

 あとは遊びましょう。

Clozure CLでプロファイラを利用する

2019-05-02 19:51追記

プロファイル取得用のイメージを作成する手順がありますが手順を間違えると処理の実行のたびにfaslのダンプが行われて計測結果を汚すので注意してください。イメージ作成手順にも追記します。

TL;DR

 Clozure CLの以下ドキュメントに書いてある通りです(本記事では素のCCLではなくRoswell上での利用方法が書かれています)。

ccl.clozure.com

動機

 PortAudio/cl-portaudioを用いてCommon Lispサウンドプログラミング実験をしています(どんなことをしているのかはざっくりこちらのスライド参照。いつか記事かなにかにしたい)。

github.com

 ある程度音が鳴らせるようになってきたので、ちょっと複雑な音を鳴らしてみようと思いました。が、プログラムの実行速度が遅いのかバッファアンダーランが発生して音がぶちぶちに切れてしまい、これでは音楽つくるどころではありません。しかしここまで大きく育ったプログラムのどこがボトルネックか、ぼくにはパッとわかりませんでした。

 なのでプロファイラを用いてどの処理が重いのかとても知りたいです。

 ちなみに普段使っている処理系はClozure CLなので、SBCLに組込みのプロファイラは使えません(さらに言うと何かがおかしいのかpukunuiをSBCLでロードすると型エラーになったような気がする)。

環境や準備

環境

使うもの

手順

 ここではサンプル用のコードを説明用に用意せず、pukunuiをそのまま使って手順を説明します。しかし使うだけならそんなに難しくないはず…。

pukunui関係準備

 Pukunuiではcl-portaudioを通してlibportaudio2を利用しますので、aptでインストールしてください。Pukunui本体も.roswell/local-projectsとかに置いている前提です。

プロファイルを取るための準備

 Oprofileでは起動前・起動後の対象を指定してプロファイラを起動し、その実行中の情報をOSを介して取得するしくみです。対象に指定できるのは以下です:

  • システム全体
  • 指定したpidのプロセス
  • 引数に指定した実行可能ファイル(とその引数)を起動したプロセス

 今回はpidを指定してプロファイルを取得します。まずCCLでしておくべきことは

  1. プロセス内のシンボル一覧をファイルに保存
  2. 上記シンボル一覧と関数が対応する処理系のイメージを作成

です。ros use ccl-binが済んでいれば簡単なのでやってみましょう。

$ ros run
Clozure Common Lisp Version 1.11.5/v1.11.5  (LinuxX8664)

For more information about CCL, please see http://ccl.clozure.com.

CCL is free software.  It is distributed under the terms of the Apache
Licence, Version 2.0.

;; シンボルテーブルを保存
? (require :elf)  ; 必要なパッケージをインポート
ELF
? (ccl::write-elf-symbols-to-file "elf-symbols")  ; `elf-symbols`にシンボルが書き出される

;; `elf-symbols`に関数が対応した処理系イメージを作成し終了
? (save-application "pukunui-prof-image" :prepend-kernel "elf-symbols")
$ 

2019-05-02 19:54追記

ちなみにライブラリのasdf:load-systemや計測に必要なLispプログラムのloadccl:save-application前にやっておきましょう。「ライブラリロードはイメージ作成前に、プログラムのロードは計測直前に」などとするとClozure CLではロードしたプログラムのfaslへのダンプ処理が計測対象実行の度に走るようです。これによりCCL::FASL-DUMP実際のコードでは使っていない処理が計測結果に登場してプロファイリングを困難にします。ご注意ください。

プロファイルを取る

 ここからはターミナルを複数立ち上げて作業します。

 まずCCLの処理系を先程のイメージで起動します。Roswell越しにCCLにオプションを渡し、イメージを指定します。その後、プロファイルを取得する前の準備(プログラムのロードや初期化)をします。 2019-05-02 19:54追記 プログラムのロード等はイメージ作成前に行ってください

$ ros run -- -I ./pukunui-prof-image
...

;; プログラムの初期化 (ここではpukunuiのロードと音を鳴らすプログラムの準備)
? (ql:quickload :pukunui)
PUKUNUI
? (load (asdf:system-related-pathname :pukunui "examples/04-seq.lisp"))
#P"pukunui/examples/04-seq.lisp"

 これでpukunuiの初期化は完了です。スタートする前に、プロファイラを別ターミナルで起動しておきます。

$ ps aux | grep lx86cl64   # 上で起動したCCLのpidを調べる
$ sudo operf --pid xxxx
...

 プロファイルを取得するためにCCLのREPLで(pukunui:start)を打ちます。カービィの音が鳴り終わったら、プロファイラのほうでCtrl-xを押してプロファイラを止めます。プロファイラを止めるとoprofile_dataというディレクトリができており、そこにプロファイル結果が入っています。サマリを見てみましょう。このとき、実行したCCLのイメージを指定すると内部のシンボル名が何回呼ばれたかが表示されます。

$ opreport -l pukunui-prof-image  | head
Using /home/grey/Dropbox/code/pukunui/examples/oprofile_data/samples/ for samples directory.

WARNING! Some of the events were throttled. Throttling occurs when
the initial sample rate is too high, causing an excessive number of
interrupts.  Decrease the sampling frequency. Check the directory
/home/grey/Dropbox/code/pukunui/examples/oprofile_data/samples/current/stats/throttled
for the throttled event names.


WARNING: Lost samples detected! See /home/grey/Dropbox/code/pukunui/examples/oprofile_data/samples/operf.log for details.
CPU: Intel Ivy Bridge microarchitecture, speed 3900 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 100000
samples  %        image name               symbol name
4434      7.9773  pukunui-image            CCL::FASL-DUMP-EPUSH
4358      7.8405  pukunui-image            RANDOM
2882      5.1850  pukunui-image            CCL::FASL-DUMP-DISPATCH
2780      5.0015  pukunui-image            CCL::%BIGNUM-RANDOM
2553      4.5931  pukunui-image            CCL::%STRING-HASH-FOLDING-CASE
1948      3.5047  pukunui-image            <Compiled-function.(:INTERNAL.CCL::GET-LOCAL-VALUE.CCL::ARGUMENTS-AND-LOCALS).(Non-Global)..0x300000A0412F>
1815      3.2654  pukunui-image            FLOAT-SIGN

 なにか警告がでていますが、全イベントが取得できなかったみたいです。まだよくわかってないですが、改善の余地があるということなのでしょう。

所感

 プロファイルを取得するときは本当に計測したい部分でのみプロファイラを起動(=プロファイルデータを集める)しましょう。pukunuiは例えば信号処理プログラムですが、最初はライブラリの初期化を含めて計測してしまったため、信号処理には関係ない(本来の計測対象ではない)処理が上位に出てきてしまいます。

 結果を見ている感じ、Pukunuiに関してはたぶん自前実装オブジェクトシステムもどきのあたりがボトルネックになってそうでした(ccl:%string-hash-folding-caseとかあるので。自前オブジェクトは型とメソッドの対応表がハッシュテーブルに入っている)。あとRANDOMってなんじゃい(上のプロファイル結果のプログラム 06-applications.lispでは使っていない)。Pukunuiが変な挙動をしているのかもしれません。

 やはりKiwi Lightweight Object Systemが必要か…。

所感追記 (2019-05-02 19:54)

 上のプロファイル結果にCCL::FASL-DUMP-EPUSHなどが現れていますが、これはpukunuiのコードで行われている処理ではありません。上で述べたようにプロファイリング用イメージの作成手順を誤ると走ってしまうようです(詳細な原因は不明)。

 また、randomexamples/06-applications.lispを用いていれば登場しないはずですので、計測用イメージの作成ミスかもしれません。計測の準備は注意ぶかく行いましょう。

2018年振り返り

2018年振り返り

 今年はいろいろあったので、振り返りしてみようという気になった。触発されたというのもある。

働いた

 2017年末にはなんか落ち込みが発生して自信をかなり損なっていた時期があったけど、2018年1月からは働きはじめた。対話AIをやっている会社で、それまでSIer (Java)やWeb屋さん(Python)としてやってきたぼくとしては新たなチャレンジだった。とはいえWebまわりをやっているけれど、いろいろなことを学んだし、(Lisperなのに)(遅いけど)対話AI、というか人工知能に興味を持つことができた。神戸大学LISPマシンを見にいったときのことも大きなきっかけである(第7回関西Lispユーザ会に行ってきた)。

読書

 あんま読んでない…。スループットは悪く、年に24冊だった。内訳としては、小説多め。今年は『エコープラクシア』『最後にして最初のアイドル』『リスの生態学』『貨幣の新世界史』『エル・アレフ』『死刑 その哲学的考察』『ペンギンの島』『トランスヒューマンガンマ線バースト童話集』、などなど。SFは収穫多かった。就職祝い(セルフ)に買ったイーガンの直交三部作は本棚をしっかりと支えています。読みたい。あとは、後述するけど音声信号処理関連の読んだけど、まだわからん…。あと、ついに『LET OVER LAMBDA』を読んだよ!! マクロって書いていいんだね!!

太った

 なぜだかわからないけど太った。きっと運動を控えめに、食事を盛大に行ったためだと思われる。怖いので体重を計測していない。帰省しているので、戻ったらまずは計測して、そして、運動をしてやせたい。

プログラミング

 いくつかトピックがあるので分けて書く。

シンセサイザーづくり

 つくってみたいもののひとつだったので、えいやっとつくりはじめてみた。とりあえずノリで実装してみてたけど、平行して書籍を読むこともした。『サウンドプログラミング入門――音響合成の基本とC言語による実装』で基本を押さえ、『サウンドエフェクトのプログラミング―Cによる音の加工と音源合成』でエフェクトのつくりかたを…わからなかったけど…。音を鳴らすのは簡単(でもない)けど、シーケンサを生やすためのイベントスケジューラを書けなくて、実力の不足を実感した。シーケンサ、生えろ。

Nim -> Rust

 興味があったのでNimを去年からいじっていた。初めてのLispをつくってみたり、上のシンセサイザーつくりに利用してみたりした。ただ、なぜか処理系の不具合を踏み抜いたようで、シンセづくりが完全にストップしてしまったのであきらめた。そのままRustに入門して、絶賛勉強中。ちなみにシンセはCommon Lispでもつくりはじめてみた。シーケンサがやっぱり鬼門。

言語を処理でき…そう…?

 去年末はNimでLispをつくっていたけど、仕組みがわからないので引き続き勉強していた。パタヘネ読んだし、仮想機械のサーベイをしたり、実際にCPUエミュレータVMを実装してみたりした。そしてForthとの出会い。そのあたりはこちら『プログラミング言語Forthに魅せられて。』にしたためておいた。定数を扱える、規格の動作をすこしずつ再現できてきているので、もしかしたらいわゆる「ちゃんとした言語」というやつになるかもしれない。

来年は

 いろいろ結実して、生えてきてほしいなあ。

セルオートマトン暗号(未完)

遅刻してますが、この記事はセルオートマトンアドベントカレンダーの23日目の記事です。

あらまし

 離散的な力学系であるところのセルオートマトンは、カオス的なふるまいをすることで知られています。カオス的であるなら、そのふるまいを暗号学的なアレとかソレとかに利用できるのでは…、とはもう研究があるようですがここでは割愛。いっぽうで、セルオートマトンの中には可逆性をもつものがあります(ここらへんも研究がありますが割愛)。

 あとは…わかるね?

 ということでとりあえずElementary Cellular Automataを用いてガッとメッセージの暗号化・複合化を実装しようとしたけど3時間ではだめでした、という残念な記事です。ざんねん。

ソースコード

文字をつらつら書くほど内容はないのでさっさとソースコードを示します。ひさしぶりにビット演算をキメた気がする。以下のことはできています:

  • 局所遷移関数をルール番号から生成(コンパイル時に)
  • 周期的境界条件(キアイで)
  • なんとなく様相を指定回数ぶん遷移
  • ファイルをビットベクタにしてECAの様相に変換しブンまわす

gist.github.com

使用感

 なんとなくこんな感じになります。

CL-USER> (let ((vec (vector 0 0 0 1 0 0 0 1 0)))
           (run 10 vec (make-array (length vec))
                (make-local-transition-fn-from-rule 150)))
#*000100010
#*001110111
#*110100010
#*000110110
#*001000001
#*111100011
#*111010101
#*110010100
#*001110111
#*110100010
#(0 0 0 1 1 0 1 1 0)

 なんとなく大域遷移はできていそう。Roswellスクリプトにしておいたので、引数にファイル名を与えるとファイルの内容で遷移してくれます。しかしビットベクタをバイトベクタとして書き出してしまっているので出力はおかしいですがもうつかれました。

まとめ

 このセルオートマトンという分野、人工生命というだけではなく暗号学の方面の研究もあり、さらに情報の圧縮や、果ては様相の遷移を群と見なして解析する純粋数学的な分野もあり、とてもたのしそうです。やりたい。

参考文献