中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

編程語(yǔ)言選型時(shí),你需要考慮的幾個(gè)方面

2018-08-19    來(lái)源:raincent

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用


前言


畢業(yè)之后一直混跡在創(chuàng)業(yè)公司,經(jīng)歷過(guò)很多次產(chǎn)品從0到1的過(guò)程。產(chǎn)品在開(kāi)發(fā)之前都會(huì)面臨一個(gè)常見(jiàn)問(wèn)題,即技術(shù)選型,首當(dāng)其沖的是采用哪門編程語(yǔ)言來(lái)開(kāi)發(fā)我們的產(chǎn)品。我的第一家公司是UCloud,三位老板都來(lái)自于騰訊,所以第一批員工不少都是騰訊兵,騰訊的主要開(kāi)發(fā)語(yǔ)言是C++,有很多現(xiàn)成的輪子可以用,自然UCloud的產(chǎn)品都是用C++開(kāi)發(fā)的,前端則采用的是當(dāng)時(shí)流行的PHP

2014年Node大火,nodejs的非阻塞IO在非計(jì)算密集的高并發(fā)業(yè)務(wù)場(chǎng)景效果出眾,開(kāi)發(fā)起來(lái)也會(huì)比較快;nodejs讓前端程序員也能參與到后端開(kāi)發(fā)中來(lái),在人力分配上面自由度會(huì)高一些;運(yùn)行時(shí)有g(shù)c, 心智負(fù)擔(dān)比C++小。UCloud也順勢(shì)采用了node, 先是前端部門用它重寫了自己的產(chǎn)品邏輯,之后后端也陸續(xù)采用,包括我自己也用node重寫了整個(gè)uhost產(chǎn)品的后端。

2016年golang熱度很高,當(dāng)時(shí)正好有個(gè)新產(chǎn)品要基于docker來(lái)開(kāi)發(fā),我們就開(kāi)始嘗試用golang來(lái)寫后端服務(wù), golang雖然類型特性不多,也不支持函數(shù)式編程,但是其極佳的工程實(shí)踐和強(qiáng)勁的并發(fā)性能還是深得大家喜愛(ài),F(xiàn)在這家公司,因?yàn)橐?guī)模尚小,考慮到招人的難度就采用了主流的Java, 用golang來(lái)做ssh client。在做編程語(yǔ)言選型的時(shí)候,除了技術(shù)本身還有很多其他因素,這些因素要針對(duì)具體場(chǎng)景來(lái)分析,所以本文從通用性的角度考慮,只講技術(shù)層面的內(nèi)容。
 

看完這篇文章能收獲什么?


  • 對(duì)于開(kāi)發(fā)人員,可以系統(tǒng)地了解各種類型特性,語(yǔ)法特性和編程范式
  • 對(duì)于架構(gòu)師或者技術(shù)leader,可以為編程語(yǔ)言的選型提供理論依據(jù)

類型系統(tǒng)


什么是類型?在軟件執(zhí)行的過(guò)程中,變量可以為很多值,定義變量的邊界的描述即類型。變量可以被賦予類型(即變量有邊界)的語(yǔ)言稱為類型語(yǔ)言(typed language),無(wú)類型語(yǔ)言(untyped language)沒(méi)有類型,或者說(shuō)只有一個(gè)全局類型,能夠存儲(chǔ)所有的值。 類型語(yǔ)言我們見(jiàn)得多了,無(wú)類型的呢?lambda演算(pure λ-calculus)是無(wú)類型的,匯編和LISP也是無(wú)類型的.

變量類型的指定可以是顯式的

 
 
// golang 
var foo int

也可以是隱式的

 
-- haskell 
fac :: Int -> Int -- 這一行可以省略 
fac 0 = 1 
fac n = n * fac (n - 1)
 
fac 0 = 1 
fac n = n * fac (n - 1)

類型系統(tǒng)會(huì)自動(dòng)賦予變量類型,這種行為稱為類型推斷(type inference)。
 

類型檢查


類型系統(tǒng)是類型語(yǔ)言的首要組成部分。類型系統(tǒng)的職責(zé)之一是跟蹤變量的類型,判斷代碼是否滿足類型約束,這種行為稱為類型檢查(typechecking), 類型檢查是保證程序穩(wěn)定運(yùn)行的手段,同時(shí)又分為運(yùn)行時(shí)檢查(runtime checks)和靜態(tài)檢查(static checks), 運(yùn)行時(shí)檢查也叫動(dòng)態(tài)檢查(dynamic checking).

類型系統(tǒng)做了靜態(tài)檢查,還有必要做動(dòng)態(tài)檢查嘛?有,比如數(shù)組的邊界檢查,就必須在運(yùn)行時(shí)做。運(yùn)行時(shí)的類型檢查會(huì)導(dǎo)致程序運(yùn)行終止(fail-stop),那為什么還要檢查呢?讓它運(yùn)行到無(wú)法繼續(xù)執(zhí)行為止不就好了?類型檢查雖然會(huì)出錯(cuò),但是阻止了更惡劣的錯(cuò)誤(untrapped errors)的發(fā)生,比如保證gc等機(jī)制能夠正常運(yùn)轉(zhuǎn),讓程序能夠更平滑地退出。動(dòng)態(tài)檢查的缺點(diǎn)是會(huì)導(dǎo)致fail-stop,也會(huì)消耗資源,影響性能,所以通常我們認(rèn)為擁有靜態(tài)檢查的類型系統(tǒng)的語(yǔ)言會(huì)更穩(wěn)定高效。但是靜態(tài)檢查就足夠安全了嗎?不一定,因?yàn)槟承┱Z(yǔ)言在靜態(tài)檢查時(shí)沒(méi)有檢查一些危險(xiǎn)操作,比如C語(yǔ)言中指針的運(yùn)算和轉(zhuǎn)換,這類語(yǔ)言稱為weekly checked, 反之, 程序在編譯期間能夠盡可能發(fā)現(xiàn)所有的類型錯(cuò)誤, 稱為strongly checked.

那么延伸一下,怎么區(qū)分一門語(yǔ)言是weekly checked, 還是strongly checked? 以下幾點(diǎn)可以作為判斷的依據(jù)。
 

Implicit type conversions


可以進(jìn)行隱式類型轉(zhuǎn)換的語(yǔ)言屬于weekly checked, 如c++

 
int a = 3; 
double b = 4.5; 
a + b; // a將會(huì)被自動(dòng)轉(zhuǎn)換為double類型,轉(zhuǎn)換的結(jié)果和b進(jìn)行加法操作

Pointers


允許指針運(yùn)算的語(yǔ)言屬于weekly checked, 比如c

 
 int num [] = {1,3,6,8,10,15,22}; 
int *pointer = num; 
printf("*pointer:%d\n",*pointer); 
pointer++; 
printf("*pointer(p++):%d\n",*pointer);

Untagged unions


