什麼是函數式語言?

能通俗點講不。Wikipedia看不懂。

謝謝。


繼續貼這張圖,越靠下表示背離傳統命令式語言更遠


本人是弱弱,以前寫的答案多有不妥,請批判性閱讀。

嚴格的函數式語言是沒有變數的賦值行為,講究的是引用透明性,也就是說一個表達式返回一個值,那麼它永遠返回一個值,不會變。

函數式語言常常和遞歸聯繫起來,這是因為一般的循環結構,除非 while(1) 這種死循環,都是和表達式的變動關聯起來的,比如說 while(n) 就是要不斷修改 n 的值直到 n == 0,在函數式語言中是必須避免的。

遞歸通過調用函數的參數不同,來達到數據的變動卻不破壞引用透明性。並且如果加入尾遞歸優化,那麼遞歸的性能和循環是等價的。

函數式語言另外一個特點,也是一個語言能自稱函數式的關鍵所在是,函數是「一等公民」,這是說能像操作數據一樣在函數中動態生成新的函數,可以將函數賦值給變數,可以把函數放到數據結構里,可以把函數作為參數和返回值。

達到這一個特性的語言都可以寬泛的稱作函數式編程語言,並不和過程式和面向對象衝突。


看了一下前面的答案,舉例子的似乎都是沒有看過SICP的傢伙,至少前三章沒看(然後直接看後面三章)

於是看看能不能順手舉個好玩程度能夠輾軋前面的例子

也就是把函數當數據看待之後玩各種各樣奇怪的遊戲

這是數據結構

(define cons (lambda( x y) (
lambda ( f) (f x y))))
(define car (lambda (f ) ( f (lambda( x y ) x))))
(define cdr (lambda (f ) ( f (lambda( x y ) y))))

於是

(car (cons 1 2))
$2 = 1
(cdr (cons 1 2))
$3 = 2

這是循環

( (lambda ( x y ) ( if ( = y 0 ) 1 (* y ( x x (- y 1)) )))
(lambda ( x y ) ( if ( = y 0 ) 1 (* y ( x x (- y 1)) )))
5)
$1 = 120

如果上面的東西看不明白的話,我寫一下C++的版本, 太久沒有碰過C++了,語法都忘記了,看看就好

有些地方不記得怎麼加關鍵字了

template&
struct Cons{
struct Apply&{
typedef Apply Ftype::Apply&::Apply
}
}

template&
struct Car{
template &< typename P, typename Q&>
struct Ftype{
typedef Apply P
}
typedef Apply X::Apply&::Apply
}

於是你可以看看 Car&< Cons&< int, float &> &>::Apply是不是int

函數式編程語言是如此的好玩,於是大部分老師會把寫一個符號微分器作為初學者們的家庭作業


僅僅作為 @朱晉玄 中C++代碼的修正。

template&
struct cons {
template& class Func&>
struct apply {
typedef typename Func&::type type;
};
};

template&
struct car {
template&
struct F {
typedef P type;
};
typedef typename C::template apply&::type type;
};


函數是語言都傾向於讓你用函數來組成函數,而不是把函數看成是一個數據弄成另一個數據的過程。


還記得中學時代數學課里的方程與函數嗎?能讓你把程序寫得跟那個公式推導一個樣子的,就是函數式了編程語言了。


在第一堂關於編程的課上,老師就會說,將數據傳入函數,然後產生結果:

data =&> function =&> result,類似於這樣的程序:

function add(a, b)

return a+b;

end

可以表示成為: function (data) =&> data,指函數接受數據,處理,最後輸出數據作為計算結果。

這種佔據大部分人腦袋的觀點直接導致了函數和數據的分割,認為兩者是完全不同的東西,然後才有 「函數式語言」 的這種說法。因為在函數式的語言裡面,程序和數據用樣都是函數。

在任何程序語言還沒有發明之前,在計算機科學家的草稿紙上,都是直接用一種叫做 lambda 函數 的東西來計算的。這些奇怪的函數重新(徹底地)定義了數和數據!就是說最簡單的數據,比如正整數,1,2,3,4.. 這些東西都可以表示成為 lambda 函數。

所有的程序都是類似這樣的東西:

function (function) =&> function,指函數接受另一個函數作為參數,運算,最後再輸出一個函數作為計算結果。那個世界裡根本就沒有數或者對象這種概念,全是函數。可是,這怎麼可能?

這的確是一個顛覆性的概念。他們用計算的過程(動作)代替了實體(概念)。比如 3 這個概念,從實體來看,它可能是 3 個蘋果,或者內存中的幾個0和1,從過程的角度來看,它可能是調用了某個函數 3 次,至於結果是什麼,一點都不重要,重要的是調用了 3 次,調用這個動作做了 3 次。

你看,3 其實是一個很複雜的東西。函數式語言,從一開始就採取了與我們看待這個世界完全不同的世界觀。這也是使用對象語言久了之後,難以接受函數語言的原因。萬物皆是對象,很好理解,而萬物皆是函數,確實難以理解。

