macで快適にTeX - Sublime Text (make) から直接TeXからPDF作成できるテンプレート

f:id:woodrush:20141030052608p:plain

普段使っている、Sublime Textから「Command + B」を押すだけ(あるいは、好みのエディタからmakeするだけ)でTeX書類が作れる、shellscript・Makeを使ったTeXテンプレートを紹介します。

Mac OS X 10.8, 10.9で(日々)動作確認しています。特に、JPEGPNGなどを指定のフォルダに入れるだけでxbbを作成し、PDF内に貼り付けることができる点や、句読点「、。」の「,.」への自動置換が楽になっています。
Shellscript・Makeに依存しているので、現在Windowsだとうまく動きません。

ただのShellscriptとMakefileなのでこれと言った工夫があまりないのですが、TeX環境作成の一助になればと思います。

入手

https://github.com/hikuta924/TeXReport

こちらにテンプレートの全体、および設定方法があります。TeX自体のインストールや、Sublime Textの設定などもこちらのReadmeに書いてあるので、必要な方は参照して下さい。

使い方

  • TeXReportフォルダ全体をコピーし、名前をつける(ReportAugustなど)
  • report.tex を編集
  • makeする (Sublime Textなら、Command + Bを押すだけ)

主な機能

  • Sublime Textで「Command + B」するだけでPDFが作成可能
    • あるいは、他のエディタからmakeするだけ
  • 句読点「、。」を自動的に全角カンマ・ピリオド「,.」に置換
    • 設定でON/OFF可能
  • 図を簡単に挿入可能
    • PNGJPEGなどを指定のフォルダに入れておくだけで、xbbを作成
    • 図の挿入・参照のTeXコマンド
  • 表をCSVファイルから作成・参照可能
    • エクセル・Numbersから表をコピーしテキストエディタに貼り付けるとtab区切りのCSVが得られるので、エクセル・Numbersから直接表を作成可能
  • Octaveをインストールしている場合、mファイルが生成するグラフを直接貼り付け可能

MATLAB BF Interpreter

bfi.m

function ret = bfi(prog)
    m = zeros(1,1024);
    mp = 1;
    stdout = [];
    prog = regexprep(prog, '[^><\+-\[\]\.,]', '');
    prog = strrep(prog, '+', 'm(mp) = m(mp) + 1;');
    prog = strrep(prog, '-', 'm(mp) = m(mp) - 1;');
    prog = strrep(prog, '>', 'mp = mp + 1;');
    prog = strrep(prog, '<', 'mp = mp - 1;');
    prog = strrep(prog, '[', 'while (m(mp) ~= 0);');
    prog = strrep(prog, ']', 'end;');
    prog = strrep(prog, '.', 'stdout = [stdout m(mp)];');
    prog = strrep(prog, ',', 'm(mp) = input(char(98,102,105,62),char(115));');
    eval(prog);
    ret = char(stdout);
end

bfi2.m

function ret = bfi2(prog)
    mem = zeros(1,1024);
    memptr = 1;
    prgctr = 0;
    repstack = [];
    stdin = [];
    stdout = [];

    prog = regexprep(prog, '[^><\+-\[\]\.,]', '');
    while (prgctr < length(prog))
        disp(mem(1:20));
        prgctr = prgctr + 1;
        switch prog(prgctr)
            case '+'
                mem(memptr) = mem(memptr) + 1;
            case '-'
                mem(memptr) = mem(memptr) - 1;
            case '>'
                memptr = memptr + 1;
            case '<'
                memptr = memptr - 1;
            case '.'
                stdout = [stdout, mem(memptr)];
            case ','
                if length(stdin) == 0
                    disp(char(stdout));
                    disp(char(10));
                    stdin = [stdin input('bfi>', 's')];
                end
                mem(memptr) = stdin(1);
                stdin = stdin(2:end);
            case ']'
                prgctr = repstack(end) - 1;
                repstack = repstack(1:(end-1));
            case '['
                if(mem(memptr) ~= 0)
                    repstack = [repstack prgctr];
                else
                    nestlevel = 1;
                    for prgctr = (prgctr+1):length(prog)
                        switch prog(prgctr)
                            case '['
                                nestlevel = nestlevel + 1;
                            case ']'
                                nestlevel = nestlevel - 1;
                        end
                        if(nestlevel == 0)
                            break;
                        end
                    end
                end
        end
    end
    ret = char(stdout);
end

MATLAB Quine

Quineとは、「自分自身のコードを出力するプログラム」です。
これの難しさは、例えば

printf("hello world!");

という文字列を出力するためには、

printf("printf(\"hello world!\");");

のようなものを書けば良いのですが、こうすると作ったコードが元のコードとは変わってしまっています。そこで更に

printf("printf(\"printf(\"hello world!\");\");");

としたくなるのですが、これもまた元のコードではありません。
この操作を繰り返していくとコードは無限に長くなってしまうため、一見Quineは作成不可能に見えるのですが、チューリング完全な言語には、必ずQuineが存在するという、クリーネの再帰定理が存在します。
クリーネの再帰定理+ Quine が書けることの証明 - まめめも

MATLABで書いてみる

ただのRubyのコードの翻訳なのですが、MATLABでquineを書いてみました。
こちらのサイトに、RubyにおけるQuineがありました。
Quine - Rosetta Code

これをMATLABに書き直すと、次のようになります。

x = 'x = %s; fprintf(x,[char(39) x char(39)])'; fprintf(x,[char(39) x char(39)])

ワークスペースを汚染しないために、更に無名関数による局所変数を用いると、(MATLABの無名関数内で条件分岐・複数行実行を行う方法 - woodrush’s diary参照)

feval(@(x)fprintf(x,[char(39) x char(39)]), 'feval(@(x)fprintf(x,[char(39) x char(39)]), %s)')

と書くことができます。

結果を文字列で返すには、fprintfをsprintfに変えて

feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')

とすれば良いです。この場合、入力文字列をプログラムとして実行するeval関数を使うことで、

>> feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')
ans = feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')
>> eval(feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)'))
ans = feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')
>> eval(eval(eval(eval(feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')))))
ans = feval(@(x)sprintf(x,[char(39) x char(39)]), 'feval(@(x)sprintf(x,[char(39) x char(39)]), %s)')

となり、「プログラムの出力文字列を実行」する操作を永遠に続けることができ、ちゃんとこのコードがquineになっていることが確認できます。

更に、エンコード方法を変えると

feval(@(x)sprintf(char(x-5),[char(39) x char(39)]), 'kj{fq-E-}.xuwnsyk-hmfw-}2:.1`hmfw-8>.%}%hmfw-8>.b.1%*x.')

のようなquineを作ることもできます。

他のMATLABのquineを探していたところ、こちらの記事に、かなり短いMATLABにおけるquineを見つけました。
Quines - From Here to There

また、Quineと言えばYusuke Endoh氏の山手Quineがかなり凄いです。
山手 quine - まめめも

MATLABの無名関数内で条件分岐・複数行実行を行う方法

これらは、以前Obfuscated MATLAB Codeを書いた時に使ったテクニックなのですが、通常のMATLABプログラミングでも使えそうだと思ったので紹介します。
Yet Another Obfuscated MATLAB Code - woodrush’s diary

これらのテクニックの根底にあるのが、fevalという関数です。fevalは、

>> feval(@sin,pi)
ans = 1.2246e-16
>> feval(@(x) x^2, 5)
ans = 25
>> feval(@(x,y) x^2+y^2, 5, 2)
ans = 29

のように、関数ハンドルや無名関数を渡すとその関数を評価してくれる、というものです。

局所変数

条件分岐・複数行実行の基礎には局所変数のテクニックがあります。
これは、

>> f = @(ind) feval((@(array,c) c*ind^2 + sum(array(1:ind))), [1,5,8,13,2,29], 1.3);
>> f(5)
ans = 61.500

のように、無名関数に定数を引数として渡して評価することで、無名関数内でこれらを変数として参照できるようにする、というテクニックです。通常は、「[5,3,6,2](ind)」のように、その場で書いた配列の要素にはアクセス出来ないのですが(Octaveだとできます)、配列を局所変数に入れてしまうことで、その変数を通してインデックス指定によって要素にアクセスすることができるようになります。

条件分岐

これを応用することで、

>> f = (@ (x) ...
         feval((@ (list,ind) list{ind}), ...
               {x^2, sqrt(x)}, ...
               (mod(x,2)==0)*1 + (mod(x,2)==1)*2));
>> f(5)
ans = 2.2361
>> f(6)
ans = 36

のように、条件分岐を行うことができます。これは、xが偶数なら二乗を、奇数ならそのsqrtを返す関数となっています。
この関数fは、「{x^2, sqrt(x)}」の「(mod(x,2)==0)*1 + (mod(x,2)==1)*2」番目の要素にアクセスする関数となっています。
{}内に実行したい候補の関数、「(mod(x,2)…」の部分に「それらを実行したい条件*インデックス」と書いておくことで、条件に応じて実行したい関数を選択することができます。

ただし、{}内は先行評価されてしまうので、例えば

>> array = 1:10;
>> f = (@ (x) ...
         feval((@ (list,ind) list{ind}), ...
               {array(-x), array(x)}, ...
               (x<=0)*1 + (x>0)*2));
>> f(5)
エラー: インデックスが行列の次元を超えています

のようなコードだと、エラーが出てしまいます。このコードはarrayのabs(x)番目の要素を返す関数を書いたものなのですが、「{array(-x), array(x)}」という式が現れた瞬間、自動的に全ての要素が評価されてしまい、必ず負のインデックスが参照されてしまい、エラーになってしまいます。つまり、条件式「(x<=0)*1 + (x>0)*2)」によって選択したい値以外の方の値も評価されてしまっているのです。

これを回避するには、遅延評価をします。これは、

>> array = 1:10;
>> f = (@ (x) ...
         feval( ...
               feval((@ (list,ind) list{ind}), ...
                     {@()array(-x), @()array(x)}, ...
                     (x<=0)*1 + (x>0)*2)));
>> f(5)
ans = 5
>> f(-5)
ans = 5

とすることでできます。このコードの原理は、

>> array = 1:10;
>> @()array(-2)
ans = @()array(-2)
>> feval(@()array(-2))
エラー: インデックスが行列の次元を超えています

のように、無名関数内の表現が、fevalされるまで評価されない事によっています。これは、@()によって中身の式array(-2)が「冷凍」されており、fevalによって「解凍」されて初めて評価される、ということを表しています。つまり、@()によって遅延評価が実現されていることになります。

さきほどのコードでは、「{@()array(-x), @()array(x)}」という式が現れても、@()の中身までもが評価されることはないためエラーが起きず、条件に応じて欲しい式の冷凍品が返ってきます。「冷凍」された式を「解凍」して中身を取り出すにはfevalをしてあげればいいので、この方法では一個前のコードとくらべてfevalを一回を多く行っています。

また、たとえば「5文字以上の文字列を受け取ったらその文字列をそのまま返し返し、5文字未満だった場合受け取ったその文字列の表す関数ハンドルを返す」関数は、次のように書けます。

>> f = (@ (x) ...
         feval((@ (list,ind) ...
                 feval(list{ind}, x)), ...
               {@(y)y, @str2func}, ...
               (length(x)>=5)*1 + ~(length(x)>=5)*2));
>> feval(f('sin'), pi)
ans = 1.2246e-016
>> f('stringover5letters')
ans = stringover5letters

ここでは、入力に恒等関数(@(x)x)を適用することで、入力を変化させないというテクニックを使っています。このように、入力に対して適用したい関数を選ぶ、という形で条件分岐を表現することもできます。

複数行実行

{}内が先行評価されてしまうことを逆手に取って、複数行の実行を行うことができます:

>> hold on;
>> f = (@ (x1,x2,x3,x4) ...
          {plot([x1(1) x2(1)],[x1(2) x2(2)],'-'), ...
           plot([x2(1) x3(1)],[x2(2) x3(2)],'-'), ...
           plot([x3(1) x4(1)],[x3(2) x4(2)],'-'), ...
           plot([x4(1) x1(1)],[x4(2) x1(2)],'-')});
>> f([1,0],[0,1],[-1,0],[0,-1]);

とすることで、四点をつないだプロットを行う関数を作ることができます。

ただしこの方法の弱点は、値を返却しない式を実行するとエラーが出ることです。実際、

>> f = (@ (x1,x2,x3,x4) ...
          {hold on,
           plot([x1(1) x2(1)],[x1(2) x2(2)],'-'), ...
           plot([x2(1) x3(1)],[x2(2) x3(2)],'-'), ...
           plot([x3(1) x4(1)],[x3(2) x4(2)],'-'), ...
           plot([x4(1) x1(1)],[x4(2) x1(2)],'-')});
>> f([1,0],[0,1],[-1,0],[0,-1]);
??? エラー ==> hold: 出力引数が多すぎます

と出てしまいます。値を返さない関数には例えばdispなどもあるため、無名関数内で文字列表示を行う処理はできません。幸いplotやplot3は値を返してくれるため、このような表記ができます。
どうしても文字列表示を行いたい場合は、

%% mydisp.m
function ret = mydisp(str)
   disp(str);
   ret = 0;
end

のように、値を返す自前のdisp関数を作っておくことで、

>> hold on;
>> f = (@ (x1,x2,x3,x4) ...
          {mydisp('A square has been drawn.'),
           {plot([x1(1) x2(1)],[x1(2) x2(2)],'-'), ...
            plot([x2(1) x3(1)],[x2(2) x3(2)],'-'), ...
            plot([x3(1) x4(1)],[x3(2) x4(2)],'-'), ...
            plot([x4(1) x1(1)],[x4(2) x1(2)],'-')}});
>> f([1,0],[0,1],[-1,0],[0,-1]);

のように、無理やり無名関数内で実行させることができます。ここで、出力引数の次元を合わせるために一つ余分にセル配列{}を作っています。

無名再帰

余談ですが、これらの技術に、Yコンビネータという高階関数を組み合わせて使うことで、無名関数だけで再帰を実現する無名再帰を実装することができます。例えば、

f = (@ (in) ...
      feval((@ (Y) ...
              feval(Y( ...
                      (@ (fact) ...
                        (@ (n) ...
                          feval(feval((@ (list,ind) list{ind}), ...
                                      {(@ () 1), (@ () n*fact(n-1))}, ...
                                      (n==1)*1+(n~=1)*2))))), ...
                    in)), ...
            (@ (f) ...
              feval((@ (g1) ...
                      (@ (m1) feval(f(g1(g1)),m1))), ...
                    (@(g2) ...
                      (@ (m2) feval(f(g2(g2)),m2)))))))

とすることで、再帰によって階乗を計算する関数を作ることができます。実際、

>> feval((@ (in) ...
           feval((@ (Y) ...
                   feval(Y( ...
                           (@ (fact) ...
                             (@ (n) ...
                               feval(feval((@ (list,ind) list{ind}), ...
                                           {(@ () 1), (@ () n*fact(n-1))}, ...
                                           (n==1)*1+(n~=1)*2))))), ...
                         in)), ...
                 (@ (f) ...
                   feval((@ (g1) ...
                           (@ (m1) feval(f(g1(g1)),m1))), ...
                         (@(g2) ...
                           (@ (m2) feval(f(g2(g2)),m2))))))), ...
           10)
ans = 3628800

と出てきます。

この階乗関数の作成、およびYコンビネータの勉強には、amachang氏の記事
Y コンビネータって何? - IT戦記
を参考にしました。こちらではJavaScriptを用いてYコンビネータを実装しており、その実装をそのままMATLABの無名関数を使って翻訳したのが上記のコードになります。上記のコードでは、最上層のfevalの引数、「(@ (f)…」以降の表現がYコンビネータと呼ばれる関数になっています。Yコンビネータは無名再帰のテクニックの核をなしている概念で、それ自体でかなり面白いものです。

Yet Another Obfuscated MATLAB Code

なんと、以下のコードはちゃんとした、実行できるMATLABコードになっています。

growth.m

                                        figure(1)
clf,hold on,grid on,view(58,38),feval(@(mm,x)mm(@(O)mm(@(Y,U,V,W,v,nN,NN,Mm,mM,md,Nn)...
mm(@(G)mm(Y(G),x,0,0,v(mM(x))),mm(@(L)(@(M)(@(n,p,q,r)mm(mm(@(l,n)l{n},{@()1,@(){mm(Y...
(L),                                                                             [25*...
v(1.                                                                             /r*(...
n-1)                                              )-   25/                       2*(r...
-1);                                           md((n-1),r)*5                     ^2-(...
r-1*                                          1)*5^  2/  2;0]                    ,[0;...
0;1]                           ,[   1;0        ;0    ],   6)                     ,M(n...
-1,0                        ,0,v(mM(x)))}            },                          ~(n>...
.0)+                       2*~(n  <=  0)))           ))                          ,(@(...
d)((        @(   u,n        ,t    ,N   )(            ((    mm   (mm              ((@(...
l,n)     l{n}),{(@()mm            (@                    ()(mm(@()Nn(u            (1),...
u(2)    ,u(3)  ,(  '*g'           ),                   U,5))  ))  ),(@           ()mm...
((@(     w,    b,   p,            m,    mN   ){(        mm    (@   (A            )Nn(...
A(1,           :)                    ,A(2,:),A(3,:            ),                 '-',...
W,N^           2*                   0.3,V  ,(  mN(N           /6                 ))),...
[u,(           u+    n*   w)]        ))    ,(   mm            (@   (f   ,T,      S){(...
d(u+              n*w,S,T,N-1))            ,(                    d(u+n*w,f(S,n   ,p),...
f(T,             n,p),  N   -1))           ,(                   d(u+n  *w  ,f(S  ,n,p...
*2),              (f    (T    ,n           ,(   p)*   2)),       N-1   ))    },  (@(r...
,n,p                    )r                    *(Mm(p))-nN(r            ,n        )*NN...
(p)+                    (1                   -Mm(p  ))  *n*(           n'        *r))...
,NN(                    m)   *n   -t*         Mm    (m   ),            n*        Mm(m...
)+NN                       (m)*t))}),9.8            /(                           7.14...
-N),                      0.1,2  +1  /10.6          ,-                           .102...
+1,(                       @(    r)   [1            *1                           *.32...
,.22                             ,1                                              *.08...
]*r+                             [0                                              ,1,(...
.5)]                             *(                                              1-r)...
)))}                                                                             ,1+(...
N>0))))))))))),@(f)mm(@(g)@(m,n,o,p)mm(f(g(g)),m,n,o,p),@(g)@(m,n,o,p)mm(f(g(g)),m,n,...
o,p)),O(0),O(1),O(2),O(3),O(4),O(5),O(6),O(7),O(8),O(9)),(@(I)mm(@(l,m)mm(l{1+(I>2)},...
char(m((m(58+I)-46):(m(59+I)-47))+18)),{@(J)J,@str2func},['[O`YS`aWhS1]Z]`ZW\SEWRbV',...
'TZ]]`Q`]aaaW\Q]aa_`b[]R^Z]b!TSdOZ/9>GLQTW[^c']))),@feval,9); %% (c) woodrush (2014) ...


そして、これを実行すると、なんとこの図が出てくるのです!
f:id:woodrush:20140825001754p:plain

手元にMATLABまたはOctave環境がある方は、是非自分で試してみてください。(Octaveだとかなり時間がかかりました。)コードをコピーして、直接コンソールに貼り付けるだけで実行できるようになっています。

このコードは、Yコンビネータの勉強をしていて書いていたものを、面白そうだったので難読化したものです。このコードのポイントは、function構文、そしてグローバル変数すら全く使わず、たった一つの無名関数を実行しているだけで動いているという事です。MATLABの無名関数では1行のコードしか記述・実行できないのですが、その制限の中で

の機能が実現されています。これらの中で特に再帰が一番難しく、そのテクニック中で用いられているのがYコンビネータの概念です。まだYコンビネータの原理については詳しくわかっていないのですが、1行の無名関数だけで再帰を実装できるこのテクニックには、かなり謎に満ちた魅力があります。

追記:これらのうち、再帰以外のテクニックについて詳しく説明したエントリを書きました。
MATLABの無名関数内で条件分岐・複数行実行を行う方法 - woodrush’s diary

Obfuscated Code

このような「一見して全く読めないのにちゃんと実行できて、更に意外な結果が出てくるコード」は一つのジャンルとなっています。特にC言語では IOCCC (The International Obfuscated C Code Contest) という世界大会があるほどで、入賞作品はあっと驚くようなコードがかなり沢山あり、全く違った世界が見えてきます。中でも、Yusuke Endoh氏の作品はかなり息を呑むようなものばかりで、特にASCII Fluidのコードは僕もかなりびっくりさせられました。これはかなりおすすめです。

[C][IOCCC] The 21st IOCCC: Most complex ASCII fluid のエントリ
http://d.hatena.ne.jp/ku-ma-me/20121018/p3

追記:
MATLAB File Exchangeにアップロードしてみました。
Growth - Yet another Obfuscated MATLAB code - File Exchange - MATLAB Central