union type即聯(lián)合類型,之后的內(nèi)容會(huì)介紹。聯(lián)合類型中的不同類型的值會(huì)被存儲(chǔ)在同一地址,這也是不穩(wěn)定因素之一,所以擁有untagged unions的語(yǔ)言屬于weekly checked. 和untagged相對(duì)的是tagged union type, tagged union會(huì)用tag字段顯式地標(biāo)記當(dāng)前正在使用的類型,因此要相對(duì)安全一些。
 

Weekly typed


一般弱類型語(yǔ)言屬于weekly checked

除此之外,往往是通過(guò)語(yǔ)言之間的比較來(lái)進(jìn)行判斷,并沒(méi)有明顯的界限。類型系統(tǒng)除了提供類型檢查,保證程序的安全性之外,還可以提供信息給編譯器,以優(yōu)化執(zhí)行效率。同時(shí),類型系統(tǒng)是對(duì)現(xiàn)實(shí)世界的抽象,可讀性高,也是更高級(jí)別抽象(如泛型,**OOP**s)的基礎(chǔ)。所以,無(wú)類型的語(yǔ)言在選型時(shí)基本會(huì)被排除掉。
 

強(qiáng)類型和弱類型


在語(yǔ)言的抉擇上,靜態(tài)檢查,動(dòng)態(tài)檢查檢查范圍這幾個(gè)角度影響的是程序的穩(wěn)定性和執(zhí)行效率,那么開(kāi)發(fā)效率呢?此時(shí)要引入另外一個(gè)維度,強(qiáng)類型弱類型,強(qiáng)類型是指一旦被指定類型,不可變,弱類型則可變,可變也分為隱式和顯式兩種。

 
// js 弱類型,隱式可變 
x = 1 
y = "2" 
z = x + y

弱類型提升了我們的開(kāi)發(fā)效率。

總結(jié),在語(yǔ)言的抉擇上,我們可以從類型系統(tǒng)的靜態(tài)檢查,動(dòng)態(tài)檢查,檢查范圍及類型是否可變這幾個(gè)維度來(lái)考慮,在開(kāi)發(fā)效率,執(zhí)行效率及安全性之間做權(quán)衡。無(wú)類型的語(yǔ)言可以很安全,但是可維護(hù)性差,基本被排除在系統(tǒng)開(kāi)發(fā)之外。

看到這里,你會(huì)發(fā)現(xiàn)文中并沒(méi)有講什么是動(dòng)態(tài)類型語(yǔ)言,靜態(tài)類型語(yǔ)言。因?yàn)檫@個(gè)維度并不是影響我們抉擇的本質(zhì)因素。動(dòng)態(tài)類型語(yǔ)言其實(shí)指的就是只有動(dòng)態(tài)檢查的語(yǔ)言,靜態(tài)類型語(yǔ)言指擁有靜態(tài)檢查的語(yǔ)言,此類型非彼類型,個(gè)人覺(jué)得是翻譯的鍋。
 

類型推斷


類型推斷(type inference)是類型系統(tǒng)中推測(cè)一段代碼(聲明,表達(dá)式等)的類型的過(guò)程。類型推斷讓我們?cè)诰帉懘a時(shí)能夠略去類型的聲明,并且不會(huì)影響到類型檢查。如果值的類型僅在運(yùn)行時(shí)才能被確定,這類語(yǔ)言被稱為動(dòng)態(tài)語(yǔ)言(dynamically typed), 同樣的,如果值的類型僅在編譯時(shí)被確定,這類語(yǔ)言被稱為靜態(tài)語(yǔ)言(statically typed).
 

類型理論


類型理論涉及到的內(nèi)容和類型系統(tǒng)會(huì)有部分重疊,你可以理解為,類型理論是服務(wù)于類型系統(tǒng)的,即,我們使用了哪些理論來(lái)構(gòu)建我們的類型系統(tǒng),或者說(shuō)該語(yǔ)言的類型系統(tǒng)實(shí)現(xiàn)了哪些特性。一門語(yǔ)言的類型系統(tǒng)一般只會(huì)實(shí)現(xiàn)其中的某幾種特性,這也是語(yǔ)言之爭(zhēng)的根源。
 

Polymorphism type


polymorphism翻譯為多態(tài)性,但不單單指面向?qū)ο罄锏亩鄳B(tài),而是指類型系統(tǒng)里的多態(tài)性質(zhì)。編譯時(shí)多態(tài),是在編譯時(shí)就能推導(dǎo)出類型或調(diào)用關(guān)系,宏也是一種編譯時(shí)多態(tài)。運(yùn)行時(shí)多態(tài)的實(shí)現(xiàn)依賴于虛函數(shù)機(jī)制(virtual function), 是在運(yùn)行時(shí)確定調(diào)用關(guān)系。多態(tài)性質(zhì)的引入可以提高代碼的復(fù)用率。

  1. Ad hoc polymorphism: 一個(gè)函數(shù)會(huì)根據(jù)有限的類型組合而擁有不同的實(shí)現(xiàn),函數(shù)重載(function overloading)和操作符重載(operator overloading)依賴于此. 從polymorphism性質(zhì)實(shí)現(xiàn)的角度講,屬于編譯時(shí)多態(tài)(static polymorphism).

     
    // java 
    public int Add(int a, int b) { 
    return a + b; 
    } 
    public String Add(String a, String b, String c) { 
    return a + b +c; 
    }
  2. Parametric polymorphism: 聲明的類型未被指定為某一類型,而在實(shí)現(xiàn)時(shí)可以指定為任意類型,即通常我們所說(shuō)的泛型,在C++里稱為模板(template). 從polymorphism性質(zhì)實(shí)現(xiàn)的角度講,屬于編譯時(shí)多態(tài)(static polymorphism).

     
    // java 
    ... 
    public class ObjectsServiceFactory { 
    public T save(T o) throws Exception { 
    try { 
    return repository.save(o); 
    } catch (Exception e) { 
    throw new DatabaseOperationException(String.format("save object %s failed, %s", o.toString(), e.getMessage())); 
    } 
    } 
    public void delete(T o) { 
    repository.delete(o); 
    } 
    } 
    ... 
    ObjectsServiceFactory s = new ObjectsServiceFactory(); 
    ObjectsServiceFactory i = new ObjectsServiceFactory(); 
    s.save("demo"); 
    i.save(100)
  1. Subtype polymorphism: 也叫subtyping, 一個(gè)類型的變量可以指代多個(gè)該類的子類實(shí)例,即我們常說(shuō)的多態(tài)。從多態(tài)性質(zhì)實(shí)現(xiàn)的角度講,此類屬于運(yùn)行時(shí)多態(tài)(dynamic polymorphism).

     
    // c++ 
    #include 
    using namespace std; 
    class Animal { 
    public : 
    virtual void shout() = 0; 
    virtual ~Animal(){} 
    }; 
    class Dog :public Animal { 
    public: 
    virtual void shout(){ cout << "dog"<shout(); 
    animal2->shout(); 
    animal3->shout(); 
    delete(animal1); 
    delete(animal2); 
    delete(animal3); 
    return 0; 
    }
  2. Row polymorphism: 也叫duck typing,針對(duì)結(jié)構(gòu)體類型,從功能(purpose)的角度對(duì)類型歸類。通常,對(duì)象是根據(jù)它們的類型來(lái)確定彼此之間的關(guān)系,比如subtyping中的父類/子類關(guān)系,而duck typing是通過(guò)函數(shù),如果它們實(shí)現(xiàn)了相同的函數(shù),就認(rèn)為它們是同一類。

    If it walks like a duck and it quacks like a duck, then it must be a duck.

    如果它走路像鴨子(實(shí)現(xiàn)了walk()函數(shù)),也會(huì)像鴨子一樣發(fā)出嘎嘎聲(實(shí)現(xiàn)了gaga())函數(shù),那它就是一只鴨子(屬于同一類型)。

     
    # python 
    class Duck: 
    def fly(self): 
    print("Duck flying") 
    class Airplane: 
    def fly(self): 
    print("Airplane flying") 
    class Whale: 
    def swim(self): 
    print("Whale swimming") 
    def lift_off(entity): 
    entity.fly() 
    duck = Duck() 
    airplane = Airplane() 
    whale = Whale() 
    lift_off(duck) # prints `Duck flying` 
    lift_off(airplane) # prints `Airplane flying` 
    lift_off(whale) # Throws the error `'Whale' object has no attribute 'fly'`

    duck typing也是go語(yǔ)言的主要特性,但是嚴(yán)格來(lái)說(shuō)并不算,因?yàn)閐uck typing發(fā)生在運(yùn)行時(shí),且沒(méi)有顯式的interface聲明,上面的python示例就是典型的duck typing

  3. Polytypism: 函數(shù)式編程語(yǔ)言里的泛型特性。以Haskell為例,其函數(shù)的定義比較具體化,單一化,缺乏可擴(kuò)展性和高度復(fù)用性,在Haskell語(yǔ)言上可以引入一種泛型機(jī)制解決上述問(wèn)題,這種泛型機(jī)制主要體現(xiàn)在泛型函數(shù)的定義上,泛型函數(shù)的定義不同于以往的函數(shù)定義方法,當(dāng)泛型函數(shù)遇到某種未定義的類型參數(shù)時(shí),它依靠泛型算法分析參數(shù)類型的結(jié)構(gòu),進(jìn)行相關(guān)轉(zhuǎn)換,可以自動(dòng)生成函數(shù)定義,這種方法可以提高程序的復(fù)用程度。[2]
     

