C中有沒有將一個函數轉變為另一個函數的函數(例如求導運算)?

rt


C 因為沒有閉包所以這種高階函數會非常不好寫,C++ 的話還是很簡單的

template &
operation derivative_any(operation f, T dx) {
return [=](T x)-&>T {
return (f(x + dx) - f(x)) / dx;
};
}


題主本來的問題

c中有沒有將一個函數轉變為另一個函數的函數

答案是:有。

這一點在其他人的回答中,並沒有爭議。

C函數可以函數指針為輸入,以函數指針為輸出。

各位還沒形成共識的是題主所求的例子

(例如求導運算)

是否存在一個C語言的實現。

如有的答主已經指出,要回答這個問題,首先需要明確關於導數的定義。

假設用戶擁有一個可導函數。@Belleve 同學給出的實現並不是求導運算,而是計算有限差分,即泰勒展開的一階項的近似值 frac{dfleft( x
ight) }{dx}=frac{fleft( x+Delta x
ight) -fleft( x
ight) }{Delta x}+Oleft( Delta x
ight)

其中Oleft( Delta x
ight) 被忽略掉了。

在許多不需要精確導數的應用中,差分法是適用的。

如果需要精確的導數,標準的方法是演算法微分(Automatic differentiation) 。與只計算泰勒展開一階項的有限差分不同,演算法微分利用鏈式法則,直接對計算程序求導。其實現方式大致分兩種

  • 編譯前的代碼生成

    在許多應用中,用戶用來實現可導函數的庫函數都是可導的。例如 math.h 中的例如 sin/cosh 等函數的導數都是已知的。利用這一性質,用戶的可導函數可以被看作一個複合函數 (composite function),並用鏈式法則 (chain rule) 自動生成導數的計算代碼。

    為C/C++提供的開源庫列表:www.Autodiff.org
  • 運行時的函數重載

    上一種方法需要對代碼進行分析,實現難度堪比編譯器。另一種方法是通過函數重載。原理和上一種方法一樣,只是導數的計算方法是動態生成的,而非通過編譯前的靜態分析。也正因如此,這種實現雖然簡單,但運行效率要低一些。

    為C/C++提供的開源庫列表:www.Autodiff.org

對於多變數的函數,演算法微分比起差分法可以快 n 倍,其中 n 是變數的個數。

試想用有限差分計算有三個變數的的函數

frac{dfleft( x,y,z
ight) }{dx}=frac{fleft( x+Delta x,y,z
ight) -fleft( x,y,z
ight) }{Delta x}+Oleft( Delta x
ight)

一次計算只能計算針對一個變數的近似導數值,要得到完整的導數需要計算三次。

而使用鏈式法則計算導數的演算法微分只需一次計算。

演算法微分的應用廣泛,尤其是在解優化問題的時候。機器學習演算法神經網路的訓練步驟里,導數的計算就是使用的演算法微分。許多主流的神經網路庫都提供題主所說的求導函數,這樣用戶就不必親自實現計算導數的程序。例如神經網路的開源庫(https://pypi.python.org/pypi/autograd/1.0.4)


任何一個系統只要可以模擬出「對象」這個概念,就能模擬出「First Class」函數(因為函數就是一個對象)。當然語法上會比FP系統啰嗦一點


你可以寫一個返回函數指針的函數


「函數」有幾種理解——

1. c中存在的、在邏輯上表示「函數」的物件,包括帶c函數指針的結構體、可被識別的AST或IR結構體、表示代碼的字元串……內部及相互之間的轉換:

可以且很常見。按題主所問,有不少用c實現的CAS系統,可以對以AST函數進行求導。

2. 上述物件到c函數的轉換:

可以,稱為JIT編譯。例如Lua的JIT引擎。

3. c函數到上述物件的轉換:

依然可以,但有一些限制。例如c函數內部的結構無法直接獲知,因此只能「包裝」而難以實現代碼級別的處理。基本無法真正意義上進行「求導」。

4. 涉及未編譯的c代碼的轉換:

參見1,需要使用c代碼分析器。

5. c函數到c函數的轉換:

參見3以及2,可做到的有意義的事主要是包裝和即時優化,除此以外意義不大。


C語言中的函數和數學中的函數完全是不同的概念;

數學中的函數尚不能都求導,

C語言中的函數則連求導的定義都沒有。

所以問題根本就不成立,

至少題主對問題的描述是錯誤的或不正確的,

題主對什麼是編程應該還沒入門吧?


這。。。嗯,你還可以把ml的kernel變換為Linux的kernel。:)


struct func_tag;

typedef int (*bar_f)(struct func_tag*, int x);

typedef struct func_tag {
const char *type,
bar_f handle,
int data[0], //...
} func_t;

typedef struct func_pow_tag {
func_t base,
int C,
} func_pow_t;

typedef struct func_kx_tag {
func_t base,
int K,
} func_kx_t;

int my_pow(func_t *func, int x)
{
int C = func-&>C;
return pow(x, C);
}

bar_f make_func_pow(int C)
{
func_t *ret = make_func();
ret-&>type = "pow";
ret-&>C = C;
ret-&>handle = my_pow;
return ret;
}

int my_kx(func_t *func, int x)
{
int K = func-&>K;
return (K * x);
}

func_t make_deriv_func(func_t *func)
{
//...
if (0 == strcmp(func-&>type, "pow")) {
func_t *ret = make_func_kx();
ret-&>type = "kx";
ret-&>K = (func_pow_t*)func-&>C;
ret-&>handle = my_kx;
return ret;
}
//...
}

int main()
{
int x = 5;
int C = 2;
func_t *func_pow2 = make_func_pow(C);
func_t *func_2x = make_deriv_func(func_pow2);
int y = *(func_2x-&>handle)(func_2x, x);
//...
}

試著寫了一下,貌似很噁心啊,湊合著看吧。。。

沒有閉包,這玩意兒就得用函數對象來模擬,而且不考慮用C艹的話,寫起來相當蛋疼。。


去學函數式語言吧


題主的意思比較複雜,指的是能否通過傳遞的函數指針判斷這個函數做了些什麼,然後再對它所做的事情進行操作和運算,得到一個新的函數。

能力不夠,抱歉。

---

個人覺得函數的模式是一個非常單一的點對點的模式,猶如西方科學工程思維中的拆到點再從點整合的那種模式。然後你要組建的這個函數收到的信息也只是一堆散點,不是連貫出現的片段。最直白的想法是內部試探函數做了些什麼,不過這似乎沒有意義。


可以

1。寫一個求導函數,輸入自然是字元串,再寫一個執行函數的函數,便可

2。若想求導已有函數則可通過,導數定義求解,畢竟你要調用fx,x在調用時已經有確定值,由此定義便可求出導數

此兩種方法,無論是求導函數表達式,還是求導現有函數都可做到

3。若是對現有函數求其導數表達式,好像比較難,只有分析曲線類型,求出其原有表達式,再進行第一種方案才行


推薦閱讀:

函數實現的時候,將函數返回值另起一行書寫,這是一種好的代碼書寫風格么?
C語言中volatile修飾符的作用?
學習 C/C++ ,有什麼書籍推薦?
C語言中定義int a[10][10],a是什麼類型?
C 字元串常量的空間是不需要回收的?

TAG:數學 | 編程 | C編程語言 | 計算機科學 | 數值計算 |