読者です 読者をやめる 読者になる 読者になる

余白の書きなぐり

aueweのブログ

万年筆用の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)

sedでファイルの最終4行を消す処理について

結論から言うと、sedを使ったスマートな方法は無いので、headコマンドを使いましょう。

$ head -n -4 file

# どうしてもsedを使いたければ
$ cat file |sed -e '$d' |sed -e '$d' |sed -e '$d' |sed -e '$d'

デマが蔓延している

以下のような$-3記法(そんなsedはない)をデッチ上げて紹介している記事が多い。

$ sed -e '$-3,$d' file

デマの発生源?

SBクリエイティブ:【正誤情報】入門UNIXシェルプログラミング 改訂第2版

●第7章:p.175、下から3-1行目

<修正前> また、ファイルの最後の 4 行を消すには、 sed -e '$-3,$d' file と書きます。$-3 は最後から数えて 3 行目、つまり下から 4 行目のことです。

<修正後> ただし、次のように最終行の $ から何行目という指定はできません。 sed -e '$-3,$d' file sed では $-3 のような相対的な行の表現ができずにエラーになります。 最後の行から数行をまとめて消したい場合には、最後の行が何行目なのかを先に調べるか、最後の行だけを消す処理を何回か繰り返すことで対応します。

vimのメモ取り環境

僕は全てのメモを「Gmailで自分宛てに送信」して記録している。 インストールログや便利なツールのメモ、数学のパズル、役所の手続きから買い物リストまで、何もかもをGmailに送信しているのだ。 リマインダもToDoリストも全てメール管理。 閲覧は主にスマホとPCのブラウザで、検索はGmailの検索窓を使っている。

話は変わるが、人生とgitの最大の違いは因果律にあると思う。 人生にタイムマシンは存在せず、git reset --hardgit rebaseなどの便利コマンドもなく、ひたすらコミットを積み重ねることしかできない。 メモは人生のコミットログであり、その媒体に相応しいのは再編集不可能なメールだと、僕は思う。 EvernoteやSlackを使いこなす意識の高い連中には分かるまい~

だいぶ脱線したが、僕のWindowsのメモ取り環境は以下のような感じ。

  1. Winキー + R で「ファイル名を指定して実行」を開く
  2. "gvim"と入力して、gvim.exeを起動
  3. 無名バッファにメモ内容を書く
  4. コマンドラインモードで:Gmailと入力してメール送信

コンセプトは「とにかく素早いメモ書き」「原始的」の2点。 後者について補足すると、vimプラグインは一切使わず、vim以外のフリーソフトも一切使わず、Windows謹製の機能だけで手軽なメモ取り環境を作りたい。 ちなみにLinuxMacだと、msmtp等を使えば原始的なメモ環境の構築など造作も無いので、今回は(厄介な)Windowsに焦点を絞った。

Windowsvimを即座に起動する

パスの通ったディレクトリにgvim.exeへのショートカットリンクgvim.lnkを設置すれば、上で書いた

  1. Winキー + R で「ファイル名を指定して実行」を開く
  2. "gvim"と入力して、gvim.exeを起動

が実現できる。起動時間の短縮には、やはりマウスよりキーボードの方が向いていると思う。
参考:http://www.kamomer.com/entry/windows10-run-filename

gvim.exeの起動は簡単だが、問題はメモ内容をメール送信する部分だ。これを外部ツールに頼らずに実現したい。

vimで書いたメモをSMTPGmailに送信

まず以下のsendGmail.vbsというファイルを、:set fenc=cp932で保存する。 このファイルは標準入力を受け取ってメール送信するVBSスクリプト

Dim objStdIn
Dim intExitCode
Set objStdIn = Wscript.StdIn

Set cdo = Wscript.CreateObject("CDO.Message")
With cdo
  .From             = "hoge@gmail.com"
  .To               = "hoge@gmail.com"
  .Subject          = "自分用メモ"
  .ReplyTo          = "hoge@gmail.com"
  .Textbody         = objStdIn.ReadAll
  .BodyPart.Charset = "Shift-JIS"
end With

schemas_url ="http://schemas.microsoft.com/cdo/configuration/"
With cdo.Configuration.Fields
  .Item (schemas_url & "sendusing")             = 2
  .Item (schemas_url & "smtpserver")            = "smtp.gmail.com"
  .Item (schemas_url & "smtpserverport")        = 465
  .Item (schemas_url & "smtpconnectiontimeout") = 30
  .Item (schemas_url & "smtpusessl")            = true
  .Item (schemas_url & "smtpauthenticate")      = true
  .Item (schemas_url & "sendusername")          = "hoge@gmail.com"
  .Item (schemas_url & "sendpassword")          = "パスワード"
  .Update
end With
cdo.Send

再度注意するが、sendGmail.vbsは:set fenc=cp932で保存するように!。 パスワードは平文直書きで、セキュリティコストは自腹。ワイルドだろう?

次に~/.vimrcに以下を追加

command! Gmail set fenc=cp932 | w !cscript C:/Path/To/sendGmail.vbs

" Markdownで書く場合はfiletype切替コマンド(sm)を自作すると便利
nnoremap sm :<C-u>set filetype=MARKDOWN<CR>

以上で設定は終わりだ。

使い方は冒頭で説明したが、vimでメモを書いた後コマンドラインモードで

:Gmail

とすれば、メモがGmailに送信される。めでたしめでたし。

蛇足

メールをUTF-8で送りたかったので、sendGmail.vbs

.BodyPart.Charset = "Shift-JIS"

のあたりをいじくり回したのだが、うまくいかなかった。 普通メールの文字コードといえばiso-2022-jpなのだが、windowsでコードを変換するには外部のソフトウェアが必要になる(たぶん)。 少なくとも僕の環境では、vim単体で無名バッファ(UTF-8)をiso-2022-jpに変換することができなかった。 またVBSスクリプトでメールを送信する際も、UTF-8は扱いづらいようだった。

" 変換に失敗する
:set fenc=iso-2022-jp
:w
" うまくいく
:set fenc=cp932
:w

文字コードをcp932(Shift-JIS)にしておけば、vimもVBSスクリプトもハマることなく動作した。 やはりWindowsではShift-JISが最強なのだと思いました。

参考