Dependent types


依賴類型(或依存類型,dependent type)是指依賴于值的類型, 此特性通過(guò)極其豐富的類型表達(dá)能力使得程序得以借助類型的形式被檢查,從而有效減少程序錯(cuò)誤。依賴類型的兩個(gè)常見(jiàn)實(shí)例是依賴函數(shù)類型(又稱依賴乘積類型, Π-類型)和依賴值對(duì)類型(又稱依賴總和類型、Σ-類型)。[4]

一個(gè)依賴函數(shù)的返回值的類型可以依賴于某個(gè)參數(shù)的具體值,而非僅僅參數(shù)的類型,例如,一個(gè)輸入?yún)?shù)為整型值n的函數(shù)可能返回一個(gè)長(zhǎng)度為n的數(shù)組

 
// Idris 
// 連接兩個(gè)列表 
// Vect n a 是依賴函數(shù)類型,a是列表元素的類型,n是輸入?yún)?shù),Vect n a 返回一個(gè)長(zhǎng)度為n的列表 
app : Vect n a -> Vect m a -> Vect (n + m) a

一個(gè)依賴值對(duì)類型中的第二個(gè)值可以依賴于第一個(gè)值,例如,可表示一對(duì)這樣的值:它由一對(duì)整數(shù)組成,其中的第二個(gè)數(shù)總是大于第一個(gè)數(shù)。

 
def do(i : {i:Int | i<=j}, j : Int) := 
// do something
 
do(10, 1) // compile error
 
do(1, 10) // ok

以依賴類型系統(tǒng)為基礎(chǔ)的編程語(yǔ)言大多同時(shí)也作為構(gòu)造證明與可驗(yàn)證程序的輔助工具而存在,如 Coq 和 Agda(但并非所有證明輔助工具都以類型論為基礎(chǔ))。近年來(lái),一些以通用和系統(tǒng)編程為目的的編程語(yǔ)言被設(shè)計(jì)出來(lái),如 Idris。
 

Linear types


Linear types的思想來(lái)源于Linear Logic, 它確保對(duì)象在程序運(yùn)行期間有且僅有一個(gè)它的引用,這種類型用來(lái)描述不能被修改的值,比如文件描述符。linear 類型系統(tǒng)允許引用,但不允許別名(被多個(gè)變量引用), 類似于C++的unique_ptr指針, 只能被移動(dòng),不能被復(fù)制。
 

Intersection types


一個(gè)intersection type(交叉類型)是多個(gè)type的結(jié)合, 以此,你能夠得到一個(gè)包含多個(gè)類型的所有成員(members)的新類型!比如,現(xiàn)有三個(gè)類Person, Serializable 和 Loggable, 新的類型 T = Person & Serializable & Loggable, 那么類型T擁有Person,Serializable及Loggable的所有成員。

 
// TypeScript mixin example 
function extend(first: T, second: U): T & U { 
let result = {}; 
for (let id in first) { 
(result)[id] = (first)[id]; 
} 
for (let id in second) { 
if (!result.hasOwnProperty(id)) { 
(result)[id] = (second)[id]; 
} 
} 
return result; 
} 
class Person { 
constructor(public name: string) { } 
} 
interface Loggable { 
log(): void; 
} 
class ConsoleLogger implements Loggable { 
log() { 
console.log("papapa!"); 
} 
} 
var jim = extend(new Person("Jim"), new ConsoleLogger()); 
var n = jim.name; 
jim.log();

Union types


學(xué)過(guò)C語(yǔ)言的對(duì)此類型并不陌生,和intersection type類似,一個(gè)union type可以為多個(gè)類型,但是在任意時(shí)刻,它的值的類型只能是其中所有類型中的一種。

 
//c 
union a_bc { 
int i; 
char mm; 
};

TypeScript里的聯(lián)合類型用豎線|來(lái)分隔每個(gè)類型,所以 value : number | string | boolean表示一個(gè)值可以是 number,或string,或 boolean。

 
/** 
* TypeScript 
* Takes a string and adds "padding" to the left. 
* If 'padding' is a string, then 'padding' is appended to the left side. 
* If 'padding' is a number, then that number of spaces is added to the left side. 
*/ 
function padLeft(value: string, padding: string | number) { 
if (typeof padding === "number") { 
return Array(padding + 1).join(" ") + value; 
} 
if (typeof padding === "string") { 
return padding + value; 
} 
} 
let indentedString = padLeft("Hello world", true); // errors during compilation 
let ok = padLeft("Hello world", 0) // compile ok

Existential types


在理解存在類型(existential type)之前,我們先看下java的類型通配符 ? , 它代表一個(gè)未知類型.
 

Upper Bounded Wildcards


