静岡理工科大学 菅沼ホーム 目次 索引

関数

  1. 関数の定義

    1. 引数とデフォルト

        ここでは,関数の定義方法について,例を使用しながら説明していきます.まず,最初は,最も基本的な関数です.3 つのデータを受け取り,それらの和を返す関数です.以下に示すような内容のファイル test.py を作成し,Window のコマンドプロンプト上で,
      py -3 test.py [data]   # [ ] 内はオプション
      				
      と入力すれば結果が得られるはずです.関数は,本来,独立したプログラムです.しかし,関数を test.py 内に記述すると,関数内からその外側の変数を参照できてしまいます.そのため,関数は,別のファイル function.py 内に記述しています.
      01	# -*- coding: UTF-8 -*-
      02	def func(s1, s2 = 20, s3 = 30) :
      03		"""example of function"""
      04		s  = s1 + s2 + s3
      05		s1 = 100
      06		return s
      ----------------------------------
      07	# -*- coding: UTF-8 -*-
      08	from function import func
      09	import sys
      10	print(func.__doc__)
      11	p1 = 10
      12	print("sum = ", func(p1))   # func(s1 = 10) or func(10) でも可
      13	print("p1 = ", p1)
      14	print("sum = ", func(p1, s2 = 50))   # func(10, 50) でも可
      15	print(len(sys.argv), sys.argv[0])
      16	#print(len(sys.argv), sys.argv[0], sys.argv[1])   # オプションを付加した場合
      				
      02 行目

        関数の定義は,キーワード def で始まり,関数名 func の後ろの括弧の中に仮引数のリスト(この例では,s1,s2,s3 )が並びます.これらの仮引数は,関数を呼び出す際にも,実引数として,同じ位置,同じ順番で記述する必要がありますので,位置指定引数と呼ばれます.なお,変数名は異なっても構いません( 12,14 行目において,p1 が s1 に対応).

        引数に対しては,それらが省略されたときの値,つまり,デフォルト値を設定することができます(デフォルト引数).この例では,引数 s2 及び s3 に対して設定しています.その結果,12 行目では s2 及び s3 を省略,また,14 行目では s3 を省略していますが,正しく計算されています.なお,最初または中間にある位置指定引数,例えば,s1 または s2 に対してだけデフォルト値を設定することはできません.なお,Python には,関数名の多重定義機能はありません.

      03 行目

        関数の本体を記述する文の最初の行は文字列リテラルにすることもできます.その場合,この文字列は関数のドキュメンテーション文字列と呼ばれ,変数 __doc__ によって参照できます( 10 行目参照).

      05 行目

        先に述べたように,関数は,本来,独立したプログラムです.しかし,関数における処理をする際に何らかの情報が欲しい場合があります.その情報を引き渡す手段が引数です(外部からの情報を必要としない場合は引数を記述しなくて良い).基本的に,実引数の値がコピーされて,仮引数に渡されます.従って,実引数と仮引数は全く独立した変数(値)ですので,この例の場合,変数 s1 の値を変更しても,変数 p1 の値は変化しません( 13 行目に対する出力結果参照).この点は,実引数と仮引数の変数名が同じである場合も同様です.

      06 行目

        return 文は,関数を呼び出した先に結果を返すための文です.この例の場合は,s1,s2,s3 の和が戻され,12,14 行目の結果となります.なお,結果を戻さない場合は必要ありません.

      08 行目

        function モジュール内にある関数 func を取り込んでいます.

      09 行目

        15,16 行目のために必要です.

      12,14 行目

        Python では,引数の渡し方として,「 s2 = 50 」のような形をとることができますが,関数は独立したプログラムであるべきなのに,関数の仮引数の名前を使用するといった仕様はいかがなものでしょうか.

      15,16 行目

        変数 sys.argv には,C/C++ 風に言えば,main 関数の引数に対する情報が入っています.プログラムを実行する際,何らかの付加的情報を必要とする場合があります.そのようなとき,上に示した使用方法における [ ] 内の data のように,その情報を記述可能です.これらの情報は,変数 sys.argv に入れられて渡されます.オプションを記述しなかった場合( 15 行目)は,実行したファイル名だけが入っています.data というオプションを付加すると,変数 sys.argv のサイズは 2 となり,その名前も渡されます( 16 行目と一番最後の出力).

        このプログラムを実行すると,以下に示すような出力が得られます.
      example of function
      sum =  60
      p1 =  10
      sum =  90
      1 test.py
      #2 test.py data
      					

      C++ の場合

      			// 通常の呼び出し
      01	int func(int s1, int s2 = 20, int s3 = 30)
      02	{
      03		int s  = s1 + s2 + s3;
      04		s1 = 100;
      05		return s;
      06	}
      07	int main()
      08	{
      09		int p1 = 10;
      10		cout << "sum = " << func(p1) << endl;
      11		cout << "p1 = " << p1 << endl;
      12		cout << "sum = " << func(p1, 50) << endl;
      13		return 0;
      14	}
      ----------------------------------
      			// アドレス渡し
      15	int func(int *s1, int s2 = 20, int s3 = 30)
      16	{
      17		int s  = *s1 + s2 + s3;
      18		*s1 = 100;
      19		return s;
      20	}
      21	int main()
      22	{
      23		int p1 = 10;
      24		cout << "sum = " << func(&p1) << endl;
      25		cout << "p1 = " << p1 << endl;
      26		cout << "sum = " << func(&p1, 50) << endl;
      27		return 0;
      28	}
      ----------------------------------
      			// 参照渡し
      29	int func(int &s1, int s2 = 20, int s3 = 30)
      30	// int func(const int &s1, int s2 = 20, int s3 = 30)
      31	{
      32		int s  = s1 + s2 + s3;
      33		s1 = 100;
      34		return s;
      35	}
      36	int main()
      37	{
      38		int p1 = 10;
      39		cout << "sum = " << func(p1) << endl;
      40		cout << "p1 = " << p1 << endl;
      41		cout << "sum = " << func(p1, 50) << endl;
      42		return 0;
      43	}
      				
      01 行目~ 14 行目

        最も一般的な呼び出しであり,Pythn の場合とほとんど同じです.このプログラムを実行すると,以下に示すような出力が得られます.
      sum =  60
      p1 =  10
      sum =  90
      					
      15 行目~ 28 行目

        24,26 行目に見るように,変数のアドレスを引数として渡しています.アドレスの場合もコピーして渡されますが,そのアドレスが指す位置は同じです.従って,関数内においてそのアドレスが指す位置にある値を変化させれば( 18 行目),main 関数内の変数 p1 の値も変化します.このプログラムを実行すると,以下に示すような出力が得られます.
      sum = 60
      p1 = 100
      sum = 180
      					
      29 行目~ 43 行目

        変数の参照を渡す場合です( 29 行目).記述方法は異なりますが,本質的に,アドレスを渡す場合と同様です.なお,30 行目のように,const 指定を行えば,値の変更が不可能になります.このプログラムを実行すると,以下に示すような出力が得られます.
      sum = 60
      p1 = 100
      sum = 180
      					
        以上のように,C/C++ においては,引数の渡し方が厳密に規定されており,誤った処理を行いにくくなっています.
        次の例では,引数にリストを用い,引数 lt のデフォルト値が [] (空のリスト)に設定されています.このプログラムを実行すると,次のような結果が得られます.明らかに奇異に感じると思います.これは,リストに対してデフォルトを設定した場合,それが評価されるのは最初に関数が呼ばれたときだけだからです.
      [1]
      [1, 2]
      [1, 2, 3]
      				
      # -*- coding: UTF-8 -*-
      def func(a, lt = []) :
      	lt.append(a)
      	return lt
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import func
      print(func(1))
      print(func(2))
      print(func(3))
      				
        しかし,次の例を見てください.上と全く同じようなプログラムです.異なるのは,引数がリストではなく整数になっているだけです.この場合は,期待したように,常に,引数 a の値に sum のデフォルト値 0 が加えられた結果が出力されます.このように,変数の型によって異なる行動を行うような方法は使用すべきではありません.もちろん,Python の仕様も変更すべきです.少なくとも,上のプログラムにおいては,デフォルト値を設定すべきではありません.
      # -*- coding: UTF-8 -*-
      def func(a, sum = 0) :
      	sum += a
      	return sum
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import func
      print(func(1))
      print(func(2))
      print(func(3))
      				

    2. 引数の型(リスト)
        デフォルト引数の例で示したプログラムと同様,リストを引数とする場合について考えてみます.まず,01 行目~ 03 行目の関数において,return 文がないことに注意してください.関数から戻る値がないにもかかわらず,06 行目において初期設定されたリストの値は,
      [1]
      [1, 2]
      [1, 2, 3]
      				
      のように変化しています.つまり,引数として渡されたデータを関数内で修正すると,その関数を呼んだ側の対応するデータも変化しています.これは,整数型のような単純変数は別として,リストのような場合は,C/C++ におけるアドレス渡しや参照渡しと同じような方法で,引数に渡されるからです.記述方法が単純変数と同じであるにもかかわらず,異なる結果になることは,プログラムの誤りの原因にもなりかねます.十分注意してください.
      01	# -*- coding: UTF-8 -*-
      02	def func(a, lt) :
      03		lt.append(a)
      ----------------------------------
      04	# -*- coding: UTF-8 -*-
      05	from function import func
      06	lt = []
      07	func(1, lt)
      08	print(lt)
      09	func(2, lt)
      10	print(lt)
      11	func(3, lt)
      12	print(lt)
      				

        以下に示すのは,C/C++ の場合です.C/C++ の例においては,リストの代わりに,配列C++ 標準ライブラリ内の vector クラスを使用しています.プログラム例に見るように,C/C++ においては,単純変数と同様,他の型の変数に対しても同様の処理を行っており,それらが厳密に区別されています.なお,vector クラスにおける << に対しては,変更可能シーケンスに固有の演算の項で示したような << 演算子のオーバーロードが行われています.この点は,以下の例に現れる set クラスmap クラス の使用においても,集合型辞書型の項で示した << 演算子のオーバーロードを行っています.

      C++ の場合

      			// 配列の場合
      01	void func(int *s, int n)
      02	{
      03		for (int i1 = 0; i1 < n; i1++)
      04			s[i1] = i1 + 1;
      05	}
      06	int main()
      07	{
      08		int s[3];
      09		func(s, 3);
      10		for (int i1 = 0; i1 < 3; i1++)
      11			cout << s[i1] << " ";
      12		cout << endl;
      13		return 0;
      14	}
      ----------------------------------
      			// vector クラスの場合
      15	vector<int> func(int a, vector<int> s)
      16	{
      17		s.push_back(a);
      18		return s;
      19	}
      20	int main()
      21	{
      22		vector<int> s;
      23		s = func(1, s);
      24		cout << s;
      25		s = func(2, s);
      26		cout << s;
      27		s = func(3, s);
      28		cout << s;
      29		return 0;
      30	}
      ----------------------------------
      			// vector クラスの場合(アドレス渡し)
      31	void func(int a, vector<int> *s)
      32	{
      33		(*s).push_back(a);
      34	}
      35	int main()
      36	{
      37		vector<int> s;
      38		func(1, &s);
      39		cout << s;
      40		func(2, &s);
      41		cout << s;
      42		func(3, &s);
      43		cout << s;
      44		return 0;
      45	}
      ----------------------------------
      			// vector クラスの場合(参照渡し)
      46	void func(int a, vector<int> &s)
      47	{
      48		s.push_back(a);
      49	}
      50	int main()
      51	{
      52		vector<int> s;
      53		func(1, s);
      54		cout << s;
      55		func(2, s);
      56		cout << s;
      57		func(3, s);
      58		cout << s;
      59		return 0;
      60	}
      				
      01 行目~ 14 行目

        配列に対する処理です.配列の場合は,配列によって確保した領域のアドレスが渡されることになりますので,関数内において,配列要素の値自身が変化します.また,配列においては,要素の数を前もって指定しなければならないため( 08 行目),vector クラスとはかなり異なった処理になります.このプログラムを実行すると,以下に示すような出力が得られます.
      1 2 3
      					
      15 行目~ 30 行目

        単純変数の場合と同様,関数内において,main 関数内で定義された vector クラスのオブジェクト s を変更できないため,return 文が必要になります.このプログラムを実行すると,以下に示すような出力が得られます.
      [1]
      [1, 2]
      [1, 2, 3]
      					
      31 行目~ 45 行目

        アドレス渡しの例です.関数内において,main 関数内で定義された vector クラスのオブジェクト s を変更することができます.このプログラムを実行すると,以下に示すような出力が得られます.
      [1]
      [1, 2]
      [1, 2, 3]
      					
      46 行目~ 60 行目

        参照渡しの例です.アドレス渡しの場合と同様,関数内において,main 関数内で定義された vector クラスのオブジェクト s を変更することができます.このプログラムを実行すると,以下に示すような出力が得られます.
      [1]
      [1, 2]
      [1, 2, 3]
      					

    3. 引数の型(関数)
        Python においては,関数も一つのオブジェクトです.従って,関数(のアドレス)を他の変数に代入すれば,その変数は関数と同じように使用できます( 14,15 行目).また,関数(のアドレス)を関数の引数としても使用可能です( 16,17 行目).
      01	# -*- coding: UTF-8 -*-
      02	def add(s1, s2) :
      03		s = s1 + s2
      04		return s
      05	def sub(s1, s2) :
      06		s = s1 - s2
      07		return s
      08	def add_sub(fun, s1, s2) :
      09		s = fun(s1, s2)
      10		return s
      ----------------------------------
      11	# -*- coding: UTF-8 -*-
      12	from function import add, sub, add_sub
      13	print(add(2, 3))   # 5
      14	kasan = add
      15	print(kasan(2, 3))   # 5
      16	print(add_sub(add, 2, 3))   # 5
      17	print(add_sub(sub, 2, 3))   # -1
      				
        C/C++ においても同様の処理が可能です.C++ では,ラムダ式を使用することによっても似たような処理を行うことができます.

      C++ の場合

      int add(int s1, int s2) {
      	int s = s1 + s2;
      	return s;
      }
      int sub(int s1, int s2) {
      	int s = s1 - s2;
      	return s;
      }
      int add_sub(int (*fun)(int, int), int s1, int s2) {
      	int s = fun(s1, s2);
      	return s;
      }
      int main()
      {
      	printf("%d\n", add(2, 3));   // 5
      	int (*kasan)(int, int) = &add;
      	printf("%d\n", kasan(2, 3));   // 5
      	printf("%d\n", add_sub(add, 2, 3));   // 5
      	printf("%d\n", add_sub(sub, 2, 3));   // -1
      	return 0;
      }
      ---------- 以下,ラムダ式を使用した場合 ----------
      int main()
      {
      	auto add = [](int x, int y) { return x + y; };
      	auto sub = [](int x, int y) { return x - y; };
      	printf("%d\n", add(2, 3));   // 5
      	auto kasan = add;
      	printf("%d\n", kasan(2, 3));   // 5
      	auto add_sub = [](auto sb, int x, int y) { return sb(x, y); };
      	printf("%d\n", add_sub(add, 2, 3));   // 5
      	printf("%d\n", add_sub(sub, 2, 3));   // -1
      	return 0;
      }
      				

    4. 再帰呼び出し
        再帰呼び出しrecursive call )とは,関数内において,その関数自身を呼び出すことです.次の例においては,階乗の計算を再帰呼び出しによって実行しています.
      # -*- coding: UTF-8 -*-
      def kaijo(m) :
      	if m > 1 :
      		s = m * kaijo(m-1)	 # 自分自身を呼んでいる
      	else :
      		s = 1
      	return s
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import kaijo
      m = input("nの値を入力して下さい ")
      n = int(m)
      print(str(n) + "の階乗は=" + str(kaijo(n)))
      				

      C++ の場合

      /**************************/
      /* mの階乗               */
      /*      m : データ        */
      /*      return : nの階乗 */
      /**************************/
      double kaijo(int m)
      {
      	double s;
      	if (m > 1)
      		s = m * kaijo(m-1);	 // 自分自身を呼んでいる
      	else
      		s = 1;
      	return s;
      }
      
      /********/
      /* main */
      /********/
      int main()
      {
      	double kai;
      	int n;
      		// データの入力
      	printf("nの値を入力して下さい ");
      	scanf("%d", &n);
      		// 階乗の計算
      	kai = kaijo(n);
      		// 結果の出力
      	printf("   %dの階乗は=%f\n",n,kai);
      	return 0;
      }
      				

    5. 可変個の引数
        可変個の引数を受け取る関数を定義することもできます.以下の例においては,2 個の引数を利用しています.送られてきた引数は,例に見るように,タプルとして処理されます.*var の後ろにも引数を設定することができますが,key = value という形をとるキーワード引数だけが許されます.C/C++ における可変個の引数に対する概念とはかなり異なっていますので,Python の例と同じような形になるような例を示しています.
      # -*- coding: UTF-8 -*-
      def func(*var, p) :
      	print(var, p)   # (10, 20) 30 を出力
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import func
      func(10, 20, p = 30)   # func(10, 20, 30) はエラー
      				

      C++ の場合

      void func(vector<int> &s, int p)
      {
      	cout << s;   // [10, 20] を出力
      	cout << p << endl;   // 30 を出力
      }
      int main()
      {
      	vector<int> s;
      	s.push_back(10);
      	s.push_back(20);
      	func(s, 30);
      	return 0;
      }
      				
        上と同様,可変個のキーワード引数を持つ関数を定義することも可能です.以下の例( 2 つのキーワード引数を使用)に見るように,key は辞書型データとして処理されます.
      # -*- coding: UTF-8 -*-
      def func(**key) :
      	print(key)   # {'key1': 10, 'key2': 20} を出力
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import func
      func(key1 = 10, key2 = 20)
      				

      C++ の場合

      void func(map<string, int> &m)
      {
      	cout << m;   // {key1 : 10, key2 : 20} を出力
      }
      int main()
      {
      	map<string, int> m;
      	m.insert(pair<string, int>("key1", 10));
      	m.insert(pair<string, int>("key2", 20));
      	func(m);
      	return 0;
      }
      				
        位置指定引数,可変個の引数,及び,可変個のキーワード引数すべてを持つ関数を定義することも可能です.ただし,引数の順番は,下に示す例のように決まっています.
      # -*- coding: UTF-8 -*-
      def func(p1, p2 = 10, *var, **key) :
      	print(p1, p2)   # 1 5 を出力
      	print(var)   # (100, 200) を出力
      	print(key)   # {'key2': -2, 'key1': -1} を出力
      ----------------------------------
      # -*- coding: UTF-8 -*-
      from function import func
      func(1, 5, 100, 200, key1 = -1, key2 = -2)
      				

    6. ローカル変数とグローバル変数

        一般に,関数内で定義された変数は,関数内だけで有効です.しかし,場合によっては,複数の関数内で特定の変数を参照したい場合があります.この様な変数をグローバル変数,また,関数内だけで有効な変数をローカル変数と呼びます.基本的に,関数外で変数を定義すれば,その変数はグローバル変数となり,関数内からもその変数を参照・変更可能となります.下の例における 13 行目の変数 a が相当します.

        ただし,関数内においてグローバル変数を修正する場合は十分注意する必要があります.04 行目が存在しなければ,03,05 行目は,グローバル変数 a の値を出力します.しかし,04 行目によって,グローバル変数と同じ名前の新しいローカル変数が定義されたとみなされます.従って,03 行目は,未定義の変数 a の出力とみなされエラーになります.

        関数 func1 のような形で,関数内でグローバル変数の値を修正しようとすれば,新しいローカル変数の定義とみなされ,グローバル変数の値は変更されません.関数内においてグロ-バル変数の値を修正するためには,08 行目のような宣言が必要です.もしこの宣言がなければ,a は新しいローカル変数とみなされ,関数 func1 の場合と同様,09 行目でエラーになってしまいます.
      01	# -*- coding: UTF-8 -*-
      02	def func1() :
      03	#	print(str(a) + " in func1")   エラー
      04		a = 20
      05		print(str(a) + " in func1")
      06	
      07	def func2() :
      08		global a   # 記述しないとエラー
      09		print(str(a) + " in func2")
      10		a *= 10
      11		print(str(a) + " in func2")
      12	
      13	a = 10
      14	print(a)
      15	func1()
      16	print(a)
      17	func2()
      18	print(a)
      				
        上のプログラムを実行すると以下のような出力が得られます.
      10
      20 in func1
      10
      10 in func2
      100 in func2
      100
      				
        このように分かり難い変数の有効範囲を許して良いのでしょうか.「関数から外部の変数を一切参照できない.必要ならば,引数,または,クラスを通して行う」といった仕様ではだめでしょうか?

  2. ジェネレータ関数

      ジェネレータは,イテレータ(順番にオブジェクトの要素を取り出すための演算子)を作成するための簡潔なツールです.簡単にジェネレータを生成したいときは,ジェネレータ式を利用できます.ジェネレータ式は,丸括弧を使用し,以下のような形になります.なお,引数が一つである関数の引数とする場合は,丸括弧を省略できます.C/C++ には,直接対応するような機能はありませんが,簡単に記述することができます.
    (式 for 式 in イテラブルオブジェクト)   # for を多重に使用しても良い
    			
      これだけでは分かり難いかと思いますので,いくつかの例を挙げておきます.まず,最初は,0 の 2 乗から 9 の 2 乗までを取り出すためのジェネレータ式です.ここで,next() は,イテレータの次の要素を取得するための組み込み関数です.
    >>> g = (pow(x, 2) for x in range(10))
    >>> list(g)
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
     ・・・・・
    >>> list(g)   # 要素を取り出した後は空
    []
    >>> sum(pow(x, 2) for x in range(10))   # 2 乗の和
    285
    			
      C/C++ には全く同じ機能は存在しません.しかし,C++ 標準ライブラリ内のには,複合データ内の要素を順番に取り出して,その要素に指定した処理を行う関数が多く存在します.ここでは,for_eachラムダ式を使用して,上に示した例と同じような処理を行ってみます.この文でも明らかなように,範囲 for 文を使用した方が分かりやすいと思います.

    C++ の場合

    		// for_each
    vector<int> v1 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    vector<int> v2;
    for_each(v1.begin(), v1.end(), [&v2](int x){ v2.push_back(x*x); });
    for (auto x : v2)
    	cout << " " << x;
    cout << endl;
    		// 範囲 for 文
    v2.clear();
    for (auto e : v1)
    	v2.push_back(e*e);
    for (auto x : v2)
    	cout << " " << x;
    cout << endl;
    			
      次は,3 次元ベクトル x と y の内積の例です.ここで,zip() は,複数のイテラブルiterable,構成要素を一度に一つずつ返すことができるオブジェクト)から,それぞれの要素を集めたイテレータを作成するための組み込み関数です.
    >>> x = [1, 2, 3]
    >>> y = [4, 5, 6]
    >>> sum(x1 * y1 for x1, y1 in zip(x, y))
    32
    			
      下に示す C++ のプログラム例においては,内積を計算する関数 inner_product を使用しています.

    C++ の場合

    		// inner_product
    vector<int> v1 {1, 2, 3};
    vector<int> v2 {4, 5, 6};
    auto x = inner_product(v1.begin(), v1.end(), v2.begin(), 0);
    cout << x << endl;
    		// 範囲 for 文
    pair p[] = {{1, 4}, {2, 5}, {3, 6}};
    x = 0;
    for (auto e : p)
    	x += e.first * e.second;
    cout << x << endl;
    			
      以下に示す例における page は,1 行毎のデータが入っているリストです.この例では,page 内に含まれる単語を集め,集合型のデータ unique_words を生成しています.集合型では,同じデータは無視されますので,unique_words は,page に含まれる異なる単語の集合となります.
    >>> page = ["aaa bbb ccc aaa", "aaa bbb ccc", "bbb aaa aaa"]
    >>> unique_words = set(word for line in page for word in line.split())
    >>> unique_words
    {'aaa', 'ccc', 'bbb'}
    			
      下に示す C++ のプログラム例においては,通常の多重ループによって記述しています.Python におけるジェネレータ関数による記述は分かりやすいでしょうか.Python においては,if 文や for 文において,必ず字下げすることを義務づけています.その目的の一つは,プログラムの分かりやすさだと思います.しかし,上に示したジェネレータ関数による記述はこの点に反しています.全体的に,Python は,記述の容易さに重点を置くあまり,分りやすさを犠牲にしているのではないでしょうか.プログラムにとって最も重要なことは,「読みやすく,理解しやすい」ことだと思います.是非,この点に力を入れてコンパイラの設計を行って欲しいと思います.

    C++ の場合

    string page[] = {"aaa bbb ccc aaa", "aaa bbb ccc", "bbb aaa aaa"};
    set<string> word;
    for (auto line : page) {
    	int p1 = 0, p2 = 0;
    	while (p2 >= 0) {   // 単語に分割して set に追加
    		p2 = line.find_first_of(" \n", p1);
    		word.insert(line.substr(p1, p2-p1));
    		if (p2 > 0)
    			p1 = p2 + 1;
    	}
    }
    for (auto e : word)
    	cout << " " << e;
    cout << endl;
    			
      ジェネレータ関数は,通常の関数のように書かれますが,何らかのデータを返すときには yield 文が使われます.ジェネレータ関数が開始されると,最初の yield 式まで処理して一時停止し,呼び出し元へ yield 式で与えた値を返します.ここで言う一時停止とは,ローカル変数の束縛,命令ポインタや内部の評価スタック,そして例外処理を含むすべてのローカル状態が保持されることを意味します.再度,ジェネレータ関数が呼び出されて実行を再開した時,ジェネレータ関数は,yield 式がただの外部呼び出しであったかのように処理を継続します.以下に示す例では,指定した位置から,データを逆方向にイテレートしています.
    >>> def reverse(data, start) :
    ...     for index in range(start, -1, -1) :
    ...         yield data[index]
    ...
    >>> data1 = "abcde"
    >>> n = reverse(data1, 2)
    >>> next(n)
    'c'
    >>> next(n)
    'b'
    >>> for char in reverse(data1, 2) :
    ...     print(char)
    ...
    c
    b
    a
    			

静岡理工科大学 菅沼ホーム 目次 索引