OCaml コンパイルの続き

前回の続き。

$ ocamlc -o fact10 fact.ml main.ml

コンパイラ ocamlcocamlopt-o オプションで実行ファイル名を指定してソース・ファイル .ml を渡すと実行ファイルが出力される。また .cmo.cmi などのファイルも生成される。

で、これは恐らく手順をスキップしているというか、コンパイラがよしなにやってくれている。順をおって一手ずつ進めるとしたらこうなる。

まずソースファイル .ml だけの状態から

$ ls
. .. fact.ml main.ml

fact.mlコンパイルする。(-c オプションを指定すると実行ファイルを作らずコンパイルだけする)

$ ocamlc -c fact.ml

$ ls
. .. fact.cmi fact.cmo fact.ml main.ml

次に main.mlコンパイルする。

$ ocamlc -c main.ml

$ ls
. .. fact.cmi fact.cmo fact.ml main.cmi main.cmo main.ml

このように 1 つずつコンパイルする場合も main.ml から先にコンパイルしようとするとエラーになった。依存される側(参照される側)から先にコンパイルする必要がある。

File "main.ml", line 1, characters 5-9:
Error: Unbound module Fact

で、オブジェクトファイル .cmoコンパイラに渡して実行ファイルを出力する。

$ ocamlc -o fact10 fact.cmo main.cmo

$ ls
. .. fact.cmi fact.cmo fact.ml fact10 main.cmi main.cmo main.ml

$ ./fact10
3628800

ちなみに .cmo を渡す場合も依存関係を無視してはいけない。main.cmo を先にするとエラーになる。

