1. 程式人生 > >聲明式編程範式初探

聲明式編程範式初探

不可 EDA fec 給定 流式 n-1 logic ESS ttr

聲明式編程範式初探

語言編程語言可以分成兩類:

  • 命令式
  • 聲明式

事實上,凡是非命令式的編程都可歸為聲明式編程。因此,命令式、函數式和邏輯式是最核心的三種範式。為清楚起見,我們用一幅圖來表示它們之間的關系。

技術分享圖片

與命令式編程相對的聲明式編程(declarative programming)。顧名思義,聲明式編程由若幹規範(specification)的聲明組成的,即一系列陳述句:‘已知這,求解那’,強調‘做什麽’而非‘怎麽做’。聲明式編程是人腦思維方式的抽象,即利用數理邏輯或既定規範對已知條件進行推理或運算

聲明式編程的發源

聲明式編程發軔於人工智能的研究,主要包括函數式編程(functional programming,簡稱FP)和邏輯式編程(logic programming,簡稱LP)。其中,函數式編程將計算描述為數學函數的求值,而邏輯式編程通過提供一系列事實和規則來推導或論證結論。

其實支持它們的語言出現得並不比命令式的晚多少——最早的函數式語言Lisp(LISt Processor)已有半個世紀的歷史,最早之一的邏輯式語言Prolog(PROgramming in LOGic)也與C同齡。只是由於大多數更多地用於學術研究而非商業應用,頗有些‘養在深閨人未識’的味道。

起源的不同決定了這兩大類範式代表著迥然不同的編程理念和風格:命令式編程是行動導向(Action-Oriented)的,因而算法是顯性而目標是隱性的;聲明式編程是目標驅動(Goal-Driven)的,因而目標是顯性而算法是隱性的。為便於說明,我們分別用三種代表性的語言來實現階乘(factorial)運算。

階乘的三種編程實現

C(命令式)——

4

for(; n > 0; --n) f *= n;

Lisp(函數式)——

4

(* n (factorial(- n 1)))))

Prolog(邏輯式)——

4

factorial(N,F) :- M is N-1, factorial(M,Fm), F is N * Fm.

以上三段代碼區別在哪裏?C明確給出了階乘的叠代算法,而Lisp僅描述了階乘的遞歸定義,Prolog則陳述了兩個關於階乘的斷言。

聲明式編程的本質

我們最早接觸的變量是代數方程中的x、y、z等,本質上是抽象化的符號,變量值是該符號在給定約束條件下的允許值。而命令式編程中的變量本質上是抽象化的內存,變量值是該內存的儲存內容。通俗地說,前者好比姓名,所指之人是固定的;後者好比住址,所住之人是變化的。此外,等號在代數中是一種約束,而在許多命令式語言中則表示賦值。因此 i = i + 1 可以在命令式編程中出現,但絕不可能在數學推理中出現 —— 除非在反證法中。

聲明式編程讓我們重回數學思維:函數式編程類似代數中的表達式變換和計算,邏輯式編程則類似數理邏輯推理。其中的變量也如數學中的一樣,是抽象符號而非內存地址,因此沒有賦值運算,不會產生變量被改寫的副作用(side-effect),也不存在內存分配和釋放的問題。這既簡化了代碼,也減少了調試——不妨想一想,有多少bug是由於某個變量被意外改寫或內存管理不慎而造成的?

聲明式語言與命令式語言的相通之處

  • 首先,所有高級語言都建立於低級語言之上,最終轉化為機器語言,聲明式語言也不例外。
  • 其次,聲明式語言與命令式語言並非涇渭分明,而是互相交叉滲透的。一些‘非純粹’ 的聲明式語言也提供變量賦值和流程控制,而一些命令式語言也在逐漸發展,通過利用其他程序或增加新的語言特征來實現聲明式編程。

總的說來,在命令式語言中融入聲明式的元素應當是一種趨勢。尤其是函數式,它的一些特征已經在許多命令式語言中得到了支持。比較而言,聲明式編程重目標、輕過程,專註問題的分析和表達而不致陷入算法的迷宮,其代碼也更加簡潔清晰、易於修改和維護。從這種意義上說,聲明式語言天然地就比命令式語言更高級。

值得指出的是,聲明式編程並不僅僅局限於函數式和邏輯式。比方說,C#中的attribute、Java中的annotation和XDoclet庫等采用的也是具有聲明式特征的屬性導向式編程(Attribute-Oriented Programming,簡稱@OP)。再比如,Prograph、SISAL等數據流語言(dataflow language)采用的數據流式編程(Dataflow Programming)與函數式編程有不少共同點,同樣屬於聲明式的範疇。還有一些語言如Oz、CHIP等支持與邏輯式編程相交的約束式編程(Constraint Programming)。此外,大家熟悉的數據庫語言SQL,樣式語言XSLT、CSS,標記語言HTML、XML、SVG,規範語言IDL(Interface Description Language)等等都是聲明式的。算上它們,聲明式語言所占的比例也是非常可觀的。此前之所以沒有提及,一方面,不少聲明式語言采用的範式並沒有專門的名稱;另一方面,這些語言大多是領域特定語言,並且不少並非圖靈完備的,有的連運算都沒有。畢竟,目前我們的重點還是放在通用編程語言上。

其實用Lisp實現階乘的方法也可以用在C上:

3

returnn == 0 ? 1 : n * factorial(n - 1);

這是C的遞歸實現。除了細微的語法差別外,二者的確很相似,這說明用命令式語言也可以講出聲明式的味道。實際上,命令式語言提倡叠代而不鼓勵遞歸,早期的Fortran 甚至都不支持遞歸。一則叠代比遞歸更符合命令式的思維模式,因為前者貼近機器語言而後者貼近數學語言;二則除尾遞歸(tail recursion)外,一般遞歸比叠代的開銷(overhead)大。相反,聲明式語言提倡遞歸而不支持叠代。就語法而言,它不允許叠代中的循環變量;就視角而言,叠代著眼微觀過程而遞歸著眼宏觀規律。

具體可以看看這個:漫談遞歸

歸根結底,編程是尋求一種機制,將指定的輸入轉化為指定的輸出。三種範式對此提供了截然不同的解決方案:

  • 命令式把程序看作一個自動機,輸入是初始狀態,輸出是最終狀態,編程就是設計一系列指令,通過自動機執行以完成狀態轉變;
  • 函數式把程序看作一個數學函數,輸入是自變量,輸出是因變量,編程就是設計一系列函數,通過表達式變換以完成計算;
  • 邏輯式把程序看作一個邏輯證明,輸入是題設,輸出是結論,編程就是設計一系列命題,通過邏輯推理以完成證明。

繪成表格如下:

命令式

自動機

初始狀態

最終狀態

設計指令

命令執行

函數式

數學函數

自變量

因變量

設計函數

表達式變換

邏輯式

邏輯證明

題設

結論

設計命題

邏輯推理

短短一篇文章無法深入講述,後面將展開,讓大家看清楚聲明式編程範式的全貌。

http://www.nowamagic.net/academy/detail/1220528

聲明式編程範式初探