D言語入門記事 ~関数(1)~

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

D言語入門記事 ~関数(1)~

投稿記事 by tk-xleader » 13年前

今日は連続投稿になっちゃいますが、今回は関数について説明したいと思います

[1] 関数の定義

 ANSI-Cとほとんど同じです。関数名の付け方は、識別子名の命名規則に従います。

CODE:

戻り値型 関数名(引数リスト){
	/*関数定義*/
}
 例を見たほうが早いと思います。func関数は、2つのint型の値を受け取って、その積を返す関数です。

CODE:

import std.stdio;

void main(){
	writefln("%d",func(150,200));
}

int func(int param1,int param2){
	return param1*param2;
}
 定義の仕方もほとんど変わりません。当然、関数から帰るときは、return文を用います。ここまで、C言語とD言語で変わることは全くありません。ところで、今回の例の場合、D言語ではインライン展開される可能性が高いです。というのも、D言語では、コンパイラが最適化の段階で、インライン展開すべきだと判断した関数は、我々がどうしようともインライン展開します。この辺りは、最適化の設定との兼ね合いにもなるんですけどね。

[2] 関数の属性

 ある一定の条件を満たす関数は、特殊な意味を持つことがあります。例えば、外部状態に依存しない関数だったり、例外を投げない関数だったり…
 これらには、属性を付加する必要がある場合が多いですが、属性の付加はとても簡単。関数定義の前に、属性を書き加えるだけです。属性は複数付けることができます。要するに、宣言方法は…

属性名… 戻り値 関数名(引数宣言){………

1.pure関数

 pure関数とは、同じ引数を渡せば、常に同じ戻り値が帰ってくる関数のことです。要するに、引数以外に依存しない関数のことです。定義は簡単、普通の関数定義の前にpure属性を付けるだけです。pure関数では、次のようなことは出来ません。

・immutable定数ではないグローバル変数や静的変数を読み書きすること (h2so5さんの指摘で判明しましたが、グローバル変数に限らず、関数内で寿命を終える変数以外を使えないということです。ごめんなさい。)
・pureでない関数を呼び出すこと
・入出力を行うこと

ってなわけで、実は、さっきのfunc関数がまさにpure関数の条件を満たすのです。だから、func関数に、何の問題も無くpure属性を付加することが出来ます。

2.nothrow関数

 これは、例外を関数外に投げることのない関数のことです。例外については日を改めて説明します。

[3] 関数の引数の属性

 関数の引数にも、属性を付けることができます。これらは、引数宣言の型の前に属性名を付けるだけです。これも、互いに矛盾しない限り、複数の属性を付けることができます。

1.in,out,ref属性

 これらは、引数の読み書きについて規定するものです。属性の意味は、次の通りです。

CODE:

in → 読み取りのみ
out → 書き込みのみ
ref → 読み書き両用

CODE:

import std.stdio;

void main(){
	int result;
	func(150,200,result);
	writefln("%d",result);
}

void func(int param1,int param2,out int result){
	result = param1 * param2;
}
さっきのfunc関数を、結果を戻り値ではなくてout引数で受け取った例です。これによって、関数から結果を受け取るためにポインタを使う必要がありません。
► スポイラーを表示
2.lazy属性

 lazy属性は、引数を遅延評価するときに使う属性です。引数を遅延評価するとはどういうことかというと、普通、関数の引数は、関数が呼び出された時点で評価されて、結果の値が関数に渡されますが、評価するタイミングが、引数が関数内部で使われたときに評価されるのです。

CODE:

import std.stdio;

void main(){
	int val1 = 1,val2 = 1;
	
	writeln("call normal function.");
	normal_func(++val1);
	
	writeln("call lazy function.");
	lazy_func(++val2);
}

void normal_func(int value){
	for(int i = 0; i < 10; i++){
		writef("%d,",value);
	}
	writeln();
}

void lazy_func(lazy int value){
	for(int i = 0; i < 10; i++){
		writef("%d,",value);
	}
	writeln();
}
実行結果
call normal function.
2,2,2,2,2,2,2,2,2,2,
call lazy function.
2,3,4,5,6,7,8,9,10,11,


分かりやすい比較だと思いますが、要するに、lazy_func関数のほうでは、++val2という式が、関数内部で呼ばれるたびに評価されているわけです。だから、for文の中で値が増えているわけです。この辺りは、C言語のマクロ引数に似たところがあると思います。

前の記事「比較演算とプログラムの制御文(1)」 ←→ 次の記事「データ型と式のプロパティ(1)、typeof演算子
最後に編集したユーザー tk-xleader on 2012年3月25日(日) 16:21 [ 編集 3 回目 ]

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前

Re: D言語入門記事 ~関数(1)~

投稿記事 by h2so5 » 13年前

補足ですが、pure関数ではグローバル変数に加えて静的変数へのアクセスもできません。
そうしないと静的変数の状態によって結果が変わってしまう可能性があります。

D言語のドキュメントでは"does not read or write any global mutable state"(グローバルの mutable な状態を読み書きしない)と書かれていますが、どうもこのglobalはスコープではなくて寿命のことを言っているらしく、静的変数も"global mutable state"に含んでるみたいですね。

このサイトには http://stackoverflow.com/questions/5812 ... mming-in-d
They may not access any global mutable state (global variables, thread-local variables, static variables, etc.) or perform I/O.
って書いてあります。

すごく表現が紛らわしいとは思うんですが...
最後に編集したユーザー h2so5 on 2012年3月23日(金) 12:05 [ 編集 1 回目 ]

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

Re: D言語入門記事 ~関数(1)~

投稿記事 by tk-xleader » 13年前

h2so5 さんが書きました:補足ですが、pure関数ではグローバル変数に加えて静的変数へのアクセスもできません。
そうしないと静的変数の状態によって結果が変わってしまう可能性があります。

D言語のドキュメントでは"does not read or write any global mutable state"(グローバルの mutable な状態を読み書きしない)と書かれていますが、どうもこのglobalはスコープではなくて寿命のことを言っているらしく、静的変数も"global mutable state"に含んでるみたいですね。

<中略>

すごく表現が紛らわしいとは思うんですが...
よく考えればそうですね。指摘ありがとうございます、訂正しておきます。
それにしても、本当に、不親切な書き方ですよね…