$ ocamlc -o fact10 main.cmo fact.cmo
File "_none_", line 1:
Error: Required module `Fact' is unavailable

コンパイラは賢いから人間がソースファイルを渡して「実行ファイルを作って」とお願いしてもやってくれるけれど、ソースファイルからオブジェクトファイルを生成して、それを結合して実行ファイルを作成するというのが本来の手順なのだろう。

でも、同じ結果が得られるなら最初から実行ファイルを作成した方が早いだろうし -c オプションはいつ使うのかという疑問がわく。

それは恐らく、もっと大きくて複雑なソフトウェアを作る場合には全体をパーツごとに分解して作っていくので、そういうときには、

ある部分は既にコンパイル済みの状態(オブジェクトファイル)で、 一部分だけコンパイル(ソースファイル -> オブジェクトファイル)し直して、 既にあるオブジェクトファイルと組み合わせて実行ファイルを作成する

という使い方をしたりするので必要、というか必須なのだと思う。(想像)

参照

OCaml プログラムをコンパイルする

インストールとか環境構築については今度書くとして、今日はコンパイルについて。

バイトコードとネイティブコード

OCaml をインストールすると標準で 2 種類のコンパイラがついてくる。

バイトコード

ネイティブコード

  • コンパイルしたプログラムは機種(ハードウェア・OS)に依存する
  • コンパイルした環境と異なる環境では動かない場合がある
  • インタプリタは不要でハードウェアから直接実行できる
  • ネイティブコードの方が実行速度は速い

コンパイルしてみる

サンプルとして階乗を求める関数 fact と、

(* fact.ml *)

let rec fact n =
  if n = 0 then
    1
  else
    n * fact (n - 1)

それに引数 10 を渡して実行した結果を表示するプログラムを書く。

(* main.ml *)

open Fact
let () = print_int (fact 10); print_newline()

ocamlc でコンパイル

コンパイルして実行ファイルを作成するには以下のように入力する。-o オプションで実行ファイルの名前を指定する。

$ ocamlc -o fact10 fact.ml main.ml

コンパイルすると実行ファイル fact10fact.cmi, fact.cmo, main.cmi, main.cmo というファイルが生成され、fact10 を実行すると結果が表示される。

$ ./fact10
3628800

しかし、ocamlc に渡す順番を逆にするとエラーになる。

$ ocamlc -o fact10 main.ml fact.ml
File "main.ml", line 1, characters 5-9:
Error: Unbound module Fact

これは main.ml の中で fact.ml で定義されている関数 fact を参照している、つまり main.mlfact.ml に依存しているからである。

ocamlopt でコンパイル

ocamlc と同様に -o で出力するファイル名を指定する。

$ ocamlopt -o fact10 fact.ml main.ml

実行ファイルの他に生成されるファイルが ocamlc の場合とは一部異なった。

ocamlc では fact.ml から fact.cmi, fact.cmo が生成されたのに対して、fact.cmi は同じで、fact.cmo の代わりに fact.cmxfact.o が生成されていた。

この *.cmi, *.cmo, *.cmx, *.o とやらは何だろうか。こういう普段見慣れないファイル形式が一度に複数出てくると、ウッてなるんだよね。落ち着いてマニュアルを見てみる。

.cmi とは

.cmo とは

.o とは

.cmx とは

参照

ネイティブコードにコンパイルされたコンパイラ

上述の ocamlcocamloptバイトコードコンパイルされたコンパイラのようで、ネイティブコードでコンパイルされたものもあり、通常はこちらを使うらしい。

  • ocamlc.opt
  • ocamlopt.opt

ネイティブコード にコンパイルされた バイトコード および ネイティブコード コンパイラ。 ネイティブコード コンパイルが可能な環境では通常このコンパイラを使う。

OCaml コンパイラソースで make opt の後に make opt.opt を行うと作成される。 通常の ocamlc, ocamlopt は バイトコード で実行されるが、 *.opt コンパイラはネイティブコードに コンパイルされているため ocamlc, ocamlopt よりコンパイル速度が早い。 (バイトコードコンパイラがひどく遅いわけではないが。)

ocamlc, ocamlopt 以外の OCaml のツールにも、.opt の suffix がついた ネイティブコードバージョンが存在するのでそちらを使ったほうがよい。

OCaml に再入門( 3 年ぶり 2 回目)

最近、OCaml に再度入門しようとしている。前に触ってみたのは 3 年前くらいだろうか。書籍『プログラミングの基礎』を読み進めながら一部を自分でも書いて動かして、という感じで途中までやっていた。

最後までやらずにやめてしまったのは何故だったか、あまり覚えていないけれど

  • より優先順位の高いプロジェクトがあり、そちらに取り組むことにしたから
  • OCamlPythonJavaScript などと比べると日本語の情報が少なく、敷居が高く感じたから
  • Emacs の中で対話環境を動かして、バッファの内容を即実行させることができたのだが、実行結果が少しずれて表示されてしまい、その直し方がわからず、何だか気持ちが悪いなという思いがつきまとっていたから

という感じだった気がする。まぁ結局のところ、必要に迫られていたわけでもないから、ということだろう。

その後、Go を少し勉強した。仕事で使う小さなツールを 2, 3 個書いて、それらは毎日のように使っているので Go は手を出してよかったと思う。仕事で使うツールを書くなら Go で書くのがよさそうだと思うが、今は仕事で使うツールを書くために週末の自分の時間を使いたいとは思わなくなったので、Go 自体の勉強もストップしている。

しばらく触らないと忘れて、忘れるとまた調べ直すために時間がかかることが億劫に感じられて触らなくなる、という疎遠ループ。

で、しばらくプログラミングとは関係ないことに取り組んでいてそちらはそちらで引き続きやっているのだけど、気分転換も兼ねてまたプログラミングに関する何かを勉強しようかなと思っていた。

そしたら、たまたま読んだ以下の記事で『プログラミング in OCaml』が紹介されていたので OCaml を再び触り始めた次第。

この本は、OCaml を使うつもりがとくにない人でも、独力でプログラミングを学んできたような人であれば、読むといいことがたくさん書いてあると思っています。 具体的には、OCaml というプログラミング言語でのプログラムの書き方だけでなく、どうしてそういう機構になっているのかがいちいち詳しく解説されており、プログラミング言語の理論についても味見できるようになっています。

やはり強静的型付けで、コンパイラがしっかりチェックしてくれる言語の方が、堅牢なプログラムを書きやすいのではないかと思っている。プロじゃないからこそ、言語の機能に頼りたいというか。

学習コストもかかるだろうが、期限がある話ではないので今後の人生を考えると長期的には十分回収できるはず。

というわけで、ゆっくり少しずつでも積み上げていけたらと思うので、調べたことをここにも書いていこうかなと。

千葉で温泉

先週の話。

  • 木曜にクロサワから連絡をもらって飯田橋で軽く飲んだ。
  • 家族が帰省中で暇してるということで週末にどこかに行こうということになった。
  • 金曜にいろいろ調べて千葉にある温泉施設、いわゆるスーパー銭湯的なところに行くことにした。
  • クロサワが車を出してくれて、まさかの朝 9 時集合で千葉までドライブ

岩盤浴が最高

  • 岩盤浴がとても気持ちよかった。
  • サウナと違って湿度が高いのであまり辛くなく、じわ〜っと汗をかく感じ。
  • フロントで専用のウェアを受け取って、それに着替えて岩盤の上に大判のバスタオルを敷いて寝そべる。
  • しばらくすると全身から汗が出てくるのでほどほどのところで一旦退室し、隣りにある涼むための部屋(エアコンががっつり効いている)に入る。
  • そして水分補給してまた岩盤の上に戻る、という感じ。
  • 以前 YZ に連れて行ってもらった板橋にあるさやの湯処もよかったな、そういえば。
    • 板橋なら岩盤浴のためだけに、月 1 回くらいのペースで通ってもいいかもしれない。

全般的に最高だった

  • 気の置けない友人と他愛もない話をしながらゆっくり風呂につかったり、岩盤浴で寝そべったり、ジンギスカンを食べたりして過ごすのはとても楽しかった。
  • 行き帰りの運転もぜんぶおまかせだったのでただ乗ってるだけという気楽さ。
  • いろいろ話が聞けて面白かった。
  • 大企業には大企業の悩みというか、いろいろとアレなところもあるのだなぁと思ったり。

東京に戻って

  • 最後は車を置いてから木場のもつ焼き屋さんで軽く飲んで解散。
  • クロサワはもともとあまりたくさん食べる感じではなかったけれど二人とも食べる量が減った気がする。
  • 少量で少しずつ食べたら満足してしまう。
  • でも活気があってよいお店だった。

log を残していこう

  • しばらくご無沙汰してたなーと思ってはいたけれど 1 月から書いてなかったのか、と少しびっくりした。
  • 何かしらちょいちょい書いていくと自分でも生活を振り返ることができて有益だし、家族にもこちらの様子が伝わるし、よいのではないか。
  • あと、ずっと文章を書かないでいると、そのうち書けなくなりそうな気もする。
  • 東京は少し暑くなってきたけれど引き続きがんばろう。

JavaScript, Node.js を始める

JavaScript 熱の高まりというか、やっぱり使えたらいろいろ便利そうだな*1と思い、昨年末あたりから少しずつ調べたりまとめたりしていたので、こちらにも書いておこう。

Node.js をインストール

公式サイトからインストールしてもいいけれど、入れ直したりバージョンを上げたりをやりやすくするためツール経由で入れるほうがよさそうなので nodebrew を使う。

curl でインストール。

$ curl -L git.io/nodebrew | perl - setup

PATH を通しておく。

$ export PATH=$HOME/.nodebrew/current/bin:$PATH

nodebrew 使い方

バイナリを入れるやり方でいいと思う。

# すでにインストールしていた場合
$ nodebrew selfupdate

# インストール可能なバージョンを列挙する
$ nodebrew ls-remote

# バイナリを入れる場合
$ nodebrew install-binary <version>
# ソースからコンパイルする場合
$ nodebrew install <version>

とりあえず stable を指定したら v7.2.1 が入った。

# <version> の代わりに stable や latest という指定が可能
$ nodebrew install-binary stable

# インストール済のバージョンを表示
$ nodebrew ls

# 使用するバージョンを指定
$ nodebrew use stable

# アンインストールする場合
$ nodebrew uninstall <version>

パッケージマネージャ npm について

Node.js をインストールすると npm コマンドが使えるようになる。パッケージマネージャと書いたけれど、備えている機能はもう少し幅広いようだ。

Node.js で開発する、プロジェクトを作るには以下のコマンドから始める。

$ npm init

プロジェクト名とか作者名とかいろいろ質問されるのでそれに答えていくとそれらの情報を含んだ package.json が作成される。

npm init の際にオプション --yes をつけると全てデフォルトで進めてくれる。

package.json にはインストールしたパッケージの情報を記載しておけば、そのプロダクトを別な環境でインストールする際に npm install とするだけで依存するパッケージをインストールしてくれる。

link

npm でパッケージをインストール

npm install <package_name> でインストールする。

オプション --save or --save-dev をつけるとインストールと同時に package.json にも記載される。

--save は実行時に必要なパッケージとして "dependencies:" に記載される。

--save-dev は開発時に必要なパッケージとして "devDependencies:" に記載される。

$ npm install <package_name> --save
$ npm install <package_name> --save-dev

オプション

  • npm inpm install と等価
  • -D--save-dev と等価
  • -S--save と等価
  • -g--global と等価

以下のようにも書ける

$ npm i -D <package_name>

link

インストールしたパッケージを確認する

# ローカルのパッケージを確認
$ npm list # or ls
# グローバルのパッケージを確認
$ npm list --global # or -g

ただ、上記のコマンドだと大量のパッケージが表示されるので以下のようにオプションを指定するのがよい。

$ npm list --depth=0

パッケージのインストール先

ローカルにインストールするとプロジェクトディレクトリ内の node_modules/ に入る。

グローバルにインストールすると <prefix>/lib/node_modules/ に入る。 <prefix> を確認するコマンドは以下の通り。

$ npm config get prefix

ローカルにインストールしたパッケージを使うために PATH を通す。

$ export PATH=$(npm bin):$PATH

その他

インストールしたパッケージのアップデート

この記事が参考になりそう。まだ必要に迫られていないので試したりしていない。

インストールするパッケージのバージョンを指定

package.json にはそのパッケージのバージョン番号も指定される。書式については以下の記事が参考になる。

ローカルのパッケージを Git の管理対象から除外

.gitignorenode_modules/ と書いておこう。

*1:GoogleAppsScript とか Electron とかを用いて生活および仕事のタスクを処理する便利な何かを作れるのではないか的な

fish を導入

(last update: 2017/02/21)

少し気になっていたモダンなシェル fish 、実家の兄も使っていていろいろ見せてもらったらやっぱり便利そうだったので導入してみた。

MacPorts でインストール。Homebrew 派の人は $ brew install fish で。

$ sudo port install fish
$ fish_config # ブラウザで設定画面が開く

fish をデフォルトのシェルに設定する。まず fish がインストールされているパスを確認して、 /etc/shells を編集する。その上で、 chsh コマンドで切り替えれば OK

$ which fish # -> /opt/local/bin/fish
$ sudo emacs /etc/shells # 上記 fish のパスを追加
$ chsh -s /opt/local/bin/fish

プラグインのインストール

oh-my-fish のメンテナの方が作っているというプラグインマネージャ fisherman をインストール

$ curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs git.io/fisherman

peco 関連のプラグインをインストール。 peco 自体は以前に go get でインストールしていた。

$ fisher omf/plugin-peco
$ fisher yoshiori/fish-peco_select_ghq_repository

fish-peco_select_ghq_repositoryghq で管理しているレポジトリを peco で検索して cd する拡張。 C-r でのヒストリ検索に peco を使うように、ghq のやつは C-o にそれぞれ設定しておく。

function fish_user_key_bindings
  bind \cr peco_select_history
  bind \co peco_select_ghq_repository
end

参考

fish-mode をインストール

Emacs で fish スクリプト(設定)を編集するための fish-mode をインストールする。 ~/.emacs.d/Cask(depends-on "fish-mode") を追加して以下のように実行すれば OK.

$ cd ~/.emacs.d/
$ cask install

設定ファイルは zshbash とはことなり .fishrc ではなく ~/.config/fish/config.fish となる。

PATH の設定

.zshrc とは構文が異なる。以下のような感じ。

# PATH
set -x PATH /opt/local/bin /opt/local/sbin $PATH
set -x PATH $HOME/bin $HOME/.cask/bin $HOME/py35/bin $PATH

# Go
set -x GOROOT (go env GOROOT)
set -x GOPATH $HOME/go
set -x PATH $GOPATH/bin $GOROOT/bin $PATH

# nodebrew
set -x PATH $HOME/.nodebrew/current/bin $PATH

alias の設定など

alias は zsh と同じ書式でもいけるという話もあったかもしれない。

# alias
alias rm "rm -i"
alias cp "cp -i"
alias mv "mv -i"
alias mkdir "mkdir -p"

alias find "gfind"
alias xargs "gxargs"

# alias for git
alias gst "git status"
alias gdif "git diff"
alias gdifc "git diff --cached"

# alias for Mac
alias ql "qlmanage -p $argv[1]"

# user defined functions
function cd
  builtin cd $argv
  ls -a
end

function fish_user_key_bindings
  bind \cr peco_select_history
end

補完候補の拡充

以下のコマンドで man などから補完候補を追加してくれるそうな。頼もしい話である。

$ fish_update_completions

現時点の config.fish

Good Bye, 2016

年末年始ということで実家に帰省している。毎年同じことを書いていると思うが、 2016 年もあっという間に過ぎていったように感じている。

学生時代に家庭教師のアルバイトで関わった生徒さんから年賀状をもらって嬉しかったのが 2016 年の 1 月で、そこからいろいろやっていたらもう 12 月。はー、速い。 結局その生徒さんには年賀状は返信して「今度食事でも」と伝えたのだけれど、結局連絡先がわからなくて連絡しないままになっていた。 12 月に入ってから手紙を書いたけれど、「もう知らね」と言われてもまぁ仕方ない。もうちょっと早めにアクションすべきだったな。

速い?早い?

関係ないけれど「時間が経つのがはやい」というときに「早い」か「速い」のどちらなのか、いつも迷う。時間が過ぎていくスピードのことを言うのだから「速い」ではないかと思っていたが、必ずしもそう考える人だけではないようだ。

この説明で納得しかけたけれども、時間の進むスピードが変化したわけではないということは十分承知の上で、「はやい、と感じる」ということを言っているのでやはり「速い」で間違ってはいないような気もする。

振り返り

  • 昨年から取り組み始めたとあることは細々と続けられている。まだ実を結んではいないが、いずれよい結果に繋げられると思う。
  • 軽い運動(腕立て伏せとスクワットなど)をする習慣もだいぶ定着してきた。

  • 英語の勉強はあまり進んでいなかった。最後に受けた TOEIC で 820 点くらい取れて嬉しかったのだけれど、確認したら 2014 年の話だった。寝る前に DUO 3.0 を開いて例文をシャドウイングすることを毎日の習慣にしたい。

  • 数学の勉強(確率、微分積分線形代数など)はあまり進められなかった。

  • Python はあまり書かなかったのでだいぶ忘れてしまった。

  • 夏頃に支出管理シートを作るため GoogleAppsScript (JavaScript) を(自分にしては)たくさん書いた。 TypeScript を使ってみたいと思いつつ環境構築の段階で躓いている。
  • Go を使ってみようと思ったのはいつからだったっけ。夏の前くらいかな?「スターティング Go 言語」を枕元に置いてちょいちょい読んだり、 A Tour of GoGo by Example を写経してみたりしたけれど、ちゃんと書くことなく時間が過ぎていった。 12 月下旬に仕事で使えそうなツールを思いつき、 Go で書いてみた。 100 行くらい。今後はもっと使えるようになりたい。

  • blog を全然書かなくなった。 Markdown で書けるようにとはてなダイアリーからはてなブログへ移行したのだけれど、あまり書かないままだった。

  • これからはもっと気軽に駄文を書き散らかしていきたい。何か書いていないと、文章を書く力が落ちると実感し始めた。他の人はどうか知らないが、少なくとも自分に限ってはそのように感じている。

そういや、 Markdown で改行のために行末にスペースを 2 つ入れる仕様って結構一般的な話なの?兄と話していて聞いたんだけど全然知らなかった。 Emacs で保存時に行末のスペースを削除する設定にしているので、全然使えないというか、相容れない感じがする。

;; 保存時に行末のスペースを削除
(add-hook 'before-save-hook 'delete-trailing-whitespace)

スターティングGo言語

スターティングGo言語

161128 近況

本当にあっという間に 1 日が終わり、1 週間が過ぎて、気づけばもう 11 月末。いやー、速いね。

2016 年のうちにもう少し進歩しているつもりだったような気がするのだけれども、何を言ってもカレンダーは後ろには戻らないので今日から残り 1 ヶ月ちょっとをしっかり過ごしていきたい。

食生活の乱れ

寒くなってくると食事の選択に影響が出る。朝起きた直後に冷蔵庫から出したばかりの冷たいヨーグルトをガツガツ食べる気になるかと言われるとならないし、フルーツグラノーラとかミューズリーとかのシリアル系も同じく冷蔵庫から出したばかりの冷たい豆乳とか牛乳とかをかけることになり、それらを食べられるときと食べるきになれないときがある。

まぁ、パンとかを常備しておいてトーストするなりして温かいものとセットにすればいいのかもしれないけれど。

Golang

結局全然書いていない。会社で使っている Python の小さなスクリプトなどを置き換えていくところからやっていこうかなと思っていたはずなのだけれど、着手できていない。その置き換えはこのままやらない気もする。

ただ Go 自体への興味関心は相変わらずあるので、少しずつ勉強を進めていくと思う。10 月くらいには Go by Example を写経したりしていたのでまたその辺からやっていこうかな。自分で書かないとすぐ忘れる。

支出管理シート

Google Apps Script で書いているやつ。もう少し入力の手間を減らす仕組みを作りたい。カード払いの明細 CSV データを貼り付けて既存の入力データとの差分というか、入力漏れを教えてくれるようなものがあったらいいなと漠然と考えているところ。

アマチュアだからこそ、素の JavaScript を書くのではなく TypeScript とか静的型付けの言語を使ったほうがよりよいものを作れるようになるのではないだろうかということを最近考えたりする。Go に興味を持ち始めたのもそういう動機が根底にあったのかもしれない。

GAS を TypeScript で開発するやり方もあるようなのだけれど、如何せん色々と環境を整える必要がありそうで少々敷居が高いと感じており、まだ踏み出せないでいる。というか時間を捻出できていない。

結局、何をするにしても必要なのは時間と集中力と体力なんだよな。

バール

先日の福島沖あたりでの地震はけっこう長い時間揺れていたので少し怖かった。 地震が起きて、ドアがぶっ壊れて外に出られなくなるかもしれないので、バールを買った。軍手よりも丈夫らしいので革手袋も。

バクマ 六角鶴首バール 19×900mm

バクマ 六角鶴首バール 19×900mm

concurrent and parallel

すぐ忘れるのでメモ

concurrent 並行

複数の動作が順不同もしくは同時に動作する

concurrent の意味 - 英和辞典 Weblio 辞書

parallel 並列

複数の動作が物理的な時間として同時に動く 物理的に複数のコアなどが必要

parallel の意味 - 英和辞典 Weblio 辞書

see also