通過(guò)聲明通配的上限(父類)來(lái)匹配,如果你的函數(shù)入?yún)⒖赡苁荓ist\, List\或者 List\ ,你可以使用?聲明

 
public static void add(List list)

Number的所有子類都可以作為入?yún)? 例如:

 
//Java program to demonstrate Upper Bounded Wildcards 
import java.util.Arrays; 
import java.util.List; 
class WildcardDemo 
{ 
public static void main(String[] args) 
{ 
//Upper Bounded Integer List 
List list1= Arrays.asList(4,5,6,7); 
//printing the sum of elements in list 
System.out.println("Total sum is:"+sum(list1)); 
//Double list 
List list2=Arrays.asList(4.1,5.1,6.1); 
//printing the sum of elements in list 
System.out.print("Total sum is:"+sum(list2)); 
} 
private static double sum(List list) 
{ 
double sum=0.0; 
for (Number i: list) 
{ 
sum+=i.doubleValue(); 
} 
return sum; 
} 
}

省略寫法也是Upper Bounded

 
Collection c = new ArrayList(); 
c.add(new Object()); // compile error

當(dāng)類型不可推斷時(shí),上限是Object

 
List

Lower Bounded Wildcards


通過(guò)聲明通配的下限(子類)來(lái)匹配, 比如函數(shù)的入?yún)⒙暶鳛?

 
List

那么,Integer類型的所有父類都可以作為入?yún)。例?

 
//Java program to demonstrate Lower Bounded Wildcards 
import java.util.Arrays; 
import java.util.List; 
class WildcardDemo 
{ 
public static void main(String[] args) 
{ 
//Lower Bounded Integer List 
List list1= Arrays.asList(4,5,6,7); 
//Integer list object is being passed 
printOnlyIntegerClassorSuperClass(list1); 
//Number list 
List list2= Arrays.asList(4,5,6,7); 
//Integer list object is being passed 
printOnlyIntegerClassorSuperClass(list2); 
} 
public static void printOnlyIntegerClassorSuperClass(List list) 
{ 
System.out.println(list); 
}

那java的類型通配符和存在類型有什么關(guān)系呢?先看看scala的締造者Martin Odersky 對(duì)scala引入存在類型的回答

Bill Venners: Existential types were added to Scala relatively recently. The justification I heard for existentential types was that they allow you to map all Java types, in particular Java's wildcard types, to Scala types. Are existential types larger than that? Are they a superset of Java's wildcard types? And is there any other reason for them that people should know about?

Martin Odersky: It is hard to say because people don't really have a good conception of what wildcards are. The original wildcard design by Atsushi Igarashi and Mirko Viroli was inspired by existential types. In fact the original paper had an encoding in existential types. But then when the actual final design came out in Java, this connection got lost a little bit. So we don't really know the status of these wildcard types right now.

Martin Odersky 在scala郵件組里的回答

The original Java wildcard types (as described in the ECOOP paper by
Igarashi and Viroli) were indeed just shorthands for existential
types. I am told and I have read in the FOOL ’05 paper on Wild FJ that
the final version of wildcards has some subtle differences with
existential types. I would not know exactly in what sense (their
formalism is too far removed from classical existential types to be
able to pinpoint the difference), but maybe a careful read of the Wild
FJ paper would shed some light on it.

可見(jiàn),java類型通配符的設(shè)計(jì)思想來(lái)源于存在類型,但有些不同。scala引入存在類型是為了兼容java和jvm,所以會(huì)比?更強(qiáng)大。那么既然區(qū)別不大,理解了類型通配即理解了存在類型。這是一個(gè)曲線救國(guó)的方式。

接下來(lái),我們從數(shù)學(xué)定義的角度來(lái)理解。

existential type里的existential來(lái)源于存在量詞, 在謂詞邏輯中的解釋:

x: P(x) 表示存在至少一個(gè) x 使得 P(x) 為真。

存在類型的公式化表示:T = ∃X { X a; int f(X); }

類型X是存在類型, 即存在一個(gè)類型X,滿足此表達(dá)式,在編程語(yǔ)言里我們稱之為可實(shí)現(xiàn)。存在類型適合用來(lái)定義接口,不論是模塊之間還是語(yǔ)言之間。

這里要提下泛型(即前面講到的Parametric polymorphism, 也叫Universal type), 以避免混淆。Universal type中的universal來(lái)源于全稱量詞, 在謂詞邏輯中的解釋:
∀ x: P(x) 表示 P(x) 對(duì)于所有 x 為真。

泛型的公式化表示:

