![]() |
| 構造体を分解し,各フィールドを個別の変数として取り出す.save/load や関数引数の処理などで便利なことがある |
- 利用する関数:fieldnames, eval, assignin
![]() |
| 構造体を分解し,各フィールドを個別の変数として取り出す.save/load や関数引数の処理などで便利なことがある |
![]() |
| A three dimensional [4 by 3 by 2] array |
![]() |
| Index / subscript correspondences of [4 by 3 by 2] array 'a' |
通し番号 = (i3-1) * s(2) * s(1) + (i2-1) * s(1) + i1となる.方式 2 では i3 = 1, 方式 3 では i3 = 1, i2 = 1 を補えばよい.例えば,
(3, 1, 2) の要素の通し番号 = (2-1) * 3 * 4 + (1-1) * 4 + 3 = 15となるわけだ.上の図と見比べてみると,方式 1, 2, 3 と通し番号との対応がわかるだろう.
(3, 4) の要素の通し番号 = (1-1) * 3 * 4 + (4-1) * 4 + 3 = 15
(15) の要素の通し番号 = (1-1) * 3 * 4 + (1-1) * 4 + 15 = 15
k = sub2ind( [4, 3, 2], 3, 1, 2 )はいずれも k = 15 を返す.
k = sub2ind( [4, 3, 2], 3, 4 )
k = sub2ind( [4, 3, 2], 15 )
>> i1 = ind2sub( [4, 3, 2], 15 )
i1 =
15
>> [i1, i2] = ind2sub( [4, 3, 2], 15 )
i1 =
3
i2 =
4
>> [i1, i2, i3] = ind2sub( [4, 3, 2], 15 )
i1 =
3
i2 =
1
i3 =
2
- この文章は脱線上等のポリシーのもとで作成されています.
- matlab において最強の文書は,matlab 付属のヘルプドキュメント群です.ともかく help を参照するに限る.ともかく matlab メニューの「ヘルプ→プロダクトのヘルプ」を読め!わからん言葉はコマンドプロンプトから >> help XX で読みまくれ!過去に色んな人に多くの質問を受けましたが,ほとんどはヘルプに書いてあるのを読んでいないだけです.ただ,ある種のことは別の切り口で見るのも有益な場合があります.本記事はそれを期待しています.
例えば,関数を func1 として,その中で変数 a に値 2 を割り当て,disp(a) するとする.これは,コマンドライン上で見える変数と,関数 func1 内で見える変数とが,別の世界に存在しており,互いに隔絶しているからである.一丁目の山田さんと三丁目の山田さんは別人なのである.この各々の状況で見えている世界のことを,matlab では workspace という.他のプログラミング言語では名前空間 namespace やスコープ scope などと呼ぶ.コマンドラインで見えている世界のことを,base workspace という.
ファイル func1.m:
function func1
a = 2;
disp('a in func1');
disp(a);
この関数はコマンドライン上で見える変数 a にどう影響するだろうか?実行してみると,
>> a = 3; func1; disp('a in base workspace'); disp(a);
a in func1
2
a in base workspace
3
となる.func1 内での a への操作は,コマンドライン上から見える変数 a には影響を及ぼしていない.
関数 func2 をなお,この受け渡しは,参照やポインタではなく,値のコピーが渡されることになっている(値渡し).一丁目の連絡文書が三丁目に伝達されるとき,その文書自体が渡されるのではなく,三丁目の人が文書を見ながらせっせと書き写して町内会に持って帰るのだ.一丁目の言う文書 a と,三丁目の言う文書 x は,当初の内容は同じでも物理的には別の存在なのだ.このことと,前記 workspace 間の隔絶とによって,外の世界と中の世界の交流は,入力引数と出力引数という,いわば関数呼び出し国と呼び出され関数国間の出入国ゲートのみに限定され,各々の中では他者の干渉を受けない鎖国が保たれているのである.なお,呼び出され関数国から見た関数呼び出し国のことを,matlab では caller と呼ぶ.呼び出し国の workspace のことを,caller workspace という.コマンドラインから関数を呼び出せば,関数内から見た caller workspace とは base workspace なわけだ.
func2.m:
function y = func2(x)
y = 3 * x;
と作ったとすると,
>> a = 4; b = func2(a);
>> disp('a'); disp(a); disp('b'); disp(b);
a
4
b
12
となる.コマンドライン上での a の中身(=4)は,func2 での x の中身として渡され,func2 での y の中身(=12)は,コマンドライン上での b の中身として渡される.
空の構造体は
とすれば簡単に作れる.では,「フィールドを持たないけど空ではない構造体」はどうすれば作れるか?>> a = struct([])
a =
0x0 struct array with no fields.
すぐに思いつくのは,rmfield を使って,フィールドを消して行くやり方だろう.
これはかっこ悪いので却下.だが実現は出来ることがわかる.>> b.foo = []; a = rmfield(b, 'foo')
a =
1x1 struct array with no fields.
おお,できた.これでいいや.>> a = cell2struct(cell(0,1),{})
a =
1x1 struct array with no fields.
しかし,恒例の whos でみると
のように,メモリが割り当てられていないではないか.しかも a の repmat で b を作ってみても,b も Bytes は 0 のままである.>> whos a
Name Size Bytes Class
a 1x1 0 struct array
Grand total is 0 elements using 0 bytes
実際にメモリを割り当てるにはフィールドを定義しないといけないらしい.AMD64 な Linux 上で AMD64 な MatlabR2006a を使った場合,
となるが,同じことを IA32 な Windows 上で IA32 な Matlab6.5.1 を使って行った場合,>> b = repmat(a,[100,1]);
>> whos b
Name Size Bytes Class
b 100x1 0 struct array
Grand total is 0 elements using 0 bytes
>> b(1).foo = '';
>> whos b
Name Size Bytes Class
b 100x1 952 struct array
Grand total is 100 elements using 952 bytes
となる.なんで違うんでしょう.>> b=repmat(a,[100,1]);
>> whos b
Name Size Bytes Class
b 100x1 0 struct array
Grand total is 0 elements using 0 bytes
>> b(1).foo = '';
>> whos b
Name Size Bytes Class
b 100x1 520 struct array
Grand total is 100 elements using 520 bytes
matlab (今回は 6.5.1 でしか試してません)は複素数について直感に反する挙動を示すことがある.例を見てみよう.
complex じゃなくなってる.およ?一応確認すると,A に代入した値は実数でなく複素数と認識されているはずだ.>> A = repmat(complex(0),[10,1]);
>> whos A
Name Size Bytes Class
A 10x1 160 double array (complex)
Grand total is 10 elements using 160 bytes
>> A(end) = complex(1,0);
>> whos A
Name Size Bytes Class
A 10x1 80 double array
Grand total is 10 elements using 80 bytes
となるからだ.ただし,入力引数に対する inputname のように,出力引数についての情報取得機構が関数 complex 内で働いているとすると,よくわからない話になるかもしれない.>> clear a; a=complex(1,0); whos a
Name Size Bytes Class
a 1x1 16 double array (complex)
Grand total is 1 element using 16 bytes
一つ考えられる仮説は,「虚部が全て 0 の配列は実数型にされてしまう」というものだ.実際,その方がメモリ効率やら計算効率やらよくなる気はする.
念のため,虚部が 0 でない複素数を代入すれば,配列は実数型に変換されることは無い.
そして,要素を虚部が 0 の複素数に順に置き換えていくと,>> A = repmat(complex(0),[10,1]);
>> A(end)=complex(0,1);
>> whos A
Name Size Bytes Class
A 10x1 160 double array (complex)
Grand total is 10 elements using 160 bytes
となった.ここまでは前述の仮説で当たっていそうである.>> A=complex([1,2],[3,4]);
>> A(1)=complex(real(A(1))); whos A
Name Size Bytes Class
A 1x2 32 double array (complex)
Grand total is 2 elements using 32 bytes
>> A(2)=complex(real(A(2))); whos A
Name Size Bytes Class
A 1x2 16 double array
Grand total is 2 elements using 16 bytes
しかし,もちろん話はこれで終わらない.次の例を見てみよう.
なんと配列は複素数のままであり,これは前述の仮説と矛盾する.ところがさらに,代入の方法を変えると,>> A=zeros(1,2);
>> A(:)=complex(0);
>> whos A
Name Size Bytes Class
A 1x2 32 double array (complex)
Grand total is 2 elements using 32 bytes
となって前述の仮説が復活する.どうなってるのという話である.>> A=zeros(1,2);
>> A(1:end)=complex(0);
>> whos A
Name Size Bytes Class
A 1x2 16 double array
Grand total is 2 elements using 16 bytes
ここで新たに,「全代入(whole substition とか?)」という概念を考えてみる.
全代入と考えるのは次の4つ.
いや~.重箱の隅つつくといろいろ出てくるなぁ.>> A=complex(rand(3));
>> A(:)=complex(0); whos A
Name Size Bytes Class
A 3x3 144 double array (complex)
Grand total is 9 elements using 144 bytes
>> A(:)=complex(zeros(3)); whos A
Name Size Bytes Class
A 3x3 144 double array (complex)
Grand total is 9 elements using 144 bytes
>> A=complex(rand(3));
>> A(:,:)=complex(0); whos A
Name Size Bytes Class
A 3x3 72 double array
Grand total is 9 elements using 72 bytes
>> A=complex(rand(3));
>> A(:)=complex(zeros(3)) + 1; whos A
Name Size Bytes Class
A 3x3 72 double array
Grand total is 9 elements using 72 bytes
matlab 上で関数を呼び出すと疑似コンパイル(p-code)され,メモリに残る.
メモリ上の p-code buffer に存在する m-file の名前を見るには,inmem という関数を使う.
これで,実際に呼ばれた関数の一覧が得られる.
関数のコードを走査して再帰的に依存を調べるには,depfun という関数を使う.
depfun の賢さは限定的なので,もれなくわかると期待してはいけない.depdir も同様.
profile なんかも結構役に立つ.単に echo してみるのも面白いかもしれない.
Matlab では,配列の最終要素のインデクスを表す end という指定法がある.
この end にまつわる小ネタ.
配列に次々要素を追加していく,という処理をスクリプト中に書く場合,番号を決め打ちする書き方
をすると,順番を入れ替えたいときや,間に要素を挟みたいときに一大事になる.a(1) = 1;
a(2) = 2;
a(3) = 3;
そこで,こんな書き方が楽チンだ.
これなら入れ替えや追加をコピペで簡単に行える.a(1) = 1;
a(end+1) = 2;
a(end+1) = 3;
ちなみにこう書いた場合,リストは横に,つまり2次元目に伸びていく.上の例では,a は 1 x 3 のサイズになる.
もともとが 3 x 1 だったりした場合,4 x 1 に伸びる.もともとが 3 x 2 だったりした場合,
のように怒られる.この場合でも,伸ばす次元が明示的になるような書き方,すなわち>> a=rand(3,2);
>> a(end+1)=3
??? In an assignment A(I) = B, a matrix A cannot be resized.
のようにすれば,各次元についての end としてちゃんと解釈される.>> a=rand(3,2);
>> a(3,end+1)=3
a =
0.1934 0.5417 0
0.6822 0.1509 0
0.3028 0.6979 3.0000
>>
実は上記の怒られ方は end に限ったことではない.つまり,
とこういうことなのだ.しかし,>> a=rand(3,2);
>> a(7)=3
??? In an assignment A(I) = B, a matrix A cannot be resized.
とこういうことでもある.ここで a(end-1) としてももちろん同じ.結局,>> a=rand(3,2);
>> a(5)=3
a =
0.3412 0.3093
0.5341 3.0000
0.7271 0.5681
補足.もちろん,end+1 に限らず end+10 のような指定も可能です.
>> a=struct('f',{'foo','bar'})次に,小括弧で普通に参照してみる.
a =
1x2 struct array with fields:
f
>> a(1)構造体が一つ返ってくる.さらに,インデクスリストで参照してみる.
ans =
f: 'foo'
>> a(:)構造体配列が返される.コロン単独 : で参照すると一次元ベクトル(縦に並んだ配列)が返されるのは,
ans =
2x1 struct array with fields:
f
>> a(1).fであって全く普通だ.しかし!インデクスで参照してフィールドを指定すると,
ans =
foo
>> a(:).fとなり,また単にフィールドを指定した場合にも
ans =
foo
ans =
bar
>> a.f
ans =
foo
ans =
bar
>> clear a b cてな感じ.連結 {} や [] を使って単純に書ける.
>> a.label='small';a.value=100;b.label='small';b.value=200;c.label='large';c.value=1000;d=[a,b,c]
d =
1x3 struct array with fields:
label
value
>> small_ones = find(strcmp({d.label},'small'))
small_ones =
1 2
>> under150 = small_ones(find([d(small_ones).value]<150))
under150 =
1
>> [d.label]=deal('tiny','little','huge');d.labelなんてこともできる.
ans =
tiny
ans =
little
ans =
huge
>> a={1,2};次に,小括弧で普通にアクセスしてみる.
>> a(1)このとき,a(1) 自体はセル配列である.つまり,b=a(1) とすれば,b は 1 by 1 のセル配列となる.
ans =
[1]
>> a(:)ここでももちろん a(:) 自体はセル配列である.つまり,b=a(:) とすれば,b は 2 by 1 のセル配列となる.
ans =
[1]
[2]
>> a{1}このとき,a{1} は数値である.さらに,インデクスリストでアクセスしてみる.ここからが面白いわけですよ.
ans =
1
>> a{:}なんと ans が 2 つ.つまり 2 つのセンテンスが評価されたことがわかる.
ans =
1
ans =
2
>> b=a{:}ただし b=a{1} のような単一要素の参照ならもちろん代入できる.
??? Illegal right hand side in assignment. Too many elements.
>> a={2,2};となる.つまり,b(2,2) という指定の本質は,
>> b=[1,2;3,4];
>> b(2,2)
ans =
4
>> b(a{:})
ans =
4
function a = myfunc(varargin)とすれば,即座にラッパーが書けるのだ.これは応用しやすい.
a = someotherfunc(varargin{:});
>> a={1,2;3,4}となってしまうので気をつけよう.
a =
[1] [2]
[3] [4]
>> {a{:,:}}
ans =
[1] [3] [2] [4]
b = [a{:}];でいいわけだ.また,次元に関しては,代入先を予め設定しておいてから
b = zeros(size(a));のように代入するか,あるいは reshape を使って
b(:) = [a{:}]'; %この転置はいるのか?手元の R2006a ではあってもなくてもエラーも出ないし結果も同じだった
b = reshape([a{:}],size(a));などとすればよいだろう.
W = diag(sparse(w));
C1 = sparse(B);
C1(m*k,n*k) = 0;
C2 = reshape(C1,[m*k*n,k]);
C2(m*(k*n+1),k) = 0;
C = reshape(C2, [m*k,n*k+1]);
C(:,end) = [];