Common Lispでディストリビューションを調べる

Common Lispにはランタイムの環境情報を取ることできる。

基本的には *features* にリスト形式で入っており、OSごとに動作を変えるようなプログラムは#+win32 (some-function args ...) というノリで書く。

具体例として、ブラウザを開く関数定義として

(defparameter +format-string+
  #+(or win32 mswindows windows)
  "explorer ~S"
  #+(or macos darwin)
  "open ~S"
  #-(or win32 mswindows macos darwin windows)
  "xdg-open ~S")

(trivial-open-browserより拝借)

コードを見れば雰囲気は掴めると思うが一応説明しておくと、

  • #+hoge*features*hogeある場合に実行
  • #-hoge*features*hogeない場合に実行

という具合である。

ちなみに環境情報を取ってくる関数なども用意してあり、例えばOSやCPUのアーキテクチャなどを

* (software-type)
; => "Darwin" (macOSの場合)
; => "win32" (Windowsの場合)
; => "Linux" (Linuxの場合)

* (software-version)
; => "19.4.0"

* (machine-type)
; => "X86-64"

というような形で調べることができる。

実はちょっと問題がある

さて、上記のように見た感じだと基本的には問題が無さそうだが、実は一つ困ったことがある。

それは、OSがmacOSWindowsの場合は上記の通りであるが、Linuxの場合はLinuxと出力されることである。

(...別に問題ないのでは?)

まあそのような気持ちになるが、Linuxにも色々あるわけである。

そう、ディストリビューションである。

Ubuntuを始めとしたDebian系だったりCentOSを始めとしたRed Hat系と、まあ色々である。

とりあえずこの使っているディストリビューションが何系なのかをCommon Lispで確認しようと思う。

Common Lispからシェルコマンドを叩く

結論として、シェルコマンドを叩くことで解決する。

Linuxでは

