関数へのポインタ

出典: フリー百科事典『ウィキペディア(Wikipedia)』
Jump to navigation Jump to search

関数へのポインタは、多くのプログラミング言語におけるポインタの一種である。関数へのポインタをデリファレンスすれば、そのポインタが指し示す関数サブルーチン)を呼び出せる。関数へのポインタを使えば高階関数を作ることができる。

関数オブジェクトは、関数へのポインタに似ているが、コード領域中のエントリポイントを指す単なるポインタである関数へのポインタと違い、データ領域上に実体を持つオブジェクトであるという点が異なっている(実装の詳細は言語や処理系により異なるが)。そのため、関数オブジェクトはデータを保持でき、クロージャを再現することもできる。ゆえに、関数オブジェクトは、「関数へのポインタ」ではなく「関数」という型と値を持つようなものと言え、より強力である。

Javaなどでは、関数へのポインタを使用できないが、そのような言語では、メソッドを1つだけ持つインタフェースで同様のことを行える。また、C#Visual Basic .NETなどといった.NET Framework用の言語には、デリゲートがある。

第一級オブジェクトとして関数を使用できる(第一級関数がある)言語では、関数も引数で渡したり、戻り値で返したり、他の関数から動的に作成したりできるなどデータ同様に扱えるため、関数へのポインタは必要とされない。

C言語での例[編集]

日付書式を切り替える[編集]

この例では、関数へのポインタを保管する変数 dateToString が宣言され、そこへ関数 YYYY_MM_DDYY_MM_DDM_DGGG_M_D へのポインターを必要に応じて代入している。そして dateToString を通じて関数を呼び出している。

#include <stdio.h>

/* 「2018/01/06」のような書式 */
char *YYYY_MM_DD (int year, int month, int day, char *buffer)
  {sprintf (buffer, "%d/%02d/%02d", year, month, day) ; return buffer ;}

/* 「18/01/06」のような書式 */
char *YY_MM_DD (int year, int month, int day, char *buffer)
  {sprintf (buffer, "%02d/%02d/%02d", year%100, month, day) ; return buffer ;}

/* 「1/6」のような書式 */
char *M_D (int year, int month, int day, char *buffer)
  {sprintf (buffer, "%d/%d", month, day) ; return buffer ;}

/* 「平成30年1月6日」のような書式 */
char *GGG_M_D (int year, int month, int day, char *buffer)
  {
  /* 明治5年12月2日以前(1972年12月31日以前)はグレゴリオ暦 */
  long int ymd = year*10000L + month*100L + day ;
  char *g =
    (18730101L<=ymd && ymd<=19120729L) ? "明治" :
    (19120730L<=ymd && ymd<=19261224L) ? "大正" :
    (19261225L<=ymd && ymd<=19890107L) ? "昭和" :
    (19890108L<=ymd && ymd<=20190430L) ? "平成" :
    "グレゴリオ暦" ;
  int gyear =
    (18730101L<=ymd && ymd<=19120729L) ? year-1872+5 :
    (19120730L<=ymd && ymd<=19261224L) ? year-1911 :
    (19261225L<=ymd && ymd<=19890107L) ? year-1925 :
    (19890108L<=ymd && ymd<=20190430L) ? year-1988 :
    year ;
  if (gyear==1)
    {sprintf (buffer, "%s元年%d月%d日", g, month, day) ;}
  else
    {sprintf (buffer, "%s%d年%d月%d日", g, gyear, month, day) ;}
  return buffer ;
  }

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
  {
  char buffer [0x100] ;
  char *(*dateToString) (int,int,int,char*) ;
  
  dateToString = &YY_MM_DD ;
  printf ("日付: %s.\n", (*dateToString) (2018, 1, 6, buffer)) ;
  
  dateToString = &GGG_M_D ;
  printf ("日付: %s.\n", (*dateToString) (2018, 1, 6, buffer)) ;
  
  return EXIT_SUCCESS ;
  }

高階関数[編集]

次は高階関数の例である。整数の配列から特定の条件を満たす値だけを残す篩い分け関数である。篩い分け関数が filter で、処理対象の配列と一緒に篩分けの条件を関数としてfilter 関数へ渡す。filter 関数は残すべきデータであるかどうかを計算する条件関数を引数 predicate で受け取り処理に使う。 

#include <stdlib.h>
#include <stdbool.h>

size_t filter (int src[], size_t src_length, bool(*predicate)(int), int dst[])
  {
  size_t dst_length = 0 ;
  for (size_t src_i=0 ; src_i<src_length ; ++src_i)
    {
    if ((*predicate) (src [src_i])) /* 値が命題 predicate を満たす場合 */
      {
      dst [dst_length] = src [src_i] ;
      ++dst_length ;
      }
    }
  return dst_length ;
  }

bool iseven (int value) {return value % 2 == 0 ;}
bool isodd  (int value) {return value % 2 != 0 ;}

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
  {
  int src [10] = {928, 994, 981, 232, 445, 428, 138, 52, 230, 551} ;
  int dst [10] ;
  size_t dst_length = filter (src, 10, &isodd, dst) ; /* 奇数を残す場合 */
  
  printf ("dst_length: %d\n", dst_length) ;
  for (size_t dst_i=0 ; dst_i<dst_length ; ++dst_i)
    {printf ("dst[%d]: %d\n", dst_i, dst [dst_i]) ;}
  
  return EXIT_SUCCESS ;
  }

高階関数(qsort[編集]

次の例はC言語の標準ライブラリー関数 qsort を使うものである。qsort は任意のデータ型の配列の並べ替えを行う関数であり、二つのデータの大小関係を計算する関数を引数のひとつとして与えなければならない。ここでは大小関係を計算する関数を Event_compareByDate として定義し、この関数へのポインタを qsort に与えている。

struct Event
  {
  int year ;
  int month ;
  int day ;
  char *content ;
  } ;

int Event_compareByDate (struct Event *l, struct Event *r)
  {
  long int lymd = l->year*10000L + l->month*100L + l->day ;
  long int rymd = r->year*10000L + r->month*100L + r->day ;
  long int compare = lymd - rymd ;
  return compare<0L ? -1 : compare>0L ? 1 : 0 ;
  }

#include <string.h>

int Event_compareByContent (struct Event *l, struct Event *r)
  {return strcmp (l->content, r->content) ;}

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
  {
  struct Event birthdays [10] =
    {
    {1949, 1,  12, "村上春樹"                },
    {1892, 3,  1,  "芥川龍之介"              },
    {1900, 11, 18, "マーガレット・ミッチェル"},
    {1903, 10, 13, "小林多喜二"              },
    {1872, 3,  25, "島崎藤村"                },
    {1929, 6,  12, "アンネ・フランク"        },
    {1821, 10, 30, "ドストエフスキー"        },
    {1919, 1,  1,  "サリンジャー"            },
    {1883, 7,  3,  "カフカ"                  },
    {1925, 1,  14, "三島由紀夫"              },
    } ;
  
  qsort (birthdays, 10, sizeof (struct Event), &Event_compareByDate) ;
  
  for (size_t i=0 ; i<10 ; ++i)
    {printf ("%d/%2d/%2d: %s\n", birthdays[i].year, birthdays[i].month, birthdays[i].day, birthdays[i].content) ;}
  
  return EXIT_SUCCESS ;
  }

脚注[編集]


関連項目[編集]

外部リンク[編集]