簡體   English   中英

C語言中的高階函數

[英]Higher order functions in C

是否有“適當”的方式在C中實現高階函數。

我對這里的可移植性和語法正確性以及是否有多種優點和缺點感到好奇。

編輯:我想知道如何創建高階函數的原因是,我編寫了一個將PyObject列表(調用python腳本時會得到)轉換為包含相同數據但以某種方式組織的C結構列表的系統取決於python.h庫。 所以我的計划是要有一個遍歷pythonic列表的函數,並在列表中的每個項目上調用一個函數,並將結果放入列表中,然后返回該列表。

所以這基本上是我的計划:

typedef gpointer (converter_func_type)(PyObject *)

gpointer converter_function(PyObject *obj)
{
    // do som stuff and return a struct cast into a gpointer (which is a void *)
}

GList *pylist_to_clist(PyObject *obj, converter_func_type f)
{
   GList *some_glist;
   for each item in obj
   {
       some_glist = g_list_append(some_glist, f(item));
   }
   return some_glist;
}

void some_function_that_executes_a_python_script(void)
{
   PyObject *result = python stuff that returns a list;
   GList *clist = pylist_to_clist(result, converter_function);
}

並澄清一個問題:我想知道如何在更安全,更正確的C語言中執行此操作。我真的很想保留高階函數的樣式,但是如果對此不滿意,我將非常欣賞通過其他方式執行此操作的方式。

從技術上講,高階函數只是采用或返回函數的函數。 所以像qsort這樣的東西已經是高階的了。

如果您的意思更像是函數語言中的lambda函數(這是高階函數真正有用的地方),則它們要難得多,並且不能在當前的標准C語言中自然完成。它們不屬於語言。 蘋果的擴展程序塊是最好的選擇。 它僅適用於GCC(和LLVM的C編譯器),但它們確實很有用。 希望這樣的事情會流行。 以下是一些相關資源:

在C中實現高階函數的最大問題是,要做任何不平凡的事情都需要閉包,閉包是函數指針,並增加了包含它們可以訪問的局部變量的數據結構。 由於閉包背后的整個想法是捕獲局部變量並將其與函數指針一起傳遞,因此沒有編譯器支持就很難做到。 而且即使有了編譯器支持,也很難不進行垃圾回收,因為變量可能存在於它們的范圍之外,因此很難弄清楚何時釋放它們。

在C語言中,這實際上只能通過函數指針來完成,這既是痛苦的,又不是針對此類事情的(這部分是為什么它們很痛苦)的原因。 不過,塊(或閉包,根據非蘋果的說法)非常適合。 它們可以在gcc-4.x或類似的文件中編譯,並可以對icc進行編譯,但是無論您要尋找的是什么。 不幸的是,我似乎無法在網上找到任何優秀的教程,但是可以說它的工作原理如下:

void iterate(char *str, int count, (^block)(str *)){
  for(int i = 0; i < count; i++){
    block(list[i]);
  }
}

main() {
  char str[20];
  iterate(str, 20, ^(char c){
    printf("%c ", c);
  });

  int accum = 0;
  iterate(someList, 20, ^(char c){
    accum += c;
    iterate(str, 20, ^(char c){
      printf("%c ", c);
    });
  });
}

顯然,這段代碼是沒有意義的,但是它打印出字符串(str)的每個字符,並在其之間留有一個空格,然后將所有字符加到累加中,每次執行時,它都會再次打印出字符列表。

希望這可以幫助。 順便說一句,塊在Mac OS X Snow Leopard api-s中非常明顯,而且我相信它們將在即將到來的C ++ 0x標准中使用,因此它們並不是那么尋常。

如果您熱衷於在純C語言中執行此操作,則需要記住要包含一個選項,以將上下文指針從函子的調用者(高階函數)傳遞到傳入的函數。這可讓您進行足夠的模擬可以使事情變得足夠容易的封閉。 該指針指向的內容...好吧,這取決於您,但這在函子的API中應該為void* (或它的許多別名之一,例如GLib世界中的gpointer或Tcl C API中的ClientData )。

[編輯]:使用/調整您的示例:

typedef gpointer (converter_func_type)(gpointer,PyObject *)

