最近ずっと寒いね

  • なんだかずっと忙しい感じが続いている。
  • 先週金曜くらいに仕事の効率アップに役立つ便利ツールの構想を思いつき、土日はずっと作っていた。
  • 今回は GCP (Google Cloud Platform) を使ってみる。最初 Python で少し書いて API を触ってみてから Go で書いている。
  • 最終的にはインターフェースとして Slack の bot も作る予定。
  • GAE がいいのかなと思ったけれど、多分 Heroku の方が適していそう。
  • EmacsPython を書いていると auto-complete の補完が少し物足りないと感じた。外部パッケージの関数とかを補完してくれていないっぽい。
  • Python はしばらくの間 Visual Studio CodePyCharm で書いていたから Emacs で書くのは久しぶり。多分設定が足りていないのかなと思うけれどひとまず後回しにしよう。
  • あ、あと Emacs先日設定ファイルを一新してから popwin とか細かいところで少しこれまでの操作感と違う部分があるから、おって設定ファイルを見直そうと思う。

引き続き自炊してる

f:id:ryskosn:20180130234311j:plain

Python 2.7 のインストール

つい忘れるので書いておこう。

MacPorts でのインストール

$ sudo port install python27

実際は他の何かを入れた際に依存関係で入っていたようなのでやっていない。

virtualenv で仮想環境を作成する

$ pip install virtualenv
$ virtualenv -p (which python2.7) ~/py27
# (which python2.7) は fish の書き方

PATH を通す

config.fish に以下を追加する。

set -x PATH $HOME/py27/bin $PATH

see also

自炊するようになった

最近また自炊するようになった。これまでと少しスタンスを変えて、食材を一度にたくさん買わないようにした。当日か、最大でも翌日には使い切るくらいのものしか買わない。内容量によっては残ることもあるけれどあまり気にしない。

幸い自宅からスーパーまでそんなに離れていないので、実際に計測してみたら往復しても大して時間はかからないことがわかったし、むしろ運動になってちょうどよいのだと考えるように切り替えた。

今までは、ある程度まとめて買う方が効率的だろうと考えていたけれども、自炊スキルがあまり高くないこともあって、たくさん買おうとすると「用途のイメージがクリアではないけれども購入する」という部分の占める割合が大きくなっていたと思う。

そして「外食するかもしれないし、自炊するかもしれない」という曖昧な状態だと、自炊を選択しない場面も出てくるので使いきれないうちに傷んでしまったりして、しょんぼりした気持ちになったりする。

一方、今日はこれを作ろう、だからアレとアレを買おう、と明確に決まっている状態であれば売り場でも迷わないし、買って帰って作って食べる、とスムーズな進行が可能になる。言われてみれば当たり前だけどさ。

ただ、もう少し短時間で済ませられるようになりたいというのはある。

f:id:ryskosn:20180123235200j:plain

ダイソーのバックレストがよかった

これ、すごくよかった。このつくりで 100 円で買えるというのはちょっと信じられない。自宅と会社の両方に設置しても 216 円!

買いだめしておいた方がよいだろうかと考えてしまうレベル。置く場所がないから難しいけれど。

バッチファイルでログを取る

会社で他の人にも使ってもらっているツールについて、うまく動かないようだという連絡をもらったので、エラーの内容を確認するためのバッチファイルを用意した。

@echo off

rem 実行する.bat のディレクトリに移動
cd /d %~dp0

set log_file="log.txt"
echo ---------- >> %log_file%

rem %var:~i,j% で var の i 文字目から j 文字目までを抜き出す
rem i は 0 始まり
echo %date% %time:~0,8% >> %log_file%

set host=%COMPUTERNAME%
echo host: %host% >> %log_file%

set user=%USERNAME%
echo user: %user% >> %log_file%
echo ----- >> %log_file%

rem ログを取りたいアプリケーションを実行
rem something.exe >> %log_file%

コマンドプロンプトはテキストのコピペがやりにくかったり、色々とフレンドリーではないよね。

PowerShell は起動が遅いし実行権限の設定を変更しないとスクリプトを実行できなかったり、管理者用という感じの印象。

そういえば、WindowsUbuntu が動くやつ、まだ試したことないのだけれどどうなんだろう。

ノート PC の設定してた

(last update: 2018/01/22)

しばらく書いてなかったな。

先週末にノート PC の OS をインストールしなおしたのでセットアップしてた。 少し前に OS を high Sierra にアップデートしてみたら、Haskell の管理ツール stack が動かなくなったみたいで、これもよいきっかけと思いクリーンインストールしなおすことにした。

数年前から各種設定ファイルは ~/dotfiles にまとめて Github で管理しているので、ここから引っ張ってきてちょこちょこと作業すればいいのだけれど、今回は少し変更したいところもあったので設定ファイルを作り直すことにした。