比如在 scheme (一種函數式語言),你可以這樣定義整數:

;; 定義數字: 1
(define (one a)
(lambda (b)
(a b)))

用 javascript (你也可以把它當作函數式語言)就是這樣:

function one (a) {
return function (b) {
return a(b);
}
}

數字1 是一個函數,它接受另一個函數 a 作為參數,然後返回一個函數,這個返回的函數帶一個參數 b,函數體裡面會調用 a(b),並返回結果。

這就是函數式語言裡面最原始的數字1。如果你在此的基礎上能定義出一個後續函數 succ,那麼,succ (1) 自然就是2了,這個後續函數還真的存在:

;; 後續函數
(define (succ a)
(lambda (b)
(lambda (c)
(b ((a b) c)))))

用 javascript 就是這樣:

function succ (a) {
return function (b) {
return function(c) {
return b( a(b)(c) );
}
}
}

那麼 2 的定義使用後續函數就會是:

(define (two a)
(lambda (b)
(((succ one) a) b)))

用 javascript 就是這樣:

function two(a) {
return function (b) {
return succ(one)(a)(b);
}
}

當你用函數重新把這些基礎的東西,比如『數』,以及更高級的數據結構定義一遍後,會發現,各種用非函數式語言下的演算法、程序都可以用這一套實現,可以有些地方還能做得更好。

不過看起來是不是好噁心?簡單的幾個數字怎麼複雜?因為這些最開始是數學家們的玩意,他們用這一套函數去證明一些結論。編程語言說到底是機械的、基於固定規則的、毫無內涵的符號替換。對象型語言來自最原始的圖靈機模型(讀、寫、跳轉這樣操作),而函數式語言來自 Lambda 函數(符號替換、規約等操作),本質上它們是完全等價的。但是對編程而言,思維上有較大的差異。最早的語言比如 C 和 lisp 分別是這兩類的代表,毫無共性可言,而更現代的語言,比如 python, javascript 它們吸收了這兩派的優點,使得編程風格更混合了。

當對象之間有清晰的界限的時候,對象型語言更擅長處理,而邊界模糊的時候,函數型更擅長。就像經典力學擅長處理邊界清晰宏觀世界,而量子力學擅長處理邊界模糊的微觀世界一樣。


任何一段程序都是一個函數……注意是「任何」一段


FP 語言的關鍵在於可以通過組合變換得到新的函數

==== 下面是 too young too simple 的分界線 ====

隨便拿一個 C 或者什麼你熟悉的程序,把中間所有修改狀態的語句全部去掉,改成函數之間的嵌套調用,就差不多了。

例:

# 這裡 i 和 sum 都是存在中間狀態的。

a = [1,2,3]
sum = 0
for i in range(len(a)):
sum += a[i]

# 改成沒有中間狀態的函數組合
def add (x, y):
return x + y
reduce (add, [1, 2, 3], 0)

# 或者更極端的例子。
for i in range(len(a)):
print a[i]

# 取消 i 的狀態

def loop(a, fn):
if len(a) &> 0:
fn (a[0])
loop (a[1:], fn)

loop([1, 2, 3], print)


簡單來講,假設有兩個函數f和g,在C裡面調用f(g(input)),會先用input做參數運行g,然後用g的輸出運行f,最後你得到f函數的結果。

在函數式編程里,函數可以拼湊,你會得到一個新的函數h=f(g(input))。以後在對h析值的時候,才會依次調用g函數和f函數。


就是可以把函數像對象一樣,當參數一樣傳來傳去,可以直接在表達式裡面定義函數


Haskell趣學指南 簡體版Haskell趣學指南 (Learn You a Haskell for Great Good! 繁體中文版)


JS函數式編程指南 - 知行 - 分享並實踐你的知識


感覺基本上一門語言支持高階函數, 但凡函數式語言的特性基本上就都能有了.

然而並不是說支持函數式語言的特性, 這門語言就是函數式了.

只能說你可以用這門語言寫出函數式風格的代碼,如果你想的話.

但是譬如用scheme, 就會很自然的使用高階函數/遞歸, 而如果,譬如說,用Perl寫程序, 並不是說我不能遞歸, 並不是說我非得利用副作用, 並不是說我不能用高階函數, 也並不是說我用遞歸/純函數/高階函數會寫起來更困難, 但是似乎第一選擇還是命令式的風格.


面向對象返回對象,面向函數返回函數。複合函數中特明顯。


推薦閱讀:

有哪些值得深入學習RxAndroid的開源項目?
EDSL相關雜記(1)
怎樣理解「組合優於繼承」以及「OO的反模塊化」,在這些方面FP具體來說有什麼優勢?
haskell中的類型類是相當於面向對象語言的介面嗎?
Lambda calculus引論(目錄)

TAG:編程語言 | 編程 | 函數式編程 |