余白の書きなぐり

aueweのブログ

zshの右プロンプトにコマンド開始終了時刻を表示

コマンドの開始時刻と終了時刻を右プロンプトに表示したくなった。

$                    15:00:00 -           # 15時00分00秒に sleep 30 を入力開始
$ sleep 30           15:00:00 -           # 10秒掛けて sleep 30 と打ち込んだ
$ sleep 30           15:00:00 - 15:00:10  # 15時00分10秒にエンターキー押した

# 30秒経過

$                    15:00:40 -           # 15時00分40秒にコマンド終了

これを実現するには、以下のコードを ~/.zshrc に書き込めば実現できる。だいぶ試行錯誤した。

export PREV_COMMAND_END_TIME
export NEXT_COMMAND_BGN_TIME

function show_command_end_time() {
  PREV_COMMAND_END_TIME=`date "+%H:%M:%S"`
  RPROMPT="${PREV_COMMAND_END_TIME} -         "
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd show_command_end_time

show_command_begin_time() {
  NEXT_COMMAND_BGN_TIME=`date "+%H:%M:%S"`
  RPROMPT="${PREV_COMMAND_END_TIME} - ${NEXT_COMMAND_BGN_TIME}"
  zle .accept-line
  zle .reset-prompt
}
zle -N accept-line show_command_begin_time

ググったら似たような設定してる人が国内外に散見されるのだが、 開始時刻と終了時刻の両方をUPDATEしている例が見つからなかった。 わりと便利だと思う。使うといいよ~

世界のインク瓶の形状まとめ

今週の土日は家に引きこもり、世界各国のインク瓶の形状を調べていた。 下のテーブルで、日本からアメリカまでの各国の青黒系インク瓶の形状をまとめておいた。

各国の特徴をまとめると、

  • 日本 … 丸や台形などシンプルな形状が多い。台形(プラチナ30mlやパイロット30ml)の瓶は外国だと珍しい?
  • ドイツ … 正面の形状が扇形の一部なのが多い
  • スイス … 歪んでいる
  • イタリア … ドイツと同じく扇型カットが多い。多面体や、デザインの凝った瓶も多い
  • フランス … 角ばっている
  • イギリス … 直方体のエッジをまるめた印象
  • アメリカ … 特徴が無いのが特徴?

あまりしっかり調べてないけど、国毎に特徴があって面白いね~

パイロット セーラー プラチナ
Montblanc Pelikan LAMY
STAEDTLER KAWECO ABER-CASTELL
Caran d’Ache
AURORA DELTA MARLEN
VISCONTI OMAS DIAMANTE
WATERMAN S.T. Dupont J. Herbin
PARKER DIAMINE
CROSS SHEAFFER MONTEVERDE

万年筆用のPILOT製70mLインク瓶に関する発見

僕は今まで8年間、毎日PILOTの万年筆 CUSTOM743 でノートを取り、70mLのBBインク瓶を使い続けてきたのだが、今日重大な事実を発見した。

発見したこと

CUSTOM743 の首軸を、 70mLインク瓶の奥の奥までブッ挿しても、ペン先が瓶底に到達しないことを発見した。

f:id:auewe:20170514230930j:plain f:id:auewe:20170514230654j:plain
万年筆の首軸を、首軸受けのプラスチック容器に突っ込んだ様子。このプラスチック容器は普段インク瓶の中に沈んでいる。 ペン先は容器の底に届いていない!

おかげでインク吸入が猛烈に捗るようになった。

吸入スタイル

今までは、瓶底にペン先が接触しないようペンを中空に浮かせつつ吸入していた。 そのため瓶縁に指を掛けてペンを固定しつつ、コンバータの固いボタンを押していたのだが、これは手がつりそうになる。 変に力が入って瓶が転倒しそうになるし、指はインクで汚れるし、ペン先を瓶底に押し付けて壊してしまう不安もあった。

しかし、実は瓶の設計上、ペン先が瓶底に届くことはなかったのだ。 今後は奥まで思いっきりブッ挿して、全力でボタン連打していく所存。 どれだけ奥まで刺しても、ペン先が潰れることはないのだ。

f:id:auewe:20170514230925j:plain f:id:auewe:20170514230919j:plain
今までのインク吸入。薬指を瓶縁に掛けてペンを中空に浮かせていた。 今後のインク吸入。全力で押し付けていく。

蛇足

なぜもっと早く気付けなかったのか。。。 パイロットはもっと積極的に「ペン先は底に当たらないよ」と喧伝してほしいですね。 CUSTOM743のニブは823と同型でかなりデカいし、 他の万年筆でもペン先は瓶底に届かないだろう。多分。

あと(これは有名だが)、70mLインク瓶は瓶を逆さにすると入れやすくなる。

f:id:auewe:20170514230915j:plain

PILOTの細字万年筆は超品質いいし、BBインクはフロー最高だし、耐水性もすごいらしいし、瓶の構造も最高だし、今後も使い続けようと思う。 あとブログ用に写真撮ってて気づいたけど、瓶の形がナナメに歪んでるな。

LaTeXでjpg挿入時に拡張子が大文字だとハマる

Windows7のC:/w32tex/…にインストールしたplatex+dvipdfmxを使っていたとき、 jpgだとセーフでJPGだとアウトだった。問題解決に一時間ぐらいかかったわ。

\documentclass[12pt,a4paper]{jreport}
\usepackage[dvipdfmx]{graphicx}
\begin{document}

% セーフ、自動でa.xbbが作成される
\begin{figure}[]
  \includegraphics[]{a.jpg}
\end{figure}

% セーフ、自動でdir/b.xbbが作成される
\begin{figure}[]
  \includegraphics[]{dir/b.jpg}
\end{figure}

% アウト、自動でdir/c.xbbが作成されない
% 自力で extractbb c.JPG したり ebb c.JPG しても読み込まれない。絶望
\begin{figure}[]
  \includegraphics[]{dir/c.JPG}
\end{figure}

\end{document}

Makefile文法ミニマム

自分用のメモとして、Makefileの文法で忘れがちな部分をまとめておく。

想定してるのは GNU make 4.2.1 で、LinuxMac OS X で普通に make コマンドを叩いたときに走るやつだ。 FreeBSDSolaris では gmake コマンドらしい。 Windowsでは http://gnuwin32.sourceforge.net/packages/make.htm からインストールできる。

以下では具体例として

$ gcc src/main.c -o obj/main.o
$ gcc src/A.c    -o obj/A.o
$ gcc obj/main.o    obj/A.o -o exec.out

を自動化するためのMakefileを考える。

文法ミニマム

CC    := gcc
ALL_C := $(wildcard src/*.c)                  # src/main.c src/A.c
ALL_O := $(patsubst src/%.c,obj/%.o,$(ALL_C)) # obj/main.o obj/A.o

exec.out: $(ALL_O) # 一番最初に設定した依存関係は、make (引数なし)で実行可
  $(CC) $^ -o $@

%.o: %.c           # パターンマッチ。他に A.o:... があれば、そちらが優先
  $(CC) -c $< -o $@

.PHONY: clean      # clean というファイルが存在しても、無視して make clean できる
clean:
   @rm -rf *.out ./obj/*.o     # @をつければ、結果が表示されない

ここで使った文法をまとめておこう。 以下の11項目を覚えるだけで、かなり高度なMakefileが作れるようになるぞ。 まずは文字列の扱いだが

  • HOGE := foo bar で定義し $(HOGE) で参照
  • ファイル検索は $(wildcard src/*.c)
  • 置換は $(patsubst a%b,c%d,aXXXb aYYYb) —> cXXXd cYYYd

patsubstは半角スペースも文字とみなすので、$(patsubst A, B, C) みたいに空白を入れてはいけない。 次に特殊変数だが

  • $@ は生成ファイル。アットマークは arrive atのatと同じ雰囲気
  • $< は依存ファイルのうち一番 <— 側に書かれたやつ
  • $^ は依存ファイル全て。^は左端から右端までをつまみ上げる雰囲気
  • %.o:... があれば、A.oが必要となった際、%をAに置換して実行される

例えば特殊変数 $@ の拡張子.oを.cに置換する場合は $(patsubst %.o,%.c,$@) とする。 そのほかの注意点は

  • .PHONY: clean しておけば、cleanというファイルが無視される
  • @rm -rf のように @ をつければサイレントモードになる

ちなみに今回は出現しなかったけど、他のファイルと連携する際は

  • Makefile から sub/Makefile に書かれた make hoge を実行するには cd sub && $(MAKE) hoge "CC=$(CC)"
  • 外部ファイル hoge を読み込むには -include hoge

ヘッダファイル

これだけだと「ヘッダファイル無いやんけ!」という指摘がくるので、記事を書いた。参考にしてくれ。 auewe.hatenablog.com

どうでもいい話。

Makefileの書き方が覚えられない。習得しても、なぜかすぐに忘れる。

Bitbucketの自分用リポジトリを調べたら、5年間あわせて25個のMakefileが見つかった。 普通これだけ書けば、自動的にメイクマスターになれるはずなのに、いまだに 「$@だっけ%@だっけ?」 などと悩んだり、置換やワイルドカードの使い方をググったりしている。 原因は、Makefileは文法がキモいうえに中途半端な機能が中途半端に揃っているせいだと思う。 メイクマスターになるためには、まず汎用性の高い便利な機能だけをしっかり覚えるのが重要なのではないか。

過去の自分と決別すべく、今回こそMakefile文法ミニマムをしっかり暗記しようと思う~

Makefileでソース、ヘッダファイルの依存関係を処理

大規模なC言語プログラムでは、ソースやヘッダファイルが複雑な依存関係を持つため、それらを自動解決してくれるMakefileが欲しくなる。 欲望を具体化すると

  • ソース、ヘッダ、オブジェクトファイルは異なるディレクトリに入れたい
  • 依存関係を認識し、更新すべきオブジェクトファイルを自動検出
  • 新しいソース、ヘッダファイルを作成した際、Makefileを書き換ずに済む
  • 他のプロジェクトにも使いまわせる汎用性
  • Makefile自体が短く、保守しやすい

だいぶ贅沢だが、頑張ってMakefileをこしらえたので、得られた知見を忘れないうちにまとめておく。 以前書いたMakefile文法ミニマムも参考にしてくれ。

ディレクトリ構成

root
+----src/
|    +---- main.c   # mA.h        を読み込む
|    +---- A.c      # mA.h と A.h を読み込む
|  
+----inc/
|    +---- mA.h     # 依存なし
|    +---- A.h      # B.h を読み込む
|    +---- B.h      # 依存なし
|  
+----obj/
|    +---- main.o   # makeコマンドで作成される
|    +---- A.o      # makeコマンドで作成される
|    +---- Makefile # *.o を作成
|
+---- exec.out      # makeコマンドで作成される
+---- Makefile      # exec.out を作成

*.c *.h *.o はそれぞれ別ディレクトリで管理する。 もちろん依存関係を考慮して

  • A.c が更新されれば、A.o を更新
  • mA.h が更新されれば、main.o と A.o を更新
  • B.h が更新されれば、A.o を更新

といった具合でオブジェクトファイルを作り直し、exec.out に再リンクしたいのだ。

Makefile

この状況に対処するには、まず root/Makefile を作る

# root/Makefile

CC     := gcc
ALL_C  := $(wildcard src/*.c)                   # src/main.c src/A.c
ALL_O  := $(patsubst src/%.c,obj/%.o,$(ALL_C))  # obj/main.o obj/A.o
ALL_CH := $(wildcard src/*.c inc/*.h)           # src/*.c inc/*.h

exec.out: $(ALL_CH)
  cd obj && $(MAKE) "CC=$(CC)"  # obj/Makefile を実行する (ALL_Oが作成される)
  $(CC) $(ALL_O) -o $@

.PHONY: clean
clean:
   @rm -rf *.out obj/*.o obj/*.d

このファイルの処理内容は Makefile文法ミニマム を見れば理解できるだろう。 要するにソースやヘッダファイルに更新があれば exec.out をリビルドするのだが、 更新すべきオブジェクトファイルの検出と再コンパイルは root/obj/Makefileに丸投げしている。 この root/obj/Makefile

# root/obj/Makefile

ALL_C := $(wildcard ../src/*.c)              # ../src/main.c ../src/A.c
ALL_O := $(patsubst ../src/%.c,%.o,$(ALL_C)) # main.o A.o
ALL_D := $(patsubst ../src/%.c,%.d,$(ALL_C)) # main.d A.d
ALL_H := $(wildcard ../inc/*.h)              # ../inc/mA.h ../inc/A.h ../inc/B.h

.PHONY: dummy             # dummy というファイルは作成されないので PHONY 指定
dummy: $(ALL_O)           # 実行するには ALL_O が必要 --> 下の %.o:... で作成

%.o: ../src/%.c           # A.c 以外の依存ファイルは、下の -include A.d で設定される
  $(CC) -c $< -o $@

%.d: ../src/%.c $(ALL_H)  # 下の -include 命令から呼ばれる
  cpp -MM $< -MF $@       # A.c の依存関係をMakefile形式で書いた A.d を生成

-include $(ALL_D)         # main.d と A.d を読み込む (無い場合は %.d:... で作成)

ファイルの冒頭の ALL_CALL_O は意味がわかると思うが、ALL_Dについては説明が必要だろう。 実は gcc にはプリプロセッサの cpp が含まれており、

$ cpp -MM A.c -MF A.d

とすることで A.c が参照している他のソースやヘッダファイルの情報を Makefile 形式で取得できる。 A.d の中身は以下のような感じ

A.o: ../src/A.c ../inc/mA.h ../inc/A.h ../inc/B.h

これを見れば、A.d を読み込む Makefile は A.d と同じディレクトリに設置する必要があると分かるだろう。 そうした事情で root/Makefile から root/obj/Makefile を分離した。

cpp して得られた A.d をファイル末尾の -include $(ALL_D) により読み込めば、 既に定義されているパターンマッチが以下のように上書きされる

# 上書き前
%.o: ../src/%.c
  $(CC) -c $< -o $@

# 上書き後
A.o: ../src/A.c ../inc/mA.h ../inc/A.h ../inc/B.h
  $(CC) -c $< -o $@

これでオブジェクトファイルの依存関係が解決し、正しく再コンパイルできるようになったわけだ。

まとめ

make コマンドを叩いた後の処理内容をまとめておく。

  1. root ディレクトリで make コマンドを叩く。
  2. root/Makefile の最初のビルド命令 exec.out: $(ALL_CH) に基づき、任意のソース・ヘッダファイルが変化していれば exec.out を更新しようとする。
  3. cd obj && $(MAKE) により、root/obj/Makefile の make を実行。
  4. root/obj/Makefile の最終行 -include $(ALL_D) により、A.d と main.d を読み込もうとする。
  5. %.d: ../src/%.c $(ALL_H) により、対象のソースか任意のヘッダファイルが変化していれば A.d と main.d を更新。
  6. A.d と main.d を読み込む。
  7. パターンマッチの %.o: ../src/%.c が A.d と main.d に書かれた依存関係で上書きされる。
  8. root/obj/Makefile の最初のビルド命令 dummy: $(ALL_O) を実行。
  9. dummyの処理は何もないが、依存ファイルとして main.o と A.o が指定されているので、それらを更新しようとする。
  10. (7で上書きした)依存関係に基づいて、main.o と A.o を更新する必要があるか判断。
  11. 必要と判断されれば $(CC) -c $< -o $@ を実行して、main.o と A.o を更新。
  12. dummy の処理(何もしない)が終わったので、root/Makefile に戻る。
  13. $(CC) $(ALL_O) -o $@ を実行し、exec.out を更新。

うーむややこしい。 処理順序としては 1,2,3 の後に 8,9,10,… としたいわけだが、8を行う前に root/obj/Makefile の事前準備として 4,5,6,7 が実行される感じだな。

C言語からLAPACKのzheevを呼んでエルミート行列を対角化

昔書いた記事のコメントでzheevの使い方を書けと言われた。 なので書いた。僕は優しいなあ。 こういうのは他人が書いた記事を鵜呑みにするより、zheev.fでググってヒットする一次情報を見るほうがいいと思うよ。 Fortranが分からなくても普通に読めると思う。 僕(を含めた一般ユーザー)の解説は往々にして間違ってるわけで、過信するのはこわいいいい

/*
 * SIZE*SIZE型のエルミート行列の固有値と固有ベクトルを計算
 *  (1  -2i)
 *  (+2i 1)
 */

#include <stdio.h>
#include <complex.h>
#define SIZE 2

int main(void) {
// 複素行列(エルミート)の対角化 (入力行列の配列は対角化後にユニタリ行列になる)
  char   jobz = 'V'           ;// 固有ベクトルを計算する
  char   uplo = 'U'           ;// Aに上三角行列を格納
  int    n    = SIZE          ;// 対角化する正方行列のサイズ

  double _Complex A[SIZE*SIZE];// 対角化する行列。対角化後は固有ベクトルが並ぶ
  A[0]=1; A[2]=-2*I;
  A[1]=2*I; A[3]=1;

  double w[SIZE]              ;// 実固有値が小さい順に入る

  int    lda  = SIZE          ;// 対角化する正方行列のサイズ
  double _Complex work[6*SIZE];// 対角化する際に使用するメモリ
  int    lwork = 6*SIZE       ;// workの次元
  double rwork[3*SIZE-2]      ;// 3*SIZE-2で固定
  int    info                 ;// 成功すれば0、失敗すれば0以外を返す

  zheev_( &jobz, &uplo, &n, A, &lda, w, work, &lwork, rwork, &info );
  // 1番目の固有値 : w[0]  1番目の固有ベクトル : (A[0] A[1])
  // 2番目の固有値 : w[1]  2番目の固有ベクトル : (A[2] A[3])

  printf("1番目の固有値:%5.3lf\n",w[0]);
  printf("1番目の固有ベクトル:(%5.3lf %+5.3lf*I %5.3lf %+5.3lf*I)\n",
      creal(A[0]), cimag(A[0]), creal(A[1]), cimag(A[1]));

  printf("1番目の固有値:%5.3lf\n",w[1]);
  printf("2番目の固有ベクトル:(%5.3lf %+5.3lf*I %5.3lf %+5.3lf*I)\n",
      creal(A[2]), cimag(A[2]), creal(A[3]), cimag(A[3]));

  return 0;
}

コンパイルと実行結果

$ gcc hoge.c -lm -lblas -llapack
$ ./a.out
1番目の固有値:-1.000
1番目の固有ベクトル:(0.000 +0.707*I 0.707 +0.000*I)
1番目の固有値:3.000
2番目の固有ベクトル:(0.000 -0.707*I 0.707 +0.000*I)