Go 語法對第一次接觸 Go 的新手來有點怪,由於你們習慣了類 C 語法將類型放在前面的方式,對 Go 將類型放在參數後面有點不習慣,剛開始感受很彆扭,那 Go 設計者是基於什麼考量才設計成這樣呢?這裏咱們比較一下 C,Go,Haskell 三者的語法,能夠看到其實語言的語法其實都是服務於本身的設計目標的。編程
咱們先來看一下 C 語法,從大學出來的通常剛開始就是接觸的 C,培訓出身的剛開始接觸的應該是 Java,不過這二者在聲明語法上基本一致(固然 Java 簡化了不少,像指針就沒了),咱們就以 C 來看,畢竟 Go 號稱新世紀的 C 語言。數組
簡單聲明:編程語言
int x;
這裏咱們將類型放在左邊,在右邊是一個表達式,所以咱們聲明指針和數組這樣寫:函數式編程
int *p; int x[3];
這裏*p
的類型是int,x
是一個int類型的數組,x[3]
的類型是int。函數
函數也遵循這個基本的結構學習
int foo(int x) int foo2(char *arg[])
這是一個很聰明的結構,對於簡單類型來講,可是當類型變得複雜後,這個語法就會變得讓人迷惑,得費點工夫才能看明白。設計
聲明一個函數指針:指針
int (*fp) (int a, int b);
這裏 *fp
必須用括號括起來,以代表這是一個函數指針,若是咱們有一個函數指針的參數呢?code
int (*fp)(int (*fp1) (int a), int b);
這已經變得很是難看懂了,至少在第一眼的時候你看不懂,無論你怎麼加空格,若是你以爲還好的話,那咱們的返回值是一個函數指針呢?繼承
int (*(*fp)(int (*)(int, int), int))(int, int)
嗯,反正我已經看不懂了。
Java 裏沒有函數指針,只有使用接口,這大大簡化了類型聲明的複雜度,並且 Java 的數組聲明也和 C 不同,爲了保持清晰度,Java 將中括號挪到了類型後面 int[] a
, 而不是跟 C 同樣 int a[]
將參數放在中間。
Go 將類型放到了後面,咱們與 C 比對一下就能發如今複雜狀況下 Go 仍是能保證基本的類型清晰度。
基本聲明
x int p *int a [3]int
p
就是一個int類型的指針,不存在第二種寫法,數組也很明確的是類型的一部分。
看下函數:
func foo(a int, b *int) string
這和 C 感受也沒有多大的差異,並且從左向右讀起來也很順暢。
參數是函數和返回值是參數的狀況呢?
func foo(func(int, int), int) func(float, []int) string
仍是很是清晰,從左到右須要的參數和返回值都是一目瞭然的。
想要說明的一點是數組和指針的使用是和 C 同樣的,咱們獲取數組某個位置的值和指針指向的值:
x := a[1] int t = *p
聲明和使用中括號和星號的位置反過來了,數組的使用是從 C 繼承過來的, 指針的星號放在前面也是爲了避免和乘號的星號混淆,不過這樣咱們有時候在使用的時候也避免不了括號。
在我看來,這種狀況下不如直接換一個符號來獲取指針所指向地址的值,由於星號已經有了兩種語義,編譯器須要根據上下文來判斷星號表明的具體含義。我掃視鍵盤,以爲@
符號甚好,語義和含義都符合取值的要求,只是不知道語言做者在設計的時候爲何沒有考慮好,多是這個符號沒人用過,他們也就瓜熟蒂落的沿襲了 C 的語法吧。
Haskell 做爲一門純函數式編程語言,大部分人可能聽過,可是接觸過、學習過的人應該不會太大,畢竟日常工做用不到,我也只是簡單的瞭解過,裏面的一些函數式理念對於寫出更復用的函數有很強的啓發做用,建議你們去了解一下。
Haskell 的語法是與自身爲純函數式的編程語言分不開的,Haskell 不使用括號這種具備邊界性質的符號來界定參數,而是使用 ->
開放形式來聲明,返回值與入參同樣,都是用->
串起來的,使得聲明看起來很是的一致。
Haskell 是強類型語言,可是帶了一個很強大的類型推導系統,咱們在聲明變量時不須要指定變量的類型,編譯器會根據初始化數據或函數返回值等來判斷參數類型,另外一方面,Haskell是函數式編程語言,咱們聲明的類型都是 immutable 的,咱們看不到 int a
的狀況。
OK, 咱們如今來聲明一個函數:
inc :: Int -> Int inc x = x + 1
注:在 Haskell 裏,函數是一等公民,這裏我將函數的聲明類型也寫出來只是爲了清晰起見,其實咱們能夠簡單隻寫inc x = x + 1
, Haskell 自動推斷出相關類型。
咱們的入參是一個整數,返回值也是一個整數,從左到右很清晰,若是咱們的入參、返回值是函數如何呢?寫一個函數式編程裏經常使用的filter
filter :: (a -> Bool) ->[a] -> [a] filter _ [] = [] filter f (x:xs) | f x = x : filter f xs | otherwise = filter f xs
咱們使用括號來界定一個函數,代表這是一個總體,返回值也同樣,只須要在後面加上括號就能夠了,能夠看到也是很是清楚明白的。
Haskell 爲何要這樣設計? 這和 Haskell 語言的函數式本質是分不開的。函數式裏面有一個術語叫柯里化,柯里化後的函數能夠一次只接收一個參數,每次返回一個新的函數,直到全部的參數都知足了,纔會觸發計算返回最終值,而 Haskell 裏的函數默認是所有柯里化的,譬如咱們想過濾出列表裏全部偶數,咱們能夠這樣寫:
list1 = filter even a list2 = filter even b
這裏a/b都是列表,你有沒有發現filter even
咱們寫了兩邊,秉持DRY原則,咱們能夠將它抽出來變成一個函數:
filterEven = filter even list1 = filterEven a list2 = filterEven b
咱們只對filter
提供一個參數,返回值是一個接收一個list參數的函數,咱們就能夠複用咱們新的函數了。 回過頭來咱們再看一下 Haskell 的函數聲明語法a -> b -> c
,其實這裏面沒有什麼入參、返回值的區別,函數從左到右接收參數,返回值就是最後參數後面的部分,也就是說咱們提供了一個參數a
,返回就是b -> c
, 是否是很熟悉,這就是一個函數,咱們能夠按正常的函數來使用,由於它於正常函數的聲明是如出一轍的。
昨天(2018.09.26)在路上走着忽然又想起來這個,C 語言的聲明語法可類比中國人的姓名,而 Go語言的聲明語法可類比美國人的名姓。中國人的先姓後名致使通常孩子隨父親姓的話,不太可能將媽媽的姓也加進來,好比魏隨風,加入另外一個姓變成魏張隨風,魏馬隨風很奇怪,美國人的名字後面能夠加任意多的姓,Anderson Ma Li Zhang,並且也相對清晰。
各個語言在設計時總要當心的考慮本身的聲明語法,要使它符合本身的設計目標,同時語法又要儘量的簡單、清晰、易用,Go 在 C 語法上的基礎上作了一點改進,就讓一些複雜狀況變得清晰了,可見也是下了很大功夫的。同時咱們也不要僅僅侷限在類 C 語言的語法上,一些其餘的語言像函數式編程語言,聲明式編程語言的編程思想對咱們也會有很大的啓發,多涉獵一下,對咱們思考問題的思路會有很大的啓發做用。