はじめに
Matlabの記述方法、何が良いのかなとふと不安になったので、次のドキュメントを訳して見ようかなと思います。
Matlab Style Guideline 2.0
書き方
1. camelCaseで書こう
matlabでは基本的に、変数も関数名もcamelCaseで書きます。
matlab使いは、数式を投影したものをコードに書きがちですが、意味を持った変数で実装する際には意味のわかる変数名にしましょう。
recommend | not recommend |
---|---|
wage = hourlyRate * nHours |
z = x * y |
2. 単数・複数の違いは、明示的に
英語だとword, wordsなどとしがちですが、ミスを誘発するので、明示的に変えましょう。
point, pointArray, pointList
3. 単一の数字を指すときはsuffixまたはprefixをつける
tableNo, employeeNo
のように、Numberの略称を単語の後につけるか、
次のfor loopのようにprefixを単語の前につけると良いです。
for iFile = 1 : nFile
files(iFile) = ..
end
iFileでiteratorを、nFileでファイルの数(number of file)を指します。
4. 論理型変数は肯定形が良い
論理型は、主語を抜かした形で書くことが多く、matlabにもisstruct
といった関数が標準実装されています。
is*
形式で書くと、if文と合わせたときに
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の予約語とかぶると後々バグの温床となるのでやめましょう。
alpha, angle, axes, axis, balance, beta, ...
7. Hungarian notationは避けよう
recommend | not recommend |
---|---|
thetaDegrees |
uint8thetaDegrees |
8. 定数は大文字で
他の言語でもやる方法ですが、定数は大文字で定義します。あと、短ければ良いというものでもなく、具体的に書きましょう
recommend | not recommend |
---|---|
MAX_ITERATIONS | TEN, MAXIT |
複数並べる際には、より一般的な単語が上位に来るように命名します。
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. スクリプト冒頭で変数・インスタンスを呼びましょう
コメントもつけると親切です。他の言語でもこの慣習があって、インプットは一箇所にまとめると可読性が上がります。
THRESHOLD = 10; % Maximum noise level found.
17. グローバル定数・変数は極力減らそう
.mファイルで定数・変数を呼ぶと実行速度が落ちます。
.matファイルから定数・変数を読み出すときには定数・変数の更新がしづらくなります。
なので、できるだけグローバル定数・変数は減らしましょう。
18. iteration前に変数は定義しておく
メモリ利用の観点から、先にresult変数を定義すべきです。
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文横にまとめて書こうとせず、論理型変数を定義してスッキリさせましょう。
if (value>=lowerLimit) & (value<=upperLimit)...
& ~ismember(value, valueArray)
...
end
isvalid = (value>=lowerLimit) & (value<=upperLimit);
isNew = ~ismember(value, valueArray);
if (isValid & isNew)
...
end
20. if statement直後には比較的良く起きる場合を、else以降はたまに起きる場合を記述
これも、可読性向上の為。珍しい事象が冒頭に来ると、読み手は少し動揺します。
fid = fopen(fileName);
if (fid ~= -1)
% よく起きる
else
% 珍しい事象
...
end
21. switch文ではotherwise
を極力定義
エラーが生じて落ちるのを防ぐために、otherwise
処理を書き足しましょう。
switch (condition)
case ABC
statement1;
case DEF
statement2;
otherwise
statementException;% this part is important to avoid errors
end
ちなみに、変数の内容(文字列とか)で切り替える場合はswitchを、条件文で分岐を書く場合にはif文を使いましょう。
22. 常識的なところ
i. 括弧をつけよう
matlabは括弧を外した表現を許容していますが、括弧は付けましょう
disp('testMessage'); % good
disp testMessage % avoid
ii. 小数は0を書こう
桁数を読み間違えたりするので、書きましょう。
THRESHOLD = 0.5; % good
THRESHOLD = .5; % avoid
iii. 比較は分かりやすく
iSample>=maxSamples; % use
~(iSample<maxSamples) % dead
23. 変数の型チェックはvalidateattributes
やinputParser
を使う
ここではvalidateattributes
のみ載せますが、inputParser
も使いやすいと思います。特に、関数の入力をチェックするときにはとても便利です。
>> 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. 見やすいところで「コード上は」改行すること
...
をつけると、コード上での改行が可能になります。処理の上では一行とみなされますので、...
を駆使することで、読みやすく書き換える事が可能です。
totalSum = a + b + c + ...
d + e;
function(param1, param2, ...
param3)
instance.setText(['Long line split'...
'into two parts']);
個人的には、演算子は改行後に含めたほうが好きです。twitter bootstrapのjavascript部分がこんな感じの実装でした。
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行で書けますよ、というもの。
if(condition), statement; end
while(condition), statement; end
for iTest = 1:nTest, statement; end
27. スペース推奨
関数の入力をギュウギュウに書く人がいますが、空白があったほうが読みやすいです。
simpleAverage = (firstTerm + secondTerm) / two; % good
simpleAverage=(firstTerm+secondTerm)/two; % not good
foo(alpha, beta, gamma)% good
foo(alpha,beta,gamma) % bad
28. 字下げを揃える
開始箇所を揃えると見やすくなったります。これはもうmatlabとか関係なく、プログラミング言語一般に対していえます。
value = (10 * nDimes) + ...
(5 * nNickels) + ...
(1 * nPennies);% 開始箇所を揃えた
29. write comments in English!
matlabが親切にも日本語版を出していて、しかもencodingがShift-JISだったりします。
しかし、英語環境でそれを開くと、日本語で記述された箇所は文字化けします。死にます。なので、コメントは(フォルダ名も)基本的には英語で書きましょう。もう一度言います、死にます。
30. .mファイルは関数化したほうが良い
スクラッチしているときはスクリプト形式で良いのですが、基本的には関数化すると良いです。それは、不要な変数を誤って使い、解析結果が無意味になるのを防ぐ意味があります。
他にも、parallel toolboxを使う際には関数化してある必要があります。
おまけ
matlabをきれいに書くtipsを書きます。ご参考までに。
I. 改行
[]で作れる行列、{}で作れるcellは、セミコロンを打たずに改行すると縦ベクトル/縦セルが作れます。
>> 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
これをうまく取り入れると、ピリオド3つ(...
)の
インライン処理を書かずにコード上で開業したことになるので、おすすめです。
一行で定義したセルを作りたい場合は、transposeすると良いです。
>> colorspec = {'-'
'--'
'.-'}' % transpose
colorspec =
'-' '--' '.-'
II. 構造体のforeach
このセクション末のサンプルコードに例を示しながら説明します。
構造体Rmse
に対して、fieldnames(Rmse)'
で構造体の子要素をセルで取得できます。取得したセルをそのままiteration変数に入れたforループを回すと、子要素名が1x1のセルとしてiteration変数childStruct
に入ります。
そこで、childStruct{1}
とすればセルから子要素名が取得できるのですが、ここで元々の構造体Rmseに対してRmse.(childStruct{1})
(すなわち、Rmse.('pls')
などとするのと同等)とすると、構造体の子要素にアクセスできます。
>> 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年に同じ文献を日本語訳したカッチリ版が出てました...
強そう!