gpointer converter_function(gpointer context_ptr,PyObject *obj)
{
    int *number_of_calls_ptr = context_ptr;
    *number_of_calls_ptr++;
    // do som stuff and return a struct cast into a gpointer (which is a void *)
}

GList *pylist_to_clist(PyObject *obj, converter_func_type f, gpointer context_ptr)
{
   GList *some_glist;
   for each item in obj
   {
       some_glist = g_list_append(some_glist, f(context_ptr,item));
   }
   return some_glist;
}

void some_function_that_executes_a_python_script(void)
{
   int number_of_calls = 0;
   PyObject *result = python stuff that returns a list;
   GList *clist = pylist_to_clist(result, converter_function, &number_of_calls);
   // Now number_of_calls has how often converter_function was called...
}

這是一個微不足道的示例,但是應該向您顯示方法。

實際上,任何有趣的高階函數應用程序都需要閉包,在C語言中,這需要手動定義和填充struct函數自變量的費力且容易出錯的例程。

這是對以下問題的答案:如何在C中編寫函數,此處將其重定向。

您可以創建一個數據結構來實現列表數據類型。 該結構可以包含函數指針。

#include<stdlib.h>
#include<malloc.h>

typedef (*fun)();

typedef struct funList { fun car; struct funList *cdr;} *funList;

const funList nil = NULL;

int null(funList fs){ return nil==fs; }

fun car(funList fs)
{
   if(!null(fs)) return fs->car; 
   else 
   {
     fprintf(stderr,"error:can't car(nil) line:%d\n",__LINE__);
     exit(1);
   }
}

funList cdr(funList ls)
{ if(!null(ls)) return ls->cdr; 
  else 
  {
    fprintf(stderr,"error:can't cdr(nil) line:%d\n",__LINE__);
    exit(1);
  }
}

funList cons(fun f, funList fs)
{  funList ls;

   ls=(funList) malloc(sizeof(struct funList));
   if(NULL==ls)
   {
     fprintf(stderr,"error:can't alloc mem for cons(...) line:%d\n",__LINE__);
     exit(1);
   }

   ls->car=f;
   ls->cdr=fs;

   return ls;
}

我們可以編寫一個應用函數列表的函數comp:

type_2 comp(funList fs, type_1 x)
{  
   return (null(fs)) ? x : car(fs)(comp(cdr(fs),x)); 
}

有關其工作方式的示例。 我們將(fgh)用作cons(f,cons(g,cons(h,nil)))的縮寫,將其應用於給定參數x:

comp((f g h),x)

=

f(comp((g h),x))

=

f(g(comp((h),x)))

=

f(g(h(comp(nil,x))))

=

f(g(h(x)))

如果您已使用SML或Haskell等類型語言使用多態列表類型,則comp的類型應為:

comp :: ([a -> a],a) -> a

因為在這種情況下,列表中的所有成員都具有相同的類型。 從這個意義上講,C可以更靈活。 也許像

typedef void (*fun)();

要么

typedef (*fun)();

您應該看到C手冊對此有何評論。 並確保所有連續功能都具有兼容的類型。

組成的函數應該是純函數,即沒有副作用也沒有自由變量。

在直接C語言中很難做到。在C ++中更可能(請參見函子教程或Boost的綁定函數庫)。 最后, C ++ 0x添加了對lambda函數的本機支持 ,該函數可幫助您封閉地捕獲函數所依賴的所有變量。

如果要創建高階函數,請不要使用C。有C解決方案可以解決您的問題。 它們可能並不優雅,或者您可能會更優雅。

[編輯]我建議實現此目標的唯一方法是使用腳本語言。 其他人叫我出去。 因此,我將其替換為:[/ Edit]

您想達到什么目的? 如果要模擬閉包,請使用支持閉包的語言(可以通過庫綁定到Ruby,lua,javascript等)。 如果要使用回調,則函數指針可以。 函數指針結合了C的最危險區域(指針和弱類型系統),因此請當心。 函數指針聲明也不是很有趣。

您會發現一些使用函數指針的C庫,因為它們必須這樣做。 如果您正在編寫庫,也許您也需要使用它們。 如果只是在自己的代碼中使用它們,則可能不是在C語言中思考。您正在以Lisp,Scheme或ruby或...進行思考,並嘗試以C語言編寫。學習C語言。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM