MATLAB Live ScriptからMarkdownへのデータの書き出し

[Live Script でデータ解析をすれば、コードとその計算結果が全部一箇所に保存できるので、これだけで十分、と思っていました](http://qiita.com/kouichi-c-nakamura/items/6233d78180a9f5e5f4ce)が、実際にその計算結果を利用して論文に記載しようと思うと、長大な Live Script そのままでは、コードに埋もれてしまって、**肝心の数字を見つけるのがかなり鬱陶しい**ということを自覚しました。

数字を見つけて、Wordにコピーするだけで数日かかってしまった上、後から問題が見つかって計算をやり直したので、もうこの作業を繰り返すわけにはいかんと結論したのでした。

そこで、結局、Live Script の中で、重要な箇所について、Live Script の出力に数字を吐き出しながら、同時に、テキストファイルに markdown を書き出して、要点だけが見られるようにまとめることにしました。



# 私の解

正直あまりエレガントとは言えませんが、落ち着いたのはこのような形です。



もともと Live Scriptで、このような形で、セミコロンを省くことで計算結果を吐き出せていたとします。

```matlab
length(A)

table(B,C,D,...
  'VariableNames',{'John','Ben','Tom'})
```



それをこのように書き換えます。

```matlab
fid = fopen('foo.md','w','n','UTF-8');
fp = @(x,y) fprintfcodemd(fid,x,y);

fprintf(fid,'# Length of A');
s = 'length(A)';
fp(s,evalc(s));

fprintf(fid,'# Data of 3 people');
s = [...
  "table(B,C,D,...";...
  "'VariableNames',{'John','Ben','Tom'})";...
  ];
fp(s,evalc(regexprep([s{:}],'\.{3}','')));

fclose(fid);
```



# 解説



```matlab
fid = fopen('foo.md','w','n','UTF-8');
```

`'w'` オプションで、`'foo.md'`という名前の markdownファイルを新たに作ります。既に同じ名前のファイルが存在する場合は上書きします。

`'n','UTF-8'`によって、Unicodeに文字コード指定します。`evalc`出力には頻繁に`×`（掛け算記号）が用いており、Unicodeに指定しないとWindowsでは文字化けが起こります。



```matlab
fprintf(fid,'# Length of A\n');
```

これは上で作ったファイルに、markdownで見出し（`#`は`H1`）を書き込んでいます。



```matlab
fp = @(x,y) fprintfcodemd(fid,x,y);
```

これは、鬱陶しいので表記を短くするための行です。

`@`で無名関数（anonymous function）を作り、`fp`という短い名前で呼べるようにします。



```matlab
fp(s,evalc(s));
```

関数`fprintfcodemd`は、markdownのコードブロック（```` `で挟まれた部分）に、プロンプトの記号 `>>`と、文字列`s`そのものを書き込み、その下に`evalc(s)`の出力、つまり`s`に書かれたコードに対するコマンドウィンドウの出力を書き込みます。このとき、実は`evalc`の出力は太字指定などの HTML タグを含んでしまっているので、関数`fprintfcodemd`の内部でこれを除去しています。

`evalc`の実行時に、`s`に登場する変数全てにアクセスする必要があるため、この`evalc`は関数`fprintfcodemd`に内蔵させることができません。



markdownの出力はこのようなイメージです。

```
# Length of A
​```
>> length(A)

ans = 

    19

​```
```



```matlab
s = {'table(B,C,D,...';...
  '''VariableNames'',{''John'',''Ben'',''Tom''})'};
```

二つ目の例は、`s`が複数行にまたがっているのが厄介です。一行に無理やり突っ込んでもいいですが、非常に長くなってしまう場合もあるのでいつも良いとは限りません。

ここでは、仕方なく、コードを文字列のセル配列 cell array of strings として`s`に格納しています。その時に、`'`を繰り返してエスケープせざるを得ないので可読性がとても悪化しています。



```matlab 
s = ["table(B,C,D,...";...
  "'VariableNames',{'John','Ben','Tom'})"];
```

このように、代案として、最近新しく加わった **string型**を利用して単引用符`'`の代わりに、複引用符`"`を使うと、あまり元のコードを乱さずに済みます。遥かに**可読性が高い**ですし、書くのも楽ですね。



```matlab
fp(s,evalc(regexprep([s{:}],'\.{3}','')))
```

`s`をそのまま`evalc`に渡すとエラーが出るので、`char`型に変換する必要があります。ここでは、`regexprep([s{:}],'\.{3}','')`によって、一行のコマンドに直しています。



# 使用した関数 fprintfcodemd

```matlab
function fprintfcodemd(fid,expr,evalcout)

p = inputParser;
p.addRequired('fid',@(x) isscalar(x));
p.addRequired('expr',@(x) ischar(x) || iscellstr(x) || isstring(x) );
p.addRequired('evalcout',@(x) ischar(x));
p.parse(fid,expr,evalcout);

if ischar(expr)
    expr = {regexprep(expr,';$','')};
elseif isstring(expr)
    expr = cellstr(expr);
end

% print to file fid

fprintf(fid,'\n```\n>> ');
fprintf(fid,'%s\n',expr{:});
evalcout_ = regexprep(evalcout,'<[/\w][^>]*[\w"]>',''); % get rid of HTML tags
fprintf(fid,evalcout_); %
fprintf(fid,'```\n\n');

% print to Command Window
fprintf('\n>> ');
fprintf('%s\n',expr{:});

%fprintf(['>> ',expr,'\n']);
fprintf(evalcout);
fprintf('\n');

end
```