その漫画自炊オタクはImageJマクロに恋をする

プログラミングを用いた、自炊漫画の画像処理

【ImageJマクロ超入門】#6 自作関数でラクをする

 

f:id:yu3xx:20210511232024p:plain

 

 

皆さまこんにちは。

マイベストアルバム(洋楽)はMarlena Shaw の『Who is This Bitch Anyway?』です、yu3xx(ゆーさんちょめちょめ)です。

フー・イズ・ジス・ビッチ、エニウェイ?

フー・イズ・ジス・ビッチ、エニウェイ?

Amazon

 

 

 前回のおさらい


前回は「配列」を扱うための重要な知識として「参照渡し」と「値渡し」の違いについて勉強しました。


今回は、いろいろ作ってみたくなる「自作関数」についてです。


もくじ

 

 

自作関数とは?


自作関数とは「自分で付けた名前」をもつ「自分の好きな処理を実行できる命令」のことです。


「よく使う命令なんだけど、書くとちょっと長くなる」っていうコードを何度も書くのはかったるいですよね。

 

自作関数を使うと、コードの最後なんかに一度だけ関数を定義しておいて、あとは自分で付けた関数名で何度でも呼び出すことが出来ます。


「ユーザー定義関数」と呼ぶこともあります。

 

自作関数の作り方


書き方のルールはこんな感じです。


function お好みの関数名(引数){
	
	お好みの処理
	
	retuen 変数
}

 
引数(ひきすう)とは、関数に外から突っ込む値のことです。

 

例えば「y = 3x」の式に「x = 4」を突っ込むとします。この「x = 4」が引数です。

引数は複数でも良いですし、処理によっては無くてもOKです。


retuenは、関数内で計算した結果を、関数の外に出してあげる命令です。

「返り値」とか「戻り値」などと言います。

関数によっては、「返り値」がない場合もあります。


自作関数の定義場所は、使いたいマクロの中だったらどこでもOKです。

マクロの「最初」でも「最後」でも「中間」でも、上記の書き方のルールで書いてあればそれでOKです。

 

ポイント

 ・function定義は、マクロ内ならどこでもOK

 ・引数はあってもなくてもOK

 ・return(返り値)もあってもなくてもOK

 

では、かんたんな実例で見ていきましょう!

 

サンプル1(引数と返り値アリ)

1から10までの整数の合計値が55というのは割と有名ですよね。では100までの合計値は何でしょうか?

それを自動計算してくれる自作関数です。

//example 6-1

kotae = ruikei(10);
print(kotae);

kotae = ruikei(100);
print(kotae);

kotae = ruikei(1000);
print(kotae);

kotae = ruikei(10000);
print(kotae);

//-----------------------------------------------------------------------------

function ruikei(x){
	sum = 0;
	for(i=1; i<=x; i++){
		sum = sum + i;
	}
	return sum;
}

//-----------------------------------------------------------------------------

 

まず関数定義の部分を見て下さい。

コメントアウトした「--------」でくくっている場所が関数を定義してる部分です。

この「--------」は私の好みです。くくっておくと見やすいかなと思っています。無くても良いです。

 

自作関数の記述内容
 ① 関数は「ruikei」命名されていて、引数はx

 ② 1からxまでの整数値を、iでループを回しながらsumに足していく

 ③ sumはreturnによって「関数の結果」として外に出される


この関数を実際の処理で使っている部分がコード前半です。

例えばruikei(100)は「関数ruikeiのxの部分に、100を突っ込むんだよね!」という命令です。

そして計算された「変数sum」の値が「変数kotae」の中に格納されていきます。

 

実行結果はこちら。

f:id:yu3xx:20210511234717p:plain
計算式自体は下で定義しているので、引数を変えるだけで何度でも再利用できます。


これが「自作関数の仕事」です。

 

サンプル2(引数と返り値ナシ)

開いている画像のファイル名、縦横pixel数をLogに表示させる関数です。

//example 6-2

//あらかじめ適当な1枚画像を開いておく

getGazouInfo();

//-----------------------------------------------------------------------------

function getGazouInfo(){

	name = getTitle();
	w = getWidth();
	h = getHeight();
	
	print("File :", name);
	print("width :", w);
	print("height :", h);
}

//-----------------------------------------------------------------------------

 getGazouInfo();と唱えるだけで画像の基本情報がLogに表示されます。


このように結果をLogウィンドウに表示するだけの場合は、「返り値」がなくてもOKです。

 

ここまででユーザー定義関数の基本の紹介は終了です。

 

自作関数で「配列」を扱う時の注意点


最後に少しややこしい話です。


自作関数は「配列」を扱うことも出来ます。

 

 