 T = ∀X { X a; int f(X); }

即,對(duì)于所有類型X,滿足此表達(dá)式。
 

語(yǔ)言規(guī)范


在語(yǔ)言學(xué)里有三個(gè)基本概念(也是三個(gè)分支),syntax, semantics, pragmatics. 即語(yǔ)法,語(yǔ)義(編譯結(jié)果)和語(yǔ)用(最佳實(shí)踐, 標(biāo)準(zhǔn)庫(kù), 生態(tài))。這里主要講語(yǔ)法層面。
 

Types


一門語(yǔ)言的規(guī)范,首先是類型(type), 聲明(statements), 表達(dá)式(expressions)等, 然后是作用域(scoping)。前面的內(nèi)容介紹了類型系統(tǒng), 那么該類型系統(tǒng)定義了哪些類型,實(shí)現(xiàn)了哪些特性是我們首先要了解的。通常一門語(yǔ)言的語(yǔ)法規(guī)范會(huì)以grammar[6]的形式來(lái)定義,例如golang中對(duì)于浮點(diǎn)數(shù)字的描述:

 
float_lit = decimals "." [ decimals ] [ exponent ] | 
decimals exponent | 
"." decimals [ exponent ] . 
decimals = decimal_digit { decimal_digit } . 
exponent = ( "e" | "E" ) [ "+" | "-" ] decimals .

這段文本描述了浮點(diǎn)數(shù)字的書寫規(guī)則,也是一棵語(yǔ)法樹(shù)。grammar對(duì)于沒(méi)有學(xué)過(guò)的人來(lái)說(shuō)有些難懂,所以本文中用類型實(shí)例的方式表示。
 

Primitive types


即基礎(chǔ)類型, 或者叫內(nèi)置(builtin)類型, 是程序以及復(fù)合類型的創(chuàng)建基礎(chǔ)。


Compound types


即復(fù)合類型, 也叫*Composite type*s. 復(fù)合類型可以由基礎(chǔ)類型和復(fù)合類型所構(gòu)成。


Statements


statement的直譯是聲明,但在這里按照代碼的邏輯單元來(lái)理解,一個(gè)statement是邏輯單元的開(kāi)始或結(jié)束。在書籍中,通常描述為xxx語(yǔ)句,比如if語(yǔ)句。
 

Empty statement


空語(yǔ)句, 不做任何事情。grammar規(guī)則:

 
EmptyStatement: 
;

Labeled statement


標(biāo)簽語(yǔ)句, 通常作為goto, break, continue 的目標(biāo),例如c/c++里的goto語(yǔ)句,一個(gè)單詞加上冒號(hào)即是labeled statement。下面代碼中的標(biāo)簽語(yǔ)句LOOP:即是goto的目標(biāo)。

 
/*c*/ 
#include  
int main () { 
/* local variable definition */ 
int a = 10; 
/* do loop execution */ 
LOOP:do { 
if( a == 15) { 
/* skip the iteration */ 
a = a + 1; 
goto LOOP; 
} 
printf("value of a: %d\n", a); 
a++; 
}while( a < 20 ); 
return

它的grammar規(guī)則為:

 
LabeledStmt = Label ":" Statement . 
Label = identifier .

labeled statement作為goto的目標(biāo)大家應(yīng)該見(jiàn)的很多,這里再舉一個(gè)作為continue的目標(biāo)的例子,便于理解。

 
 // golang 
guestList := []string{"bill", "jill", "joan"} 
arrived := []string{"sally", "jill", "joan"} 

CheckList: 
for _, guest := range guestList { 
for _, person := range arrived { 
fmt.Printf("Guest[%s] Person[%s]\n", guest, person) 

if person == guest { 
fmt.Printf("Let %s In\n", person) 
continue CheckList 
} 
} 
}

Expression statement


某些expression(表達(dá)式), 比如賦值,函數(shù)調(diào)用等,可以作為一個(gè)statement,稱之為expression statement
 

If statement


我們常說(shuō)的if條件語(yǔ)句,也叫If-then-else, 如果條件滿足則執(zhí)行此邏輯, 否則執(zhí)行它的else(如果存在)邏輯.

 
// golang 
if x > max { 
x = max 
}

Assert statement


斷言語(yǔ)句,assert 加真假值表達(dá)式, 如果表達(dá)式結(jié)果為false, 程序退出。

 
// c 
assert( size <= LIMIT );

不是所有語(yǔ)言都有斷言語(yǔ)句.
 

Switch statement


switch條件語(yǔ)句,判斷表達(dá)式的值,滿足不同的條件執(zhí)行時(shí)執(zhí)行不同的邏輯, 當(dāng)所有條件都不滿足時(shí),執(zhí)行默認(rèn)邏輯(如果存在).

 
switch(expression) { 
case n: 
code block 
break; 
case n: 
code block 
break; 
default: 
code block 
}

對(duì)于golang, switch statement分成了兩類, 一類是常規(guī)的expression switch(上面的例子), 一類是type switch(下面的例子)

 
// golang type switch 
switch i := x.(type) { 
case nil: 
printString("x is nil") // type of i is type of x (interface{}) 
case int: 
printInt(i) // type of i is int 
case float64: 
printFloat64(i) // type of i is float64 
case func(int) float64: 
printFunction(i) // type of i is func(int) float64 
case bool, string: 
printString("type is bool or string") // type of i is type of x (interface{}) 
default: 
printString("don't know the type") // type of i is type of x (interface{}) 
}

While statement


while循環(huán)語(yǔ)句, 重復(fù)判斷bool表達(dá)式的值,如果為真則執(zhí)行,直到表達(dá)式的值為假

 
// c 
while(condition) 
{ 
statement(s); 
}

tip: 在golang里,for語(yǔ)句加上bool表達(dá)式,可以實(shí)現(xiàn)while語(yǔ)句

 
// golang 
for { 
// code block 
} 
for a < b { 
f() 
}

Do statement


do語(yǔ)句,和while配合使用,先執(zhí)行一次邏輯,再判斷條件

 
// c 
#include  
int main(void){ 
int sum=0,i; 
scanf("%d",&i); 
do{ 
sum=sum+i; 
i++; 
} 
while(i<=10); 
printf("sum=%d",sum); 
return 0; 
}

For statement


for語(yǔ)句, 一般由三部分組成,初始化表達(dá)式,條件表達(dá)式和一個(gè)末尾表達(dá)式(post statement),初始化表達(dá)式只在開(kāi)始時(shí)執(zhí)行一次,條件表達(dá)式用來(lái)判斷本次執(zhí)行是否退出,末尾表達(dá)式在每次for語(yǔ)句的代碼邏輯執(zhí)行完后都會(huì)執(zhí)行。

 
// golang 
for i := 0; i < 10; i++ { 
f(i) 
}

For-in statement


for語(yǔ)句常用來(lái)迭代,于是出現(xiàn)了多個(gè)變種,for-in常見(jiàn)于腳本語(yǔ)言,如TypeScipt,Groovy, 用于迭代可枚舉的(enumerable types)數(shù)據(jù)類型

 
// JavaScript 
var person = {fname:"John", lname:"Doe", age:25}; 
var text = ""; 
var x; 
for (x in person) { 
text += person[x] + " "; 
}

這里,x被賦值為person這個(gè)map的key。當(dāng)遍歷的對(duì)象是map時(shí),x為key,當(dāng)遍歷的對(duì)象是數(shù)組時(shí),x為索引下標(biāo)
 

For-of statement


for語(yǔ)句變種, 類似于for-in, 用來(lái)迭代可迭代的(iterable)數(shù)據(jù)類型, 比如數(shù)組和字符串

 
// JavaScript 
function* foo(){ 
yield 1; 
yield 2; 
} 

for (let v of foo()) { 
console.log(v); 
// expected output: 1 

break; // closes iterator, triggers return 
}

這里變量v被賦值為foo函數(shù)返回的對(duì)象。被賦值的變量總是可迭代類型里的元素。

For-in vs For-of


For-range statement


for語(yǔ)句變種,在golang中用來(lái)迭代數(shù)組, 或者map

 
// golang 
nums := []int{2, 3, 4} 
sum := 0 
for _, num := range nums { 
sum += num 
} 

kvs := map[string]string{"a": "apple", "b": "banana"} 
for k, v := range kvs { 
fmt.Printf("%s -> %s\n", k, v) 
}

Break statement


跳出語(yǔ)句,用于立即跳出一個(gè)邏輯單元,當(dāng)不配合labeled statement使用時(shí),立即(abruptly)跳出最里層的一個(gè)封閉(enclosing)邏輯單元, 如switch, do, while, for. 當(dāng)配合labeled statement使用時(shí),立即跳出label標(biāo)定的層級(jí)的封閉邏輯單元。

 
// c 
#include  
int main () 
{ 
/* 局部變量定義 */ 
int a = 10; 
/* while 循環(huán)執(zhí)行 */ 
while( a < 20 ) 
{ 
printf("a 的值: %d\n", a); 
a++; 
if( a > 15) 
{ 
/* 使用 break 語(yǔ)句終止循環(huán) */ 
break; 
} 
} 
return 0; 
}

labeled break已在labeled statement中說(shuō)明,這里不再贅述.
 

Continue statement


continue語(yǔ)句,立即結(jié)束當(dāng)前層級(jí)的邏輯,跳轉(zhuǎn)到for循環(huán)的末尾表達(dá)式,開(kāi)始下一次循環(huán).

 
// golang 
package main 

import "fmt" 

func main() { 
rows := []int{1, 3, 5} 
colunms := []int{2, 4, 6} 

for _, row := range rows { 
for _, column := range colunms { 
if column == 4 { 
continue 
} 
fmt.Printf("%d-%d\n", row, column) 
} 
} 
}

上述代碼的輸出為:

 
1-2 
1-6 
3-2 
3-6 
5-2 
5-6

continuelabeled statement配合使用時(shí),不僅會(huì)結(jié)束當(dāng)前層級(jí)的邏輯,還會(huì)跳轉(zhuǎn)到label標(biāo)簽指定的位置。我們看下下面的代碼邏輯.

 
// golang 
package main 

import "fmt" 

func main() { 
rows := []int{1, 3, 5} 
colunms := []int{2, 4, 6} 

RowLoop: 
for _, row := range rows { 
for _, column := range colunms { 
if column == 4 { 
continue RowLoop 
} 
fmt.Printf("%d-%d\n", row, column) 
} 
} 
}

當(dāng)column等于4時(shí),結(jié)束邏輯,此時(shí)不是跳轉(zhuǎn)到當(dāng)前層級(jí)的post statement, 而是跳轉(zhuǎn)到RawLoop, 所以輸出結(jié)果應(yīng)該為:

 
1-2 
3-2 
5-2

不僅4沒(méi)有輸出,6也被continue掉了.
 

Return statement


return語(yǔ)句跳出當(dāng)前函數(shù),回到函數(shù)的調(diào)用方, 同時(shí)將一個(gè)或者多個(gè)返回值傳給調(diào)用方。本應(yīng)出現(xiàn)在最后一行的return語(yǔ)句,在沒(méi)有返回值的情況下,可以省略。Groovy語(yǔ)言的return語(yǔ)句是可選的。

 
 // sum method 
def static sum(n, closure) { 
for(int i = 2; i <= n; i += 2) { 
closure(i) 
} 
// return 2, 可以簡(jiǎn)寫成2 
2 
}

Throw statement


在一些語(yǔ)言中比如Java, JavaScript等使用throw語(yǔ)句來(lái)拋出錯(cuò)誤,以便上層的調(diào)用方能夠通過(guò)try-catch-throw的方式捕捉并處理。未捕捉的throw語(yǔ)句會(huì)導(dǎo)致線程/進(jìn)程終止。對(duì)于Java, throw的的對(duì)象必須是Exception或者其子類,對(duì)于JavaScript, throw的對(duì)象可以是任意類型.

 
// JavasScript 
function getRectArea(width, height) { 
if (isNaN(width) || isNaN(height)) { 
throw "Parameter is not a number!"; 
} 
} 

try { 
getRectArea(3, 'A'); 
} 
catch(e) { 
console.log(e); 
// expected output: "Parameter is not a number!" 
}

Goto statement


goto語(yǔ)句和labeled statement配合使用,用于邏輯跳轉(zhuǎn),程序執(zhí)行流程會(huì)直接跳轉(zhuǎn)到標(biāo)簽處. goto statement只有部分語(yǔ)言提供,而且寫法也有不同,比如對(duì)于golang, labeled statement必須在goto statement之前, 而C語(yǔ)言則無(wú)此限制。

 
// c 
#include  

int main () 
{ 
/* 局部變量定義 */ 
int a = 10; 

/* do 循環(huán)執(zhí)行 */ 
LOOP:do 
{ 
if( a == 15) 
{ 
/* 跳過(guò)迭代 */ 
a = a + 1; 
goto LOOP; 
} 
printf("a 的值: %d\n", a); 
a++; 

}while( a < 20 ); 

return 0; 
}

以上都是常見(jiàn)的語(yǔ)句,除此之外,語(yǔ)言也會(huì)有實(shí)現(xiàn)一些非常規(guī)的語(yǔ)句,比如golangdefer語(yǔ)句。
 

Operators


在介紹表達(dá)式之前,先介紹它的組成元素之一,操作符。其中,操作符分為一元(unary)操作符,二元(binary)操作符和三元操作符. 優(yōu)先級(jí)決定了在多個(gè)操作符同時(shí)出現(xiàn)時(shí),先使用哪個(gè)來(lái)求值。數(shù)字越大,優(yōu)先級(jí)越高。
 

Unary operators



Binary operators



Ternary operators


在計(jì)算機(jī)中也叫條件運(yùn)算符(conditional operator)


Expressions


在編程語(yǔ)言里一個(gè)表達(dá)式(expression[7])是由一個(gè)或多個(gè)常量,變量,操作符或函數(shù)組成。通過(guò)對(duì)表達(dá)式求值(evaluate)來(lái)得到一個(gè)新的值,這個(gè)新的值可以是基礎(chǔ)類型,也可以是復(fù)合類型。表達(dá)式在運(yùn)算時(shí),會(huì)進(jìn)行類型推斷和類型檢查。從之前講的內(nèi)容可以看出,statements的作用主要是控制代碼邏輯的執(zhí)行順序,expressions則是具體的代碼邏輯.
 

Variable expression

 
// golang 
c := a + b

Arithmetic expression

 
2 + 3

Relational expression

 
3 != 4

Function expression

 
// golang 
v := f(1)

Index expression

 
a[x]

表達(dá)式并沒(méi)有嚴(yán)格的分類,各個(gè)語(yǔ)言也不盡相同,這里僅列舉了部分例子來(lái)說(shuō)明。
 

Scoping


即作用域, 作用域是名稱(比如變量的聲明)和其實(shí)體(entity, 比如變量的定義)的綁定規(guī)則。作用域約束了實(shí)體的作用范圍,保證程序是無(wú)歧義的。
 

Expression scope


實(shí)體僅在表達(dá)式內(nèi)可用。

 
// c 
({ int x = f(); x * x; })

臨時(shí)變量x接受函數(shù)的返回值并平方,這樣避免兩次調(diào)用函數(shù)f.
 

Block scope


通常編程語(yǔ)言都會(huì)使用花括號(hào){}來(lái)將代碼包裹成塊(block), 在block內(nèi)聲明的實(shí)體,僅在block內(nèi)有效。

 
// golang 
{ 
var a int 
b := a 
} 
b := a // compile error
 
// golang 
{ 
var a int 
b := a 
} 
b := 1 // ok

Function scope


在函數(shù)內(nèi)聲明的實(shí)體,僅在函數(shù)內(nèi)有效。

 
def square(n): 
return n * n 

def sum_of_squares(n): 
total = 0 
i = 0 
while i <= n: 
total += square(i) 
i += 1 
return total

為了不和block scope混淆,這里用python的例子。
 

File scope


在代碼文件內(nèi)聲明的全局變量,僅在當(dāng)前文件內(nèi)有效。
 

Module scope


在某些現(xiàn)代語(yǔ)言中,一個(gè)實(shí)體可以在一個(gè)模塊內(nèi)的各個(gè)文件內(nèi)有效,比如golang. 部分語(yǔ)言,一個(gè)文件就是一個(gè)獨(dú)立的module,此時(shí),也屬于file scope.
 

Global scope


在所有模塊,所有文件內(nèi)都有效的實(shí)體稱為全局實(shí)體,此類作用域?qū)儆?em>global scope. 在編程實(shí)踐當(dāng)中,應(yīng)盡量避免使用。
 

Packages


一個(gè)復(fù)雜程序一般會(huì)由多個(gè)包(或者叫模塊)組成, 這種機(jī)制能讓程序的結(jié)構(gòu)和邏輯更加清晰可讀,提高代碼的復(fù)用能力,也可以借助module scope來(lái)避免同名之間的沖突。這里僅列舉幾種常見(jiàn)的包引入方式。

 
// Java 包名是一個(gè)層級(jí)結(jié)構(gòu) 
import org.apache.commons.net.util.SubnetUtils; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.security.core.context.SecurityContextHolder; 
import org.springframework.stereotype.Service; 
import org.springframework.util.StringUtils; 

Subnetutils sbt = new Subnetutils("xx")
 
// python 
from sys import argv

順便說(shuō)下python里from和常規(guī)import之間,在包的的使用上會(huì)有差異。

 
>>> from os import path 
>>> path. 
File "", line 1 
path. 
^ 
SyntaxError: invalid syntax 
>>> path 
 
>>> os.path 
Traceback (most recent call last): 
File "", line 1, in  
NameError: name 'os' is not defined 
>>> import os.path 
>>> os.path 
 
>>>
 
// node 
var circle = require('./circle.js'); 
console.log( 'The area of a circle of radius 4 is ' + circle.area(4));
 
// golang 
import ( 
"fmt" 
"github.com/xx/xx" 
)
 
// TypeScript 
import { cube, foo } from './mylib'; 
console.log(cube(3)); 
console.log(foo);
 
// c/c++ 
#include "my_header.h" 

int foo(char* name) { 
//do stuff 
return 0; 
}

編程范式


編程范式(programming paradigms),是在編程的理論與實(shí)踐當(dāng)中提煉出的概念模型。

Programming Paradigm: A conceptual model underlying the theories and practice of programming
 

IP(Imperative Programming)


即指令式編程。程序由一系列指令和流程控制語(yǔ)句組成,運(yùn)行過(guò)程中指令不斷改變程序的狀態(tài),由此達(dá)到最終的編碼意圖。IP范式會(huì)顯式地指定代碼的執(zhí)行流程,以及運(yùn)算邏輯。匯編是典型的使用IP范式的編程語(yǔ)言。

 
 result = [] 
i = 0 
start: 
numPeople = length(people) 
if i >= numPeople goto finished 
p = people[i] 
nameLength = length(p.name) 
if nameLength <= 5 goto nextOne 
upperName = toUpper(p.name) 
addToList(result, upperName) 
nextOne: 
i = i + 1 
goto start 
finished: 
return sort(result)

SP(Structured Programming)


即結(jié)構(gòu)化編程,在IP的基礎(chǔ)上,我們可以將用goto來(lái)控制流程的代碼,以for語(yǔ)句,while語(yǔ)句等此類結(jié)構(gòu)化的代碼塊(block structure)組織起來(lái),使得代碼的可讀性更高,那么此種編碼方式即為結(jié)構(gòu)化范式。SP是現(xiàn)代語(yǔ)言都支持的一種基礎(chǔ)范式。

 
result = []; 
for i = 0; i < length(people); i++ { 
p = people[i]; 
if length(p.name)) > 5 { 
addToList(result, toUpper(p.name)); 
} 
} 
return sort(result);