変更したかったのは

  1. Cask での Emacs パッケージ管理をやめる
  2. Emacs の設定ファイル init.el を分割する
  3. $GOPATH$HOME/go ではなく $HOME にする
  4. zsh はもう使わない、fish だけでよい
  5. Ruby のインストールもいらないかな

などなど。

Emacs のパッケージ管理

これまでは Cask を使っていた。~/.emacs.d/Cask というファイルに使いたいパッケージを記入しておくと $ cask install とコマンド実行するだけで全部インストールしてくれるもの。

これはこれで便利だったけれど、use-package というパッケージを使えば、elisp のみでパッケージのインストールとその設定をまとめて記述することができて、外部コマンドを導入する必要もなくすっきりするので、変更してみたくなった。

use-package のいいところ

  • require したパッケージが入っていなかった場合でも init.el の読み込み自体はエラーにならない。
  • :ensure t と書いておくとそのパッケージをインストールしてくれる
  • key-bind や add-hook なども簡潔に記述できる

こんな感じ。

;; https://github.com/benma/visual-regexp.el
(use-package visual-regexp
  :ensure t
  :bind (("M-%" . vr/query-replace))
  )

Emacs の設定ファイル分割

分割をやめた方の話も一応。

ag とか ripgrep とか

grep の代替となる高速検索ツール、ag とか名前は聞いたことがあったけれど使っていなかったので今後は活用してみたい。そもそも grep すらあまり使っていないので何かと効率悪いことをしていると思われる。

anything と組み合わせて便利な感じになったりするんだと思う。多分。もしかしたら helm じゃないとだめなのかな。

何ならもう一回、rubikitch 先生の有料メルマガを購読して質問してみるのがいいかもしれない。

ripgrep 入れようとしたら

ripgrep 入れようとしたらスムーズにいかなかくて yak shaving 感あったと tweet したら作者の方からバイナリあるよ、とリプライいただいてびっくりした。

素直にダウンロードして /usr/local/bin に置いておいた。まだ使いこなせていないけれどせっかくだから使いたい。

Go の TOML ライブラリの作者として認識していた。すごい人はいろいろすごい。

fish の設定

昨年使い始めてから、もう二度と zshbash には戻れないと思わせる、とても便利なシェル fish。

今回は、使いたい関数は config.fish に直接書けばいいかなと思い、プラグインマネージャー fisherman は入れなかった。実際、ヒストリ検索と ghq 管理下のリポジトリcd するやつの 2 つがあれば満足かなと。

って思っていたのだけれど、z という最近 cd したディレクトリを列挙してくれるツールが快適そうに思えてきて、やっぱり fisherman 入れようかなと少し揺れているところ。

一回使ってみようかしらん。

そんなこんなで

設定ファイル init.el の内容を見直しつつ、分割して、書き直していった。一つ一つ調べながらやっていたら、結局一週間かかった。

Github にある既存のリポジトリをいずれ差し替えたいのだけれど、Git の操作に疎いのでひとまず bitbucket に置いた。(公開設定がプライベートになっていたので修正した)

README.md の更新もだいたい落ち着いたのでようやく一息つけるわ。

OCaml モジュールとシグネチャ

OCaml モジュールとシグネチャ

ソースファイル .mlコンパイルするとオブジェクトファイル .cmo.cmx .o と同時にインターフェイスファイル .cmi が生成される。

この .cmi について調べてみた。理解してしまえば何ということはないのだけれど、少し時間がかかった。

  • .mliコンパイルすると .cmi が生成される。
  • .mli がない場合は .ml から .cmi が生成される。

では .mli とは何だろう。

ではモジュールとは、シグネチャとは何だろう。

モジュールとは

詳細な解説は以下の記事、資料を読むとよさそう。

ただ、一度に全部を理解しようとするのはかなり困難だったので、ひとまずは「関連する処理やデータなどのまとまり」くらいの理解でよいと思う。Python や Go でいうパッケージと同じようなものだと考えればよいだろう。

OCaml では一つのファイルを一つのモジュールとするそう。前回書いた、階乗を求める処理でもモジュールとして扱っていた。

fact.mlFact モジュールとなる。

(* fact.ml *)

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

利用する側からは open で読み込む。

(* main.ml *)

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

もしくは、モジュール名.関数 の形でドット . をつけてモジュール内の関数を利用する。

(* main.ml *)

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

モジュールに別名をつけて読み込む

Pythonas で別名をつけて import するのと同じように

import numpy as np

以下の書き方で別名をつけて open することができる。

module F = Fact
let () = print_int (F.fact 10); print_newline()
  • この場合 open というキーワードは使わない。
  • 別名 F の部分は大文字で始める必要あり。

シグネチャとは

モジュールについては他の言語でも似たような仕組みはあるので、何となくではあるけれどわかった。次はシグネチャである。