$ cat /etc/*-release

=> 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

によって環境情報を取ってくることができる。

この出力の中で ID がどのディストリか、また ID_LIKE がどの系列のディストリであるかを示している。

ちなみにこの例の出力はUbuntuである(Dockerでやってみた)

さて、Common Lisp (SBCL)でシェルコマンドを叩くためには Quicklisp経由でtrivial-shell を使うか sb-ext:run-program を使う。

ここではtrivial-shellを用いる。

;; Quicklispでロード
(ql:quickload :trivial-shell)


;; 文字列を改行ごとに分割してリストへ格納する関数
(defun split (x str)
  (let ((pos (search x str))
        (size (length x)))
    (if pos
      (cons (subseq str 0 pos)
            (split x (subseq str (+ pos size))))
      (list str))))


;; trivial-shellでコマンドを簡略化したもの
(defun system (cmd-str)
  (trivial-shell:shell-command cmd-str))


;; OSがLiinuxである場合にディストリを取ってくる
(defun get-dist ()
  (let ((os-data (split (string #\Newline)
                        (system "cat /etc/*-release"))))
    (loop for i in os-data
          if (search "ID=" i)
          do (return (subseq i 3)))))


(defun get-os ()
  #+(or win32 mswindows windows) ; Windows
  "windows"
  #+(or macos darwin) ; macOS
  "darwin"
  #-(or win32 mswindows macos darwin windows) ;Linux
  (get-dist))

これによってCommon LispLinuxディストリビューションを調べることができる。

まとめ

今回はCommon LispLinuxのディストリを調べる方法をまとめた。

今回のこれの動機として、現在開発中のCommon Lisp用プロッターであるKaiのバックエンドにGRを使えるように拡張しようとしていた。

github.com

その際にGRのバイナリを利用する必要があったのだが、これがディストリビューションごとによって異なるのである。

GRのページ : https://gr-framework.org/c.html

今回ディストリを調べることによってディストリごとにバイナリのダウンロードクライアントを実装することができたので、晴れてこれからバックエンド拡張の実装に取り掛かることができる。

これからはひたすらCFFIで無限にFFIしていくことになりそうなので、気合入れて開発していこうと思う。

最後に、Kaiは現在すでにある程度使えるレベルになっているので、ぜひ触ってみて感想を欲しい。

ついでにスター押して欲しい。

京都大学を卒業した&東京に引っ越した&色々

f:id:komi1230:20200327155907j:plain
春っぽい画像を用意してみた。特に意味はない。

卒業と引っ越し

2020年3月24日をもって京都大学を卒業した。

ついでに3月から両国に住み始めた。

こんな具合で自分のプライベートに関してはかなり変化があった。

ただ最近話題のCOVID-19のおかげもあって、引っ越したての頃は全くトイレットペーパーやティッシュが手に入らずに四苦八苦したり各種イベントが吹き飛んだり、まあ色々。

卒業式が公式に催されることが無くなったということで別に大学は閑散としてるだろうからわざわざ京都に行かなくてもいいかという考えのもと学位記は大学の事務の人に東京の家へ郵送してもらうことにしたのだが、ツイッターやらFacebookを見ているとどうやら大学の同期はみんなスーツや袴(ちょこちょこ仮装)を着て大学に集まり写真を撮っていたらしい。

なんだかモヤッとした感じのまま卒業してしまった。

まあそんなこともあるだろう。

そんなこんなで今は東京でのんびり過ごしている。

社会人へ

さて、4月からはとうとう社会人となる。

自分はソフトバンクに内定していて、4月1日の入社式では新入社員700人の代表として孫さんの前で答辞を読むことになっているのだけど、昨今の経済情勢を鑑みるにまだ内定切りの可能性もあるのでまだ油断ならない。

まあ流石に無さそうだが。

学生期間中はちょこちょこインターンなどでフルタイムで働いていたので1日8時間程度働くことについては肌感覚として分かっているつもりで、というか2月末まで京都でほぼフルタイムでアイフルで働いていたので、4月からは社会人としての云々が始まるというより働く場所が変わるくらいの感覚。

とりあえずがんばっていこうと思う。

バイト先を退職

ちなみに今この文章を書いていて思い出したのだけど、アルバイトとして約9ヶ月程度働いたアイフル株式会社を3月をもって(卒業するので)退職した。

出勤日自体は2月末までで、有給を消化し終えたのが3月。

アイフルではほぼほぼフルスタックエンジニアみたいな働き方をしていて、前半の方はインフラのセットアップ(GCP)からサーバー、フロントまで色々やっていたけど後半の方は人が増えてきたのもあってマネジメントとか要件定義とかマネージャーっぽい何かをやっていた。

金融系企業(いわゆるJTC)でエンジニアリング組織の立ち上げをやるのは良くも悪くもかなりの苦労と学びがあり、学生という立場であるにも関わらずそれなりに責任のある仕事を任せてもらった上に様々な経験をさせてもらったのはかなり良い収穫だったと今では思う。

退職エントリーみたいなのを書こうかなと一瞬考えたり考えなかったりという感じだったが、正社員じゃないのに退職エントリーを書くのはなんか違うかなーという思いと、この4, 5月から技術顧問という形で再入社(?)することになるので、退職エントリーについては見送りということしておき、このエントリーにて簡単に報告という形で済ませておこうと思う。

Kaiの進捗

ついでにもう一つ報告で、現在開発中のOSSとしてCommon Lisp用プロッターのKaiを作っているのだが、それがある程度動くようになった。

github.com

2019年のソフトバンクAI部のアドベントカレンダーCommon Lisp愛を熱く語り、最後に自作OSSとしてKaiの初期バージョンを発表するというのをやったのだけど、その際はKaiのバックエンドにはOpenGLを採用していた。

komi.hatenadiary.com

ただまぁこれがなんとも汚いというか、描画したときのデザイン感が気持ち悪すぎてちょっと無理になった。

具体的に、線を描画するくらいのところまでは開発を進めたのだけど、線のつなぎ目が気持ち悪かったりウィンドウ内の文字のフォントがWindowsOSかよってくらい汚くて、これはもう自分の美学には反するということで開発を中断し、バックエンドをWebGLに差し替えることにした。

そんなわけでのんびりたらたら作っていたわけだけど、ようやく動かせるレベルにまで至ったわけである。

一応現在使える機能としては

  • 線プロット(領域塗り潰しも)
  • 点プロット
  • 棒グラフ
  • 円グラフ
  • Sunburst

あたり。

Sunburstってのはこんなやつ。

これから実装予定なのは

  • ヒートマップ
  • 等高線(Contour)
  • 3Dプロット

を考えている。

Common Lispで使えるプロッターはかなりサーベイしたのだけど、どうやらどのライブラリも線プロットと点プロットが限界らしいので既に棒グラフや円グラフを使えるようにしているKaiはCommon Lisp用プロッターで一番高機能なライブラリなのではないかと思っている(あくまで思っているだけ)(もっと優秀なやつがあるかもしれない)

また、Kaiの今後の発展可能性として、上記の機能を追加に加え、バックエンドを柔軟にスイッチできるようにしようと考えている。

具体的に、現在はWebGLベースのPlotlyをバックエンドのメインとして開発を進めているが、これからデスクトップ環境での描画媒体としてGRやPyPlot、Gnuplotにも対応していこうと考えている。

これほどの野望となると開発期間もかなり長くなっていきそうだが、まああくまで趣味程度にのんびり開発を進めていき、いつの日にかKaiがCommon Lispのプロッターライブラリでデファクトスタンダードになってくれればいいなと考えている。

Kaiについては乞うご期待(入社してから忙しすぎて開発が全く進まなくなったら察して欲しい)

まとめ

以上、色々雑多に色々まとめたが、こんな具合である。

前まではブログも頻繁に更新していたのだけど、新居に引っ越してからまだWi-Fiが開通してないのもあってのんびりブログを書く時間がないのである(日中はカフェに行ってWi-Fiに繋いで開発を進めている)

本当はもっと大なり小なり報告することがあるのだけど(入学式のときに買ったスーツが去年の内定式のとき太ったせいで着れず新しいスーツをユニクロで買ったけど最近痩せて元のスーツが着れるようになった話とか)、それらについてはTwitterを見守っててくださいという具合で。

ではでは。

Zshでサブコマンドにエイリアスをあてる

REPLを触っているとき過去の入力を遡ったり矢印キーを使って云々するってことはよくあること。

これがあると結構色々便利だったりする。

さて、Common Lispの処理系でREPLを起動するときあまりこれができないのでhogeという気持ちになってしまう。

普段使っているのはRoswellというもので、REPLを起動する際は

$ ros run

とする。

今回はこのros runに対してrlwrapを効かせたい。

エイリアスをあてる

zshの環境でエイリアスをあてるとき

alias '[target-command]=[original-commands]'

という書き方をするが、このtarget-commandにサブコマンド入りを入れることはできない。

具体的に

# これは動かない
alias 'pip install=pip install --user'

は機能しない。

ということでzshrcにて関数を定義してやる。

function ros() {
    if [[ "$1" == "run" ]]; then
        shift 1
        command rlwrap ros run "$@"
    else
        command ros "$@"
    fi
}      

これをしてやることによって、例えばros install hogehogeとすると普通のrosコマンドが機能し、第一引数にrunがある場合はrlwrap ros runが機能するよう条件分岐してくれる。

先ほどのpip installの例では

function pip() {
  if [[ "$1" == "install" ]]; then
    shift 1
    command pip install --user "$@"
  else
    command pip "$@"
  fi
}

となる。

macOSでoptionキーをメタキーとして使う

今までエディタを自分オリジナルにカスタマイズするとき、各エディタごとにoptionキーに役割を当ててたりしたのだが、ターミナルの環境設定のところからoptionキーをメタキーに割り当てるという設定を発見した。

f:id:komi1230:20200129133826p:plain
画像の右下の部分

今までエディタはずっとEmacsを使っていたのだけれど、最近ちょっとLemが気になってきて前に少し触ってみた。

キーバインドEmacsライクなのでCtrlキーが命なのだけれど、Ctrlキーはちゃんと動く。

しかしOptionキーをメタキーに割り当てる方法を色々ググっていたのだけれど、LemのIssueには上がっていなくて、なので諦めていた。

今回optionキーをメタキーに当てる方法が見つかったのでこれからLemを使ってみようと思う。

Common LispでHTMLファイルをブラウザで見る

なんかバタバタしてて久しぶりな更新な気がする。

前にはブラウザを立ち上げる方法について簡単に調べたのだけど、結局Pythonで確認しただけでCommon Lispでどうなってるのかについては試してなかった。

komi.hatenadiary.com

で、このあと色々試したのだけど、どうやら色々勘違いをしていたというか、もっと根本的に掘り下げる必要があったのが発覚した。

というのも、基本的にコードからHTMLファイルをブラウザで開くような操作をするにはUnixコマンドのopenコマンドを直接叩くというのが主流らしい。

これの裏付けとして、Pythonの言語側のWeb Browser Driverの実装を眺めているとしっかりopenコマンドを叩きにいってる。

github.com

他にもJuliaとかもそんな具合だった。

さて、前回の記事でcl-selenium-webdriverがいい感じかもしれないというような言及をしたが、これについては半分正解で半分間違いという具合。

github.com

このライブラリのうまみは何かというと、ブラウザを開くという所作ではなくコードから直接HTTP通信ができるという点らしい。

それの副次的な作用がブラウザを開くという帰結であり、今回自分はローカルに置いたHTMLファイルを開くことさえできれば良いので、まあ言ってしまえばオーバーキル感があるのである。

方針は決まった

以上、色々と試行錯誤したが、結局のところローカルにあるHTMLファイルをブラウザで開くにはCommon LispソースコードからUnixコマンドを叩きさえすればいい。

(defun system (cmd-str)
  (trivial-shell:shell-command cmd-str))

(defun open-browser ()
  (let ((path-to-html (check-file-exist "index.html")))
    #+darwin
    (system (format nil "open ~A" path-to-html))
    #+linux
    (system (format nil "xdg-open ~A" path-to-html))
    #+win32
    (system (format nil "start ~A" path-to-html))))

Unixコマンドへのアクセスは処理系依存なところがあり、これをここで細かく書いてると色々ゴチャゴチャしてきて本来やりたいことできなくなってしまうので、Common LispソースコードからUnixコマンドへのアクセスに関してはtrivial-shellを利用した。

ちなみにこのコードはいつぞや立ち上げたプロジェクトであるKaiの一部切り出し。

github.com

Kaiの現在の進捗

12月に始めたKaiプロジェクトであるが、現在のところコード自体については遅々ながらも徐々に進んでいる。

もともとバックエンドにはOpenGLを採用していたのだが、使っていたライブラリがバージョンが弱いやつだったのか知らないがあまり描画能力が高く、フォントも汚かったので、このままでは将来性が無いということでバックエンドを別に切り替えることにした。

バックエンドの選定にはJuliaのPlotsを参考にし、PlotlyとGRの2つにすることにしている。

本心としてはPyplotが使いたいところだったのだけれど、実装を眺めてみるとどの言語からも触れるようなAPIが提供されているわけではなくC言語からゴリゴリにPythonのラッパーを書いていて、Common Lispに移植するのは簡単ではなさそうだったのでまた来世ということに。

Plotlyは描画媒体がブラウザということもあって、Common LispからPlotlyを触るにはJavaScriptのコードへ変換するようなマクロを提供するだけで解決する。

で、そんなわけでKaiを作り続けているのだけど、現在の進捗としてはJavaScriptへの変換マクロさえ書いてしまえばKaiのPlotlyをバックエンドとしたバージョンは完成という状況となっている。

Plotlyを利用する際のフローとして、

  1. ホームディレクトリの~/.cache/にKaiのディレクトリを切る
  2. ダウンロードクライアントを用いてPlotlyのjsファイルを取ってくる
  3. Common LispからPlotlyのためのJavaScriptコードを生成
  4. index.htmlを生成
  5. HTMLファイルをブラウザで開く

という形式をとっている。

で、真ん中のJavaScriptのコードジェネレータ以外は完成しているという具合。

すぐに残りの文を完成させたいのだけど、その前準備として今はPlotlyのAPIを眺めながらどういう風に実装するのが良いか悩んでいる。

さっさと仕上げてデモを公開したいところ。

これからもがんばるぞい。

『ベイズ深層学習』が最高すぎた

今回は書評エントリー。

ちょうど今日の午前中に須山さんの『ベイズ深層学習』を読み終えた。

ものすごく良かったのでここで全力で宣伝しようと思う。

概要

本書はベイズ統計と深層学習の組み合わせについて詳説した一冊で、頻度論に基づく線形回帰と確率分布の基礎の解説から始まり、そこから線形回帰やニューラルネットワークベイズ的にどのように説明できるかについて展開、そこから深層学習のベイズ的な説明をしてガウス過程へとたどり着く構成となっている。

本書の魅力はなんといってもそのボリュームにある。

本来であればこのレベルのコンテンツは3, 4冊に分かれていてもおかしくない量と濃さであるにも関わらず、これらの内容を簡潔に解説して1冊にまとまっている。

帯の「欲張り本」という表現は非常に的を射ていると思う。

ただ、この本のすごいところは、解説の丁寧さとわかりやすさにある。

数式を用いて厳密な説明をしているが、一方で非常にわかりやすく書かれているため、疑似コード等は書かれていないものの数式と説明を読んで容易に実装のイメージがつくようになっている。

また、各アルゴリズムのわかりやすさのためにアルゴリズムを実装して可視化したグラフも豊富に用いられている。

ここまで解説を丁寧にしつつわかりやすさと内容の濃さを実現したこの一冊は、他に類を見ないと思う。

感想

だんだんと機械学習という単語が民主化してきて「AI」がバズワードとなって久しいが、それに応じてなんちゃってAI解説本が出てきて書店の棚にも怪しい本が立ち並ぶ中、このような強烈に素晴らしい本が出てきたことが非常に素晴らしいと思う。

かつて、日本語の書籍でベイズ機械学習の勉強をしようと思うのならPRMLの和訳版の一択であると個人的に考えていたが、今後はPRMLより『ベイズ深層学習』を勧めたいと思っている。

もちろんPRMLは良い本だと思うが、内容が出版されてからだいぶ時間も経っている(2006年出版)ために深層学習の周辺についてカバーし切れていないというのも事実だと思う。

その点で、PRMLよりもコンパクトに、かつ要点をしっかり押さえた上で深層学習までカバーするという意味では『ベイズ深層学習』の方が良いような気がする。

もちろん、PRMLにも良さはあって、本書がカバーし切れていない部分(時系列解析など)がある。

個人的にベイズ機械学習の導入については『ベイズ深層学習』の方が簡潔だと感じているため、今後はベイズ機械学習の勉強のロードマップでは最初に『ベイズ深層学習』を読んだあとにPRMLを読んだ方が最終的に理解度合いが深まるのではないかと思っている。

なんとなく書店で本書を手にとって読み始めたのだが、個人的にここ最近で一番の当たりだと思っているので、機械学習の勉強をこれからするという人にはぜひともオススメしたい一冊である。

コードからブラウザへジャンプする

2020年初の更新。

最近色々コード書いているのだが、ソースコードからHTMLファイルを生成してそれをブラウザで起動するというようなことをしたくなった。

経緯

Common LispのプロッターとしてKaiを作っており、OpenGLをラップしたCL-OpenGLを用いていたのだけれど、見た目は拡張性の問題からバックエンドを別のものへ移植しようと考えている。

実際のところ今のままでも開発は進められるのだけれど、少々機能が弱すぎるのとフォントが汚い、線の描画が弱すぎる、APIが使いにくい、OpenGLのバージョンが古すぎて参照できるドキュメントが死にすぎているなどの問題がある。

そこでバックエンドの移植を考えているのだけれど、移植先として今のところ考えているのはGRかPlotlyの2つ。

GRはGKS(Graphical Kernel System)とOpenGLをもとにしたグラフィックライブラリであり、重いがデスクトップ上で動き、デザインに関しては非常に良い。

PlotlyはWebGLをベースにしたグラフィックライブラリもといブラウザ上で動くプロッターであるが、これは基本的なプロット機能はすでに実装してあるのでCommon Lisp用のプロッターとして仕上げる場合はPlotlyとCommon Lispの間を繋ぐミドルウェアを作ることが目標になる。

そんなこんなでまずはPlotlyから着手しようと考え、Plotlyを動かすにはソースコードからブラウザを起動できる必要があり、そんなこんなで今回の記事に至ったわけである。

Python

環境はmacOSなのだが、Pythonでは以下のようなコードで実行ができる。

import webbrowser

# HTMLのファイルがある場所へのパス
path = "file://" + "/Users/komi/test/index.html"

webbrowser.open(path)

拡張子が.htmlの場合はブラウザが、これを.mdのようにしたらXcodeが開く。

ちなみに正式には使用するブラウザを指定してから実行する必要があるらしい。

import webbrowser

# MacOS
chrome_path = 'open -a /Applications/Google\ Chrome.app %s'

# Windows
# chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'

# Linux
# chrome_path = '/usr/bin/google-chrome %s'

webbrowser.get(chrome_path).open(url)

Common Lisp

Common LispではCL-Selenium-WebDriverというのが存在するらしく、以下のリポジトリに従ってやればCommon Lispのコードからブラウザを叩けるらしい。

github.com

これについては未確認なので後日試してみる。

ただ、こいつに関しては最後のコミットが数年前なので少々不安なところである。

終わりに

GUIアプリの云々やグラフィックライブラリの諸々を調べていたところ、どうやら非常に混沌としているような印象を受けた。

パッと見、どのライブラリがどこまでカバーしているのか謎なのだけれど、とりあえずGRとPlotlyがある程度新しくて安心して使えそうなグラフィックライブラリだと思われる。

2020年も開発をがんばっていきたい。