PP(Procedure Programming)


即過(guò)程式編程,單看中文可能難以理解,procedure來(lái)源于procedure call, 即函數(shù)調(diào)用,主要是因?yàn)?strong>PP在IP的基礎(chǔ)上引入了函數(shù)及函數(shù)調(diào)用, 將可提煉的邏輯用函數(shù)封裝起來(lái),以復(fù)用代碼和提高可讀性。

OOP(Object-oriented Programming)

即我們常說(shuō)的面向?qū)ο缶幊。?strong>SP和PP的范疇里,數(shù)據(jù)類型是松散的,數(shù)據(jù)結(jié)構(gòu)和算法之間也是松散的,而OOP則提供了一種類似于人類對(duì)現(xiàn)實(shí)世界建模的方法,對(duì)二進(jìn)制世界的類型和邏輯進(jìn)行建模和封裝,并在此基礎(chǔ)上提供了更多的類型和語(yǔ)法特性。OOP的優(yōu)點(diǎn)簡(jiǎn)列如下(封裝,繼承,多態(tài)):

  • 當(dāng)我們對(duì)一組數(shù)據(jù)類型進(jìn)行抽象,封裝成類(class, 類是OOP的基本概念)時(shí),我們可以定義該類的子類,來(lái)共享它的數(shù)據(jù)類型和邏輯,此方式稱為繼承(inheritance), 能夠有效減少我們的開(kāi)發(fā)時(shí)間。
  • 當(dāng)類被定義后,通常它只需要關(guān)注它自身的數(shù)據(jù)和邏輯,通過(guò)語(yǔ)法特性,一般是public/private關(guān)鍵字,將這類數(shù)據(jù)和邏輯隱藏起來(lái),避免被非法(或者說(shuō)不合理的, 不應(yīng)當(dāng)?shù)?訪問(wèn),提升程序和系統(tǒng)的安全性。
  • 一個(gè)封裝好的類,不僅能被它的創(chuàng)建者使用,也可以分發(fā)(在網(wǎng)絡(luò)上)給其他人使用, 比如Java的jar包。
  • 一門語(yǔ)言不可能把開(kāi)發(fā)者所需要的所有的類型都定義好,class的概念則很好地解決了這個(gè)問(wèn)題,開(kāi)發(fā)者可以定義任意自己想要的數(shù)據(jù)類型。
  • OOP的多態(tài)性質(zhì)可以讓代碼更加靈活。


