「~をする関数を最小何行で書けるか」というチャレンジ、よくプログラミングコンテストで聞きますよね。
この記事では私がMATLABのある機能を勉強していた時にたまたま書いた「世界一短い関数」について書きます。「世界一」と言うと物言いがつきそうですが、飽くまで自称ということで...
関数の振る舞い
まずは何をする関数かみてみましょう。
>> data = {123, [1 2 3], 'xyz'};
>> [a,b,c] = my_fun(data)
a =
123
b =
1 2 3
c =
'xyz'
これはセル配列を入力として受け、要素をそれぞれの出力変数に割り当てる関数です。
通常、これをMATLABでやろうとするとこんな感じになります。
>> a = data{1}
a =
123
>> b = data{2}
b =
1 2 3
>> c = data{3}
c =
'xyz'
ということで、まず今回作った関数がそれなりの使い道があるものという事で話を進めます。
関数定義
では、この関数の中身を見てみましょう。
.
.
.
.
.
.
.
.
.
.
.
.
ハラハラ
.
.
.
.
.
.
.
.
.
.
.
.
.
ドキドキ
.
.
.
.
.
.
.
.
.
.
.
ジャジャン!
.
.
.
.
.
function varargout = my_fun(varargout)
以上です。
「世界一短い関数」と聞いて無名関数を想像した方もいるかもしれませんが、これは実際の関数ファイルです。1行の関数ファイル、しかも必要最小限の要素からなっています。
- 関数宣言のキーワード:
function
- 出力引数:
varargout
- 関数名:
my_fun
- 入力引数:
varargout
一見、ただ入力をそのまま出力に流しているように見える関数ですが、実はそこにはMATLAB特有の性質が生かされているのです。
解説
ポイントは変数varargout
です。これは実は普通の変数と違って、出力引数に使われた時に特別な役割を果たすのです。ドキュメンテーションを見てみましょう。
varargout は、任意の数の出力引数を関数で返すことを可能にする関数定義ステートメントの出力変数です。小文字を使用して varargout を指定し、明示的に宣言された出力の後に最後の出力引数として含めます。関数を実行した場合、varargout は 1 行 N 列の cell 配列となり、N は明示的に宣言された出力の後に要求された出力の数を表します。
う~ん。どういう事でしょう。
まず、MATLABの性質として言えるのが、MATLABは柔軟性をもった呼び出し構文に対応しているという事。つまり、関数の書き方によっては異なる引数(数や型)に対応します。例えば、size
関数を見てみましょう。
>> x = [1 2;3 4;5 6];
>> sz = size(x) % 変数のサイズをベクトルとして返す
sz =
3 2
>> [m,n] = size(x) % 第1と第2次元のサイズを個々の変数に返す
m =
3
n =
2
>> n = size(x,2) % 第2次元のサイズを返す
n =
2
このような、柔軟性をもった関数を書くときに便利なのがvarargin
とvarargout
です。
本来はvarargin
は任意の数の入力引数を受け付けるときに使い、varargout
は任意の数の出力引数を返すときに使いますが、今回は入力と出力両方にvarargout
を使うのがポイントです。ここで、もう一度ドキュメンテーションの文章を読み返してみましょう。
関数を実行した場合、varargout は 1 行 N 列の cell 配列となり...
そう。つまり変数varargout
はもともとセル配列という扱いなのです。また、そのセル配列の各要素は関数を呼ぶときに指定した出力引数に順次割り当てられるのです。例えば、もし関数の中でvarargout
が{123, [1 2 3], 'abc'}
というセル配列であったとすると、その関数を
>> [a,b,c] = my_fun(...)
と呼んだとしたらセルの要素がそれぞれa
、b
、c
に代入されるのです。
今回のmy_fun
では入力引数にも同じ名前のvarargout
を使ったので(しかも入力引数のvarargout
は普通の変数扱い)、外から入力されたデータがそのままvarargout
に代入されます。今回の例のように外からセル配列が渡されると、本来varargout
が期待しているものとなるのでうまくいくのです。
まとめてみますと、
- セル配列
data
を関数入力として渡す - 関数内ではセル配列データが変数
varargout
に代入される - セル配列データ
varargout
が任意の数の出力引数として返される - セル配列の要素が用意された出力変数
a
、b
、c
に代入される
補足
今回は計算など一切しない、MATLABの性質を生かした簡単な関数を作成しましたが、本来ですとちゃんとエラーチェックなどをして、入力が期待する型やサイズなのかをチェックするのがベストプラクティスだと思います。ただ、「世界最小」にこだわりたかっただけです。。。
varargout
でできるという事は、varargin
でもできます。
function varargin = my_fun2(varargin)
解説は省きますが、この関数は任意の数の入力を一つのセル配列にまとめてくれます。
>> a = 123;
>> b = [1 2 3];
>> c = 'xyz';
>> data2 = my_fun2(a,b,c)
data2 =
1×3 の cell 配列
{[123]} {1×3 double} {'xyz'}
ただ、これはもっと分かり易い推奨する方法があるので関数にするメリットはありませんね。
>> data2 = {a,b,c}
data2 =
1×3 の cell 配列
{[123]} {1×3 double} {'xyz'}
MATLABプログラミング
私は殆ど他のプログラミング言語の経験はないので(BASIC、Pascal、Javaは多少あり)、MATLABバカといわれてもしょうがないかと思いますが、私がMATLABで一番気に入っているのはプログラミングの部分です。最近はToolboxがかなり充実してきていますが、やはりMATLAB言語の部分にいつも惹かれます。ということで、R2019bで私が一番気に入っている機能を紹介してこの記事は終わりにさせて頂きます。
Function Argument Validation