重要でややこしくて注意すべきなのは、

「返り値」として配列を出力する際の受け渡し方法は「参照渡し」である

という点です


「引数の配列」に関数内で処理を加え、その配列を「返り値として外に出す」場合、「返り値を受け取った配列」は、その後も「元の配列」と「値を共有してしまう」ことが問題となります。

 

f:id:yu3xx:20210512013400p:plain


「参照渡し」の難しさは、配列の記事でも細かく解説していますので、こちらも参考にしてみて下さい。


うまく説明出来るでしょうか。実例でも見てみましょう!

 

サンプル3(返り値を参照渡しするときの問題点)

自作関数によって配列Aに+10した配列Bを作成します。その後、Bだけにさらに処理を加えて、AとBの値がどうなるかを観察しましょう。

//example 6-3

A = newArray(100,100,100);

//初期状態のAをカクニン
print("初期A =");
Array.print(A);

//自作関数でAに+10したBを作成
B = plusTen(A);

//中身チェック
print("");
print("自作関数でAに+10したBを作成した。");
print("感覚的にはAは(100,100,100)のままのはず。");
print("自作関数後のA =");
Array.print(A);
print("自作関数後のB =");
Array.print(B);

//ここで試しにBだけに+1000してみる
B[0] = B[0]+1000;
B[1] = B[1]+1000;
B[2] = B[2]+1000;

//中身チェック
print("");
print("追加処理でBだけ+1000した。");
print("感覚的にはAには+1000されてないはず。");
print("追加処理後のA =");
Array.print(A);
print("追加処理後のB =");
Array.print(B);

//-----------------------------------------------------------------------------

function plusTen(X){
	for(i=0; i<X.length; i++){
		X[i] = X[i] + 10;
	}
	return X;
}

//-----------------------------------------------------------------------------

 

マクロを実行してみると...

f:id:yu3xx:20210512004904p:plain

 B = plusTen(A);

この命令により、Bは、plusTen(A)の返り値を「参照渡し」によって受け取ります

plusTen(A)の返り値はX、つまり引数であるAなので、BとAは同じ場所にあるデータを共有します。

 

そのため、自作関数を通した後は、AとBは常に同じ値となってしまいます。


これを解消するためにはどうするか。

あらかじめAの「値渡しコピー」を作っておいて、そのコピーを関数に突っ込みます

 

コードを書いてみるとこんな感じです。

 

サンプル4(返り値参照渡し問題の解決策)

//example 6-4

A = newArray(100,100,100);
C = Array.copy(A);

//初期状態のAをカクニン
print("初期A =");
Array.print(A);

//自作関数でAに+10したBを作成
B = plusTen(C);

//中身チェック
print("");
print("自作関数でAに+10したBを作成した。");
print("感覚的にはAは(100,100,100)のままのはず。");
print("自作関数後のA =");
Array.print(A);
print("自作関数後のB =");
Array.print(B);

//ここで試しにBだけに+1000してみる
B[0] = B[0]+1000;
B[1] = B[1]+1000;
B[2] = B[2]+1000;

//中身チェック
print("");
print("追加処理でBだけ+1000した。");
print("感覚的にはAには+1000されてないはず。");
print("追加処理後のA =");
Array.print(A);
print("追加処理後のB =");
Array.print(B);

//-----------------------------------------------------------------------------

function plusTen(X){
	for(i=0; i<X.length; i++){
		X[i] = X[i] + 10;
	}
	return X;
}

//-----------------------------------------------------------------------------

 マクロを実行してみると...

f:id:yu3xx:20210512005156p:plain
配列Aを「値渡し」でコピーした配列Cを作成し、plusTenには配列Cを引数としました。

 

これならその後、配列Aを計算に使う場合でも安心です!

 

 

ちなみにですが、配列を返り値にする時には、「渡し先の配列(B=plusTen(C);のBのこと!)」はnewArrayで配列宣言しなくてもOKです!空気を読んで勝手に配列にしてくれます。

 

 

今回は以上です。

 

「自作関数」を使うと、マクロをスッキリ書ける上に、一度書いてしまえば様々なマクロでコピペして使いまわすことも出来るので、すごく重宝します。


ポイントおさらい

 ・function定義は、マクロ内ならどこでもOK

 ・引数はあってもなくてもOK

 ・return(返り値)もあってもなくてもOK

 ・配列を引数、返り値にする関数も作れる

 ・配列が返り値になる場合は、「参照渡し」で受け渡す

 

 

次回は「ROIを使って自動解析をしよう」の回です!こちらもご覧ください!

 

 


おしまい

 

 

  

 

・公開したマクロのまとめ

imagej-jisui.hatenablog.com