DP(Declarative Programming)


即,描述性范式。和IP相反,此類語(yǔ)言只描述最終的編碼意圖,不描述達(dá)到意圖的過(guò)程。舉個(gè)例子,如何用程序來(lái)回答你是怎么回家的?

  • IP:

    Go out of the north exit of the parking lot and take a left. Get on I-15 south until you get to the Bangerter Highway exit. Take a right off the exit like you’re going to Ikea. Go straight and take a right at the first light. Continue through the next light then take your next left. My house is #298.

  • DP:

    My address is 298 West Immutable Alley, Draper Utah 84020

典型的DP范式語(yǔ)言如SQL, 僅描述目的,達(dá)到目的的邏輯被隱藏。

 
SELECT * FROM Users WHERE Country=’Mexico’;

LP(Logic Programming)


即邏輯編程,它屬于DP的范疇。邏輯編程的要點(diǎn)是將數(shù)學(xué)中的邏輯風(fēng)格帶入計(jì)算機(jī)程序設(shè)計(jì)之中。它設(shè)置匹配規(guī)則來(lái)解決問(wèn)題(rule-based),而非設(shè)置步驟來(lái)解決問(wèn)題, 即事實(shí)+規(guī)則=結(jié)果。Prolog是典型的LP范式語(yǔ)言,此類語(yǔ)言主要應(yīng)用在人工智能,專家系統(tǒng)等領(lǐng)域。
 

