[#matlab] Matlabのベスト・プラクティス

# はじめに

Matlabの記述方法、何が良いのかなとふと不安になったので、次のドキュメントを訳して見ようかなと思います。
[Matlab Style Guideline 2.0](https://jp.mathworks.com/matlabcentral/fileexchange/46056-matlab-style-guidelines-2-0)

# 書き方
## 1. camelCaseで書こう
matlabでは基本的に、変数も関数名もcamelCaseで書きます。
matlab使いは、数式を投影したものをコードに書きがちですが、意味を持った変数で実装する際には意味のわかる変数名にしましょう。

| recommend | not recommend |
|:-:|:-:|
| `wage = hourlyRate * nHours` | `z = x * y`  |


## 2. 単数・複数の違いは、明示的に
英語だとword, wordsなどとしがちですが、ミスを誘発するので、明示的に変えましょう。

```matlabsession:recommended
point, pointArray, pointList
```

## 3. 単一の数字を指すときはsuffixまたはprefixをつける
`tableNo, employeeNo`のように、Numberの略称を単語の後につけるか、
次のfor loopのようにprefixを単語の前につけると良いです。

```matlabsession
for iFile = 1 : nFile
   files(iFile) = ..
end
```

iFileでiteratorを、nFileでファイルの数(number of file)を指します。

## 4. 論理型変数は肯定形が良い
論理型は、主語を抜かした形で書くことが多く、matlabにも`isstruct`といった関数が標準実装されています。

`is*`形式で書くと、if文と合わせたときに

```matlabsession
if isstruct(Struct1)
  ...
end
```
と書けるので可読性が上がります。その際、否定を含めた変数名定義はやめましょう。

| recommend | not recommend |
|:-:|:-:|
| `isFound` and `~isFound` | `~isNotFound` |

## 5. 略語(abbreviation)もcamelCaseで

camelCaseの慣習を崩すので、手法の略称を大文字で続けるのは避けましょう、とあります。(ホントかなぁ？)

| recommend | not recommend |
|:-:|:-:|
| html, isUsaSpecific, checTiffFormat() | hTML, isUSASpecific, checkTIFFFormat() |

機械学習の分野でいえば、Pls, Pcaとかってなります...

## 6. 予約語とかぶらないように
当然ですが、matlabの予約語とかぶると後々バグの温床となるのでやめましょう。

```matlabsession:not_recommended_words
alpha, angle, axes, axis, balance, beta, ...
```

## 7. Hungarian notationは避けよう

| recommend | not recommend |
|:-:|:-:|
| `thetaDegrees` | `uint8thetaDegrees` |

## 8. 定数は大文字で
他の言語でもやる方法ですが、定数は大文字で定義します。あと、短ければ良いというものでもなく、具体的に書きましょう

| recommend | not recommend |
|:-:|:-:|
| MAX_ITERATIONS |  TEN, MAXIT |

複数並べる際には、より一般的な単語が上位に来るように命名します。

```matlabsession
COLOR_RED   = 'r';
COLOR_GREEN = 'g';
COLOR_BLUE  = 'b';% color specs
% 構造体も良いかと.
COLOR.RED   = 'r';
COLOR.GREEN = 'g';
COLOR.BLUE  = 'b';% color specs
```

## 9. 関数により毎回定数を返す場合は、全部小文字かcamelCaseで書く
matlabでたまに実装されていますが、関数として固定の出力をする場合には大文字以外で命名しましょう。
例: `pi`


## 10. 構造体は大文字始まり
多分C++とかの思想だと思いますが、構造体は大文字始まりです...
あと、field名に構造体名を含まないようにしましょう。

| recommend | not recommend |
|:-:|:-:|
| Segment.length | Segment.segmentLength |

## 11. 関数名は小文字か、camelCase
先程も書きましたが、全て小文字か、camelCaseで定義します。rubyやpythonのようなsnake_caseは推奨されてないです。多分、ベースがjavaだからではないでしょうか。

名前が長くなるときはcamelCaseが良いでしょう。

## 12. 意味のある関数名にしよう
意味わかんない関数名をつけると、保守性が下がりますので、やめましょう。


| recommend | not recommend |
|:---:|:---:|
| computeTotalWidth | compwid |


## 13. アウトプットが1つの関数は、そのアウトプットの名前を関数名としてください
`computeRmse`なんてクドい関数名は、やめようということですね...

例: `mean`, `standardError`

## 14. 関数は基本的には動詞で始めます

### i. get/setはgetter/setterがあるので注意
クラスやオブジェクトを使える人はご存知かと思いますが、matlabもsetter, getterが予約語として存在します。安易にgetNumberとせずに、`getObj`, `setProperty`など、オブジェクトやプロパティへのアクセスのみに利用してください。

### ii. 計算する関数は, `compute`などで統一
統一したほうが可読性が上がるので、`calcRmse`などと適当につけずに全て`computeRmse`, `computeResponsibility`などと統一しましょう。

### iii. indexを返す関数の際には`find`がよいです
rubyのactive recordでも、条件を満たすデータを探す際に`ClassName.find(:id)`などと検索をします。それにならって、findとしましょう。`get`を使わずに済みますね。
例: `findOldestRecord`, `findTallestMan`

## 15. その他関数の名前の注意
### i. `init`じゃなくて`initialize`にしよう
動詞で明示的に書いたほうが可読性が上がります。アメリカ英語の`initialize`の方が、イギリス英語の`initialise`よりも良いです。
例: `initializeProblemState`

### ii. 論理型を返す関数は`is`で始める

例: `isOverpriced`, `iscomplete`

### iii. `is`以外にも、yes/noで答えられそうな名前にしよう
例: `hasLicense`, `canEvaluate`, `shouldSort`

## 16. スクリプト冒頭で変数・インスタンスを呼びましょう
コメントもつけると親切です。他の言語でもこの慣習があって、インプットは一箇所にまとめると可読性が上がります。

```matlabsession:example
THRESHOLD = 10; % Maximum noise level found.
```

## 17. グローバル定数・変数は極力減らそう
.mファイルで定数・変数を呼ぶと実行速度が落ちます。
.matファイルから定数・変数を読み出すときには定数・変数の更新がしづらくなります。
なので、できるだけグローバル定数・変数は減らしましょう。


## 18. iteration前に変数は定義しておく
メモリ利用の観点から、先にresult変数を定義すべきです。

```matlabsession
result = nan(nnEntries, 1); % define a variable that keep results
for iEntry = 1 : nEntries
  result(iEntry) = foo(iEntry);
end% end for loop of iteration for entries
```

for loop中の`break`, `continue`の使用は最小限にとどめ、`end`の直後に、何が終了したか書きましょう。

## 19. 条件文は分割して見やすく
if文横にまとめて書こうとせず、論理型変数を定義してスッキリさせましょう。

```matlabsession:badCase

if (value>=lowerLimit) & (value<=upperLimit)...
                       & ~ismember(value, valueArray)
...
end

```

```matlabsession:goodCase
isvalid = (value>=lowerLimit) & (value<=upperLimit);
isNew   = ~ismember(value, valueArray);
if (isValid & isNew)
...
end
```

## 20. if statement直後には比較的良く起きる場合を、else以降はたまに起きる場合を記述
これも、可読性向上の為。珍しい事象が冒頭に来ると、読み手は少し動揺します。

```matlabsession
fid = fopen(fileName);
if (fid ~= -1)
% よく起きる
else
% 珍しい事象
...
end
```

## 21. switch文では`otherwise`を極力定義
エラーが生じて落ちるのを防ぐために、`otherwise`処理を書き足しましょう。

```matlabsession:switch-statement
switch (condition)
case ABC
    statement1;
case DEF
    statement2;
otherwise
    statementException;% this part is important to avoid errors
end
```

ちなみに、変数の内容(文字列とか)で切り替える場合はswitchを、条件文で分岐を書く場合にはif文を使いましょう。

## 22. 常識的なところ
### i. 括弧をつけよう
matlabは括弧を外した表現を許容していますが、括弧は付けましょう

```matlabsession
disp('testMessage'); % good
disp testMessage     % avoid
```

### ii. 小数は0を書こう
桁数を読み間違えたりするので、書きましょう。

```matlabsession
THRESHOLD = 0.5; % good
THRESHOLD = .5;  % avoid
```

### iii. 比較は分かりやすく

```matlabsession
iSample>=maxSamples;  % use
~(iSample<maxSamples) % dead
```



## 23. 変数の型チェックは`validateattributes`や`inputParser`を使う

ここでは`validateattributes`のみ載せますが、`inputParser`も使いやすいと思います。特に、関数の入力をチェックするときにはとても便利です。

```matlabsession:example
>> s = @(x) x.^2;% function handle
>> validateattributes(s, {'function_handle'}, {'nonempty'});% ok
>> validateattributes([1 2], {'function_handle'}, {'nonempty'}); % raise error
Expected input to be one of these types:

function_handle

Instead its type was double.
```

## 25. 見やすいところで「コード上は」改行すること
`...`をつけると、コード上での改行が可能になります。処理の上では一行とみなされますので、`...`を駆使することで、読みやすく書き換える事が可能です。

```matlabsession:inline
totalSum = a + b + c + ...
           d + e;
function(param1, param2, ...
         param3)
instance.setText(['Long line split'...
         'into two parts']);
```

個人的には、演算子は改行後に含めたほうが好きです。twitter bootstrapのjavascript部分がこんな感じの実装でした。

```matlabsession:inline
totalSum = a + b + c ...
         + d + e;
function(param1, param2 ...
       , param3)
instance.setText(['Long line split'...
         'into two parts']);
```

## 26. inline if-statement
if, while, forが1行で書けますよ、というもの。

```matlabsession:one-liner
if(condition), statement; end
while(condition), statement; end
for iTest = 1:nTest, statement; end
```

## 27. スペース推奨
関数の入力をギュウギュウに書く人がいますが、空白があったほうが読みやすいです。

```matlabsession
simpleAverage = (firstTerm + secondTerm) / two; % good
simpleAverage=(firstTerm+secondTerm)/two;       % not good

foo(alpha, beta, gamma)% good
foo(alpha,beta,gamma)  % bad
```

## 28. 字下げを揃える
開始箇所を揃えると見やすくなったります。これはもうmatlabとか関係なく、プログラミング言語一般に対していえます。

```matlabsession
value = (10 * nDimes) + ...
        (5  * nNickels) + ...
        (1  * nPennies);% 開始箇所を揃えた
```

## 29. write comments in English!
matlabが親切にも日本語版を出していて、しかもencodingがShift-JISだったりします。
しかし、英語環境でそれを開くと、日本語で記述された箇所は文字化けします。死にます。なので、コメントは(フォルダ名も)基本的には英語で書きましょう。もう一度言います、死にます。

## 30. .mファイルは関数化したほうが良い
スクラッチしているときはスクリプト形式で良いのですが、基本的には関数化すると良いです。それは、不要な変数を誤って使い、解析結果が無意味になるのを防ぐ意味があります。

他にも、parallel toolboxを使う際には関数化してある必要があります。

# おまけ

matlabをきれいに書くtipsを書きます。ご参考までに。

## I. 改行
[]で作れる行列、{}で作れるcellは、セミコロンを打たずに改行すると縦ベクトル/縦セルが作れます。

```matlabsession
>> mat = [1
          2
          3]
mat =
     1
     2
     3
>> colorspec = {'-'
                '--'
                '.-'}
colorspec = 
    '-'
    '--'
    '.-'
>> size(mat), size(colorspec)
ans =
     3     1
ans =
     3     1
>> matrixA = [1 2
              3 4
              5 6]% 2x2 matrix
matrixA =
     1     2
     3     4
     5     6
```

これをうまく取り入れると、ピリオド３つ(`...`)の
インライン処理を書かずにコード上で開業したことになるので、おすすめです。

一行で定義したセルを作りたい場合は、transposeすると良いです。

```matlabsession
>> colorspec = {'-'
                '--'
                '.-'}' % transpose
colorspec = 
    '-'    '--'    '.-'
```


## II. 構造体のforeach
このセクション末のサンプルコードに例を示しながら説明します。
構造体`Rmse`に対して、`fieldnames(Rmse)'`で構造体の子要素をセルで取得できます。取得したセルをそのままiteration変数に入れたforループを回すと、子要素名が1x1のセルとしてiteration変数`childStruct`に入ります。

そこで、`childStruct{1}`とすればセルから子要素名が取得できるのですが、ここで元々の構造体Rmseに対して`Rmse.(childStruct{1})`(すなわち、`Rmse.('pls')`などとするのと同等)とすると、構造体の子要素にアクセスできます。

```matlabsession
>> Rmse.pls = 0; Rmse.svr = 0;% initialize
>> for childStruct = fieldnames(Rmse)'% for loop
    Rmse.(childStruct{1}) = 10;
end
>> Rmse
Rmse = 
    pls: 10
    svr: 10
>> Rmse.('pls') % allow struct.(STRING) access
ans =
    10
>> Rmse.('svr')
ans =
    10
```

こんな感じです。ご質問・修正すべき点などあれば、コメントいただければと思います。

## 2019/03/19 追記

2017年に同じ文献を日本語訳した[カッチリ版](http://www.mk.ecei.tohoku.ac.jp/dsptext/MatlabStyle2book_Japanese.pdf)が出てました...
強そう！