OCaml におけるシグネチャは、モジュールの中身に対するアクセスの可否を制御する仕組みで、個々の関数には private とか public とかつけずに別途まとめて管理できるようになっている。

今まで触ったことのある言語(Python, Go, JavaScript あたり)ではこれに該当するものはなかったと思われ、新鮮に感じた。言語によっては、関数の仮引数や戻り値の型、アクセス修飾子などのことを指してシグネチャと呼ぶ場合があるみたいだけれど。

Python だと名前の頭にアンダースコア ___ をつけることで private 扱いになり、Go だと名前の頭文字が大文字なら外部からアクセス可能で小文字だとアクセス不可、という仕様になっている。

で、OCaml の場合。

Fact モジュールを少し拡張して試してみる。階乗 n! のバリエーションとして多重階乗というものがあるらしい。二重階乗 n!! であれば一つ飛ばしで積をとる。

5!! = 5 * 3 * 1
10!! = 10 * 8 * 6 * 4 * 2

この多重階乗を求める関数 multifact を書いてみた。

(* fact.ml *)

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

let rec multifact x n =
  match n with
  | 0 -> 1
  | 1 -> 1
  | _ -> if x > n then
      n
    else
      n * multifact x (n - x)

let doublefact = multifact 2

モジュールを利用する側のコード。

(* main.ml *)

open Fact
let () =
  print_int (fact 10); print_newline ();
  print_int (multifact 2 10); print_newline ();
  print_int (doublefact 10); print_newline ();
;;

実行結果はこうなる。

# コンパイルして
$ ocamlc -o fact10 fact.ml main.ml

# 実行する
$ ./fact10
3628800 # fact 10
3840    # multifact 2 10
3840    # doublefact 10

シグネチャ*.mli に書く

次に Fact モジュールのシグネチャfact.mli に書く。公開するものだけを書き、シグネチャに書かれていないものは外部からアクセスできなくなる。

試しに multifact は非公開としてみる。

(* fact.mli *)

val fact : int -> int
val doublefact : int -> int

このシグネチャの書式はコンパイラ-i オプションをつけて当該ファイルを渡すと確認できる。-i オプションのみの場合は実際のコンパイルはしない(.cmo ファイルは生成されない)。

$ ocamlc -i fact.ml
val fact : int -> int
val multifact : int -> int -> int
val doublefact : int -> int

*.mli にリダイレクトして、公開したくないものを削除すればよい。

$ ocamlc -i fact.ml > fact.mli

ファイル *.mli が存在する場合は、*.ml よりも先にコンパイルしないとエラーになる。

$ ocamlc -c fact.ml
File "fact.ml", line 1:
Error: Could not find the .cmi file for interface fact.mli.

fact.mliコンパイルして、

$ ocamlc -c fact.mli # fact.cmi が生成される

fact.mlコンパイルして、

$ ocamlc -c fact.ml # fact.cmo が生成される

main.mlコンパイルしようとすると、エラー!

$ ocamlc -c main.ml
File "main.ml", line 4, characters 13-22:
Error: Unbound value multifact

ふむふむ。

それでは修正してみよう。main.ml から multifact を呼ぼうとしている箇所をコメントアウトして、

(* main.ml *)

open Fact
let () =
  print_int (fact 10); print_newline ();
  (* print_int (multifact 2 10); print_newline (); *)
  print_int (doublefact 10); print_newline ();
;;

コンパイルすると、今度は無事通った。

$ ocamlc -c main.ml # main.cmo が生成される
$ ls
. .. fact.cmi fact.cmo fact.ml fact.mli main.cmi main.cmo main.ml

これで必要なファイルが揃ったので、以下のように -o オプションで実行ファイルを出力する。

$ ocamlc -o fact10 fact.cmo main.cmo
$ ./fact10
3628800
3840

あるいは、以下のようにして最初から実行ファイルを出力することも可能。この場合もコンパイラには *.mli を先に渡す必要あり。

$ ocamlc -o fact10 fact.mli fact.ml main.ml

シグネチャを書かない場合は全て公開される

先のエントリで見たようにシグネチャを書かなくても *.ml から *.cmi が生成されていた。この場合は、モジュールの中身は全て公開されることになる。

まぁ OCaml プログラマが意図的にシグネチャを書かないというケースはなさそう(想像)だから、気にしなくていいんじゃないかな。

参照

OCaml コンパイルの続き

(last update: 2017/09/23)

前回の続き。

$ 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 オプションはいつ使うのかという疑問がわく。

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

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

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

(追記 2017/09/23)

上記ではバイトコードコンパイラ ocamlc での結果を書いたが、ネイティブコードコンパイラ ocamlopt で試した場合も同様の結果になった。

参照

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 がついた ネイティブコードバージョンが存在するのでそちらを使ったほうがよい。