FP(Functional Programming)


即函數(shù)式編程,也是DP的子集, 在函數(shù)式編程里,所有的計(jì)算都是通過(guò)函數(shù)調(diào)用完成的,函數(shù)里的SP邏輯尤其是控制流邏輯,被隱藏了起來(lái). 假設(shè)我們要編寫一個(gè)函數(shù),將一個(gè)數(shù)組的每個(gè)元素都乘以2,PP風(fēng)格的代碼如下:

 
// TypeScript 
function double (arr) { 
let results = [] 
for (let i = 0; i < arr.length; i++){ 
results.push(arr[i] * 2) 
} 
return results 
}

上述代碼,詳細(xì)地寫明了整個(gè)計(jì)算過(guò)程,包括迭代過(guò)程和計(jì)算方法。所以IP范疇的范式會(huì)詳細(xì)描述計(jì)算機(jī)是如何完成這件事的,有篇文章是這么描述IP

First do this, then do that.

FP則不會(huì)描述數(shù)組是如何迭代的,也不會(huì)顯式地修改變量, 僅僅描述了我們想要什么,我們想要將元素乘以2.

 
function double (arr) { 
return arr.map((item) => item * 2) 
}

FP將開(kāi)發(fā)者從機(jī)器的執(zhí)行模型切換到了人的思維模型上,可讀性會(huì)更高。需要注意的是,某些支持FP的語(yǔ)言本身是屬于IP的,同時(shí)也可以認(rèn)為其屬于DP, 不必過(guò)于糾結(jié)。
 

FRP(Functional Reactive Programming)


即,函數(shù)式響應(yīng)型編程。
 

MP(Meta Programming)


即元編程, 也寫做Metaprogramming。元編程是一種可以將程序當(dāng)作數(shù)據(jù)來(lái)操作的技術(shù),元編程能夠讀取,生成,分析或轉(zhuǎn)換其他的程序代碼,甚至可以在運(yùn)行時(shí)修改自身. C++的template即屬于meta programming的范疇,編譯器在編譯時(shí)生成具體的源代碼。在web框架Ruby on Rails里,元編程被普遍使用。比如,在SQL數(shù)據(jù)庫(kù)的表里,有一個(gè)表users,在ruby中用類User表示,你需要根據(jù)user的email字段來(lái)獲取相應(yīng)的結(jié)果,通常的做法是寫個(gè)sql查詢語(yǔ)句去完成,但是Ruby on Rails在元編程的加持下,會(huì)讓這件事變得異常簡(jiǎn)單。

 
User.find_by_email('songtianyi630@163.com')

find_by_email并不是我們自己的定義的函數(shù),它是如何完成這件事的呢?框架會(huì)根據(jù)函數(shù)名find_by_email動(dòng)態(tài)生成一個(gè)查詢函數(shù)去查詢數(shù)據(jù)庫(kù)。除了這種黑魔法,元編程還能夠動(dòng)態(tài)修改一個(gè)類的成員函數(shù),在運(yùn)行時(shí)創(chuàng)建函數(shù)等等,這里不展開(kāi)講,在Ruby或者Groovy的相關(guān)書籍里會(huì)有詳細(xì)介紹。語(yǔ)言的反射特性和對(duì)模板的支持是實(shí)現(xiàn)元編程的主要基礎(chǔ),雖然c++并不支持反射,但c++的模板提供了元編程的能力。

編程范式還有很多細(xì)分項(xiàng),比如Event-driven programming, Distributed programming[10]等, 這里不再一一列舉。
 

依賴管理


有了合適的類型系統(tǒng),且該語(yǔ)言支持了我們所需要的編程范式,那么它就是不二之選了嘛?不一定。

長(zhǎng)期從事開(kāi)發(fā),或者從事過(guò)大型項(xiàng)目開(kāi)發(fā)的程序員會(huì)發(fā)現(xiàn)代碼的依賴管理也是開(kāi)發(fā)和維護(hù)過(guò)程當(dāng)中的重點(diǎn)。好的依賴管理會(huì)降低我們的心智負(fù)擔(dān),也會(huì)降低業(yè)務(wù)風(fēng)險(xiǎn)。

TODO
 

標(biāo)準(zhǔn)庫(kù)


TODO
 

選型參考


術(shù)語(yǔ)說(shuō)明


  • static and dynamic checks: 指該語(yǔ)言的類型系統(tǒng)會(huì)進(jìn)行靜態(tài)檢查和動(dòng)態(tài)檢查,注意,所有語(yǔ)言都有動(dòng)態(tài)檢查。
  • strongly checked: 以檢查范圍的大小作為標(biāo)準(zhǔn),意味著類型系統(tǒng)的類型檢查(type checking)已經(jīng)盡可能消除了類型錯(cuò)誤, 有些地方會(huì)稱為strongly typing, 有的地方甚至?xí)Q為strongly typed, 要注意區(qū)分.
  • weekly typed: 類型可變
  • strongly typed: 類型不可變
  • dynamically typed: 值的類型僅在運(yùn)行時(shí)確定
  • statically typed: 值的類型僅在編譯時(shí)確定
  • duck: duck typing, 也叫row polymorphism
  • generic: parametric polymorphism
  • subtype: subtype polymorphism
  • overloading: Ad hoc polymorphism, 函數(shù)重載或操作符重載或兩者都有

整理完這個(gè)對(duì)比表才發(fā)現(xiàn)一些有意思的事情:

  • 原來(lái)大部分語(yǔ)言都有OOP, js也是.
  • Go的優(yōu)勢(shì)并不在自己的類型系統(tǒng)和語(yǔ)法特性上, 它的設(shè)計(jì)充分體現(xiàn)了less is more的設(shè)計(jì)哲學(xué), 上手十分簡(jiǎn)單, 工程實(shí)踐效果很好。


參考文獻(xiàn)


  1. 《Type Systems》, Luca Cardelli
  2. 《函數(shù)式語(yǔ)言泛型特性的研究與實(shí)現(xiàn)》, LI Yang, YU Shangchao, WANG Peng
  3. 《Types and Programming Languages》, Benjamin C. Pierce
  4. 《依賴類型》, wikipedia
  5. 《what is dependent type?》, StackOverflow
  6. 《Antlr Docs》, github
  7. 《Expression (computer science)》, wikipedia
  8. 《List of programming languages by type》, wikipedia
  9. 《Comparison of programming languages》, wikipedia
  10. [《Comparison of multi-paradigm programming language》, wikipedia

標(biāo)簽: isp seo 安全 代碼 腳本 開(kāi)發(fā)者 數(shù)據(jù)庫(kù) 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:大數(shù)據(jù)+人工智能正以八種方式撼動(dòng)商界

下一篇:鉛酸蓄電池難以適應(yīng)數(shù)據(jù)中心的要求