各個編程語言都有哪些「亮點」?
鏡像問題 各個編程語言都有哪些「黑點」? - 計算機 - 知乎
潛水了好幾天沒人提Clojure,我來開個頭吧。
這幾年總是有人問我Java入門了再學個什麼語言比較好,在我自己的經歷中,Java是我的第N語言,因此談不上實際經驗。但在幾年前,我的回答是Ruby,一年前,變成了Clojure。
我的觀點是,第二語言應該選一個成熟的但與第一語言有顯著不同的,開眼界的的意義多於使用。學習的過程應該充滿了「原來這樣也可以」的驚喜,而不是「這不就是XXX里的YYY么」的虛假掌控感。更嚴重的是看了半天第二語言,回過頭來覺得第一語言是垃圾,那就是失敗中的失敗。每一種新語言的學習都應該回饋你已經入門的語言,拓展對原有語言的理解,而不應該導致你完全放棄先前的語言,否則你每看一個新語言,要麼簡單嘗試後就覺得新語言不值得學,要麼新語言對胃口舊語言是垃圾,最終結果就是要麼固化在某一個語言所形成的舒適區中,要麼各種語言都淺嘗輒止,不能深入。如果語法結構相近(比如選Groovy,Scala,Kotlin作為第二語言),很容易就陷入無聊的代碼風格喜好的比較。
而Clojure作為Java程序員的第二語言的最大亮點就是,同樣在JVM上,在可以與JAVA互操作的前提下,它有著與Java完全互補的編程概念與開發體驗。可以說是對Java的一個完美概念補完。學習了Java與Clojure後再回過頭去看大部分JVM語言,你會發現其大部分概念都落在兩者之間,而不至於每接觸一個新語言都大驚小怪的一番了(當然我只是想像,我接觸Clojure之前已經接觸過一些其他語言,包括LISP系的Scheme和Common LISP了)。況且,Clojure本身也是一個很實用的語言,我對Scheme和Common LISP就純屬是玩票,因為學了語言沒時間熟悉第三方庫,短時間內做不出什麼真正實用的東西。而Clojure可以直接使用熟悉的Java類庫。這幾年我用Ruby,Scala,Groovy,Python都寫過本地腳本,現在已經全部遷移到Clojure上,目前Clojure也是我的主力腳本語言。
下面簡單列一列(我想像中)學習和使用Clojure會給新手帶來的體驗。不過我在函數編程方面浸淫不深,本文將以基於面向對象開發背景的實用為主,高手請勿噴臉。
對比一下Java與Clojure的不同
Java:靜態類型,面向對象(基於名詞),風格嚴謹(鼓勵使用統一的最佳實踐寫法,不鼓勵使用非既定語法或模式的任何東西)
Clojure:動態類型,面向函數(基於動詞),風格鬆散(天馬行空, 在幾條最簡單的基本規則之上可以隨意構造語法元素)
亮點一:各種基本元素任意組合
Clojure是LISP的方言,如果你簡單了解過LISP,你可能已經知道LISP的程序本身就一棵語法樹,所有表達式都是「動作」帶頭的中綴表達式,比如說(+ 1 2 3 4)對應其他語言中的1+2+3+4。同樣,Clojure里提供了以下方式來調用對象方法:
(. "abc" substring 0 3) 對應java的 "abc".substring(0, 3)
(.substring "abc" 0 3) 對應java的 "abc".substring(0, 3)
第一種寫法,. 是動作(表示「調用對象方法」),然後第一個參數是對象(換言之就是this),第二個參數是方法名稱,後續的參數對應原本的參數表。
第二種寫法,方法名稱 .substring 是動作(看作一個函數),然後第一個參數是this,後續參數對應原本的參數表。
別看只是簡單的寫法變換,事實上它把Java中寫死的 對象.方法 模式打散了,你能夠清楚看到其中 「this引用」,「方法調用操作符」,「方法」 等元素以不同形式組合起來。以後接觸到Javascript里 foo.apply(obj, x ,y) 或者Java反射里的 method.invoke(obj, x, y) 之類的寫法也就順理成章了。
那麼Java里的鏈式調用怎麼辦,按上面的寫法,employees.get("Tom").getName().getFirstName() 得寫成 (.getFirstName (.getName (.get employee "Tom"))) 這得多醜。
事實上,前面說了,在Clojure里各種調用元素已經打散了,你可以用任意方法把它們組合起來。例如:(.. employees (get "Tom") getName getFirstName)
;; .. 表示鏈式調用對象方法
(-&> employee (.get "Tom") .getName .getFirstName StringUtils/isNullOrEmpty)
;; -&> 表示鏈式調用任意函數
(some-&> employee (.get "Tom") .getName (.setFirstName "Tommy"))
;; some-&> 表示null安全的鏈式調用,調用鏈中任意一個節點結果為null則停止調用,
;; 返回null。類似Groovy里的 ?. 操作符。
其實鏈式調用的原理很簡單,定義一個動作,從一個起始值開始,把它插入到一個後續函數中作為第一個參數,再把調用結果插入到再下一個函數中作為第一個參數,依此類推即可。
特別地,some-&> 是基於宏展開為if判斷非null的方式,上述代碼宏展開後如下

而非像Groovy那樣採用catch NullPointerException的方式。使用Groovy的?.操作會干擾調試時使用NullPointerException異常斷點,一度令我非常抓狂。而Clojure的some-&>實現沒有這個問題。
當然還有更多玩法,在Java里如果想用鏈式調用串起一系列setter,你要麼得破壞JavaBean協議在setter里返回this,要麼得添加一個新方法。而且對於已經寫死了返回值為void的方法就只能幹瞪眼或者寫wrapper。而在Clojure里很簡單:
(doto employee
(.setName "Tom Parker")
(.setAge 28)
(.setPosition (Position. "Java Dev"))
;; (Position. "Java Dev") 表示調用Position的構造方法,相當於 new Position("Java Dev")
do 表示把一個值插入到後續一系列函數調用中作為第一個參數,最終返回原始的值(例子中返回employee), 這就類似於Groovy里的 employee.with {name = "Tom Parker";/*任意操作*/; it}。進一步,我可以很方便的在一個單一結構中構造一個即用即棄的對象,設置其初始狀態,然後調用一個關鍵方法
(def employee (-&> (doto (EmployeeFactory.)
(.setType (doto (SimpleEmployeeType.)
(.setAddressRecordType "json")))
(.setRecordType "xml"))
.createInstance))
寫成java,得寫成
EmployeeFactory employeeFactory = new EmployeeFactory();
SimpleEmployeeType simpleEmployeeType = new SimpleEmployeeType();
simpleEmployeeType.setAddressRecordType("json");
employeeFactory.setType(simpleEmployeeType);
employeeFactory.setRecordType("xml");
Employee employee = employeeFactory.createInstance();
同一個單詞重複了多少次就不提了,畢竟寫Java也習慣了,最大問題是引入了兩個中間變數,我還得為它們命名。要知道編程兩大難,緩存失效與命名。
用Groovy可以寫成
def employee = new EmployeeFactory.with {
type = new SimpleEmployeeType().with {
addressRecordType = "json"
it
}
recordType = "xml"
it
}.createInstance()
寫法上倒也差不多,但Groovy使用了兩次開域方法with, 使用了兩次Lambda,資源損耗上比Java多了不少。而Clojure的代碼宏展開後與Java代碼邏輯上是完全相同的,僅僅在兩個do上使用了兩個(隱含的)局部變數。(論跑benchmark,clojure可是跟scala同一級別的 https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=clojurelang2=scala )
亮點二:編輯方便
等等,有沒有搞錯,這一堆括弧套括弧的東西你跟我說編輯方便?我連看都看不清楚!
其實作為跟C一樣古老的語言(如果不是比C更古老),LISP界早有成熟的解決方案。就以上面的代碼為例,在IDE里看起來是這樣的。

無論採用怎樣的縮進方案,結構都很清晰(看括弧顏色就行),而且因為基本元素都是獨立的,隨便怎樣斷行縮進都很自然。例如在Groovy里如果.createInstance()那裡斷一行就顯得頗為奇怪,因為.createInstance()不是一個獨立的語法元素,而它之前的new EmployeeFactory.with{}卻可以作為一個獨立元素,也就是如果在這裡斷行,讀者就會先以為def employee=new EmployeeFactory.with {}是一個完整的statement,直到看到下一行,才發現它還沒完。而Clojure里,無論你選擇斷行或不斷行,都很自然。
更重要的是,由於一個語法結構總是包含在括弧中,我可以方便地選中整個結構(子樹)。用VIM鍵位的話,跑到括弧最頂層vi(即可。而無論是Java,Groovy或Scala,你都沒有辦法在一堆並列的代碼里直接選中上面這個結構。特別是Java,不靠空行的話你在一堆代碼中目測分離出這段代碼都不容易。
那麼編輯呢,兩頭插括弧夠痛苦了吧。其實只要習慣了讀Clojure (LISP)程序,整個程序就是一棵語法樹,編輯程序就是在修改語法樹的結構,用這個角度來看,一切就括然開朗了。常用的編輯動作就幾種,一般的Clojure編輯器都會提供對應的方案。
1. 游標右邊的子樹加深一層。
用人話說就是把游標後面的語法結構兩頭套括弧,比如你有 (A (B C)), 以|表示游標
|(A (B C)) 時使用就變成 (|(A (B C))) ;; 下文用 ==&> 表示執行編輯操作後的結果
(|A (B C)) ==&> ((|A) (B C))
(A |(B C)) ==&> (A (|(B C)))
一個簡單的場景是,原來有一大段代碼
(/*幾十行代碼*/)
現在我要用if把它包起來。我只需要把游標移動到左括弧前,加深子樹,變成
(|(/*幾十行代碼*/))
繼續直接輸入if和條件就變成了
(if (some? amount) (/*幾十行代碼*/))
在其他語言一般的做法是在要移入if的代碼塊前面打出一個if語句,IDE自動生成空的花括弧。然後移動游標選擇要移入的代碼,目測保證結構完整,然後剪切移入。(也有人喜歡打if和左花括弧後不按回車,然後移動游標找到要包圍的代碼結尾,加入右花括弧。這種方法的問題是,這時被包圍的代碼還保留著原來的縮進,代碼也沒被選中。唯一方便的做法是全文自動格式化一下,而我是很不喜歡無腦全文格式化的,玩習慣了有一天換了個編輯器沒配好格式那就是給整個團隊找麻煩)。
2. 游標所在的元素提升一層,代替原來的父節點。
用人話說就是去掉包圍當前元素的代碼
(A| (B C)) ==&> A
(A (|B C)) ==&> (B C)
(A (B| C)) ==&> (A B)
基本就是1的逆操作,我有一段在try catch里的代碼,現在我不想要try catch了,一鍵解決。
3. 把當前節點的下一個相鄰兄弟變成當前節點的最後一個子節點
用人話說就是擴大右括弧的範圍
(A| B) C (D E)
==&> (A| B C) (D E)
==&> (A| B C (D E))
簡單場景:原來代碼已經有連續兩次對同一個對象的setter調用,現在我要加入第三個,我覺得應該把它們組織起來形成一個獨立結構了。
原來
(.setName employee "Tom")
(.setAge employee 35)
修改:在前面加入新的setter, 使用重複調用的do形式:
(doto employee
(.setPosition "PM"))
(.setName employee "Tom")
(.setAge employee 35)
(doto employee
(.setPosition "PM")
(.setName employee "Tom")
(.setAge employee 35))
(doto employee
(.setPosition "PM")
(.setName "Tom")
(.setAge 35))
4. 當前選中內容作為一個節點的子節點
用人話說就是選中內容兩頭加括弧。VIM里裝了surrounding插件用 S 即可。
這個跟第2個操作配合起來簡直爽歪歪,我需要去掉任何選中代碼兩端的包圍代碼,只需要兩頭加括弧,提升層次,去掉括弧即可。在Java里我得先從要保留的代碼開頭往上查找到父結構的起始位置,刪掉。再找到要保留代碼的結尾,往下查找到父結構的結尾(注意這時由於父結構開頭已經被刪了,整體代碼結構已經被破壞了,一不小心看走眼了就要redo重來),刪掉。
5. 針對當前節點的選擇和刪除。
就是VIM里的 da( 和 va( 。就不用展開了。
亮點三:宏
估計不少人已經聽說過LISP的宏強大,具體理論我這裡不展開了,就秀點簡單的宏吧
1. 重複用不同參數執行同一方法。
StringBuilder的sb.append的N連擊算經典了,有沒有人覺得打一堆append也很煩啊,但為了少打幾個append專門寫個方法用個for來循環調append又太大題小作。同樣某些Obserever模式代碼連續加好幾個listener,狂調register方法也很煩。
一個宏搞定:
(defmacro doto-with-method
"Invoke the same method on an object with different arguments"
[x m args]
(let [gx (gensym)
gm (if (keyword? m) (symbol (name m)) m)]
`(let [~gx ~x]
~@(map (fn [a]
(if (vector? a)
`(~gm ~gx ~@a)
(if (nil? a)
`(~gm ~gx)
`(~gm ~gx ~a))))
args)
~gx)))
用法
(def ejb-java-scanner-chain
(func/doto-with-method (CompositeJavaCodeScanner.) .add
(NameMappingScanner.)
(ObserverAnnoScanner.)))
(func/doto-with-method sb .append
"&
展開效果

2. 輸出表達式和值
Java8的Stream有個peek方法,在對鏈式調用進行查錯時挺好用的。可惜不是所有對象上都有peek(在強類型系統里也沒什麼用),比如說a.getA().getB().getC()這樣的鏈式調用,我想打日誌就沒法寫a.getA().peek().getB().peek().getC()了,只能另起一行手動拷貝。Groovy這種動態類型語言可以通過修改Object的metaclass解決,屬於高級特性(不嫌丑的話也可以寫個函數解決,寫成 peek(peek(a.getA()).getB()).getC(), 當然這樣搞改起來麻煩,改回來也麻煩,一般沒人會用 )。Scala應該是可以通過隱式類型轉換來加,也不簡單。而在Clojure里簡直是順理成章的事,定義個函數
(defn dprint [x]
(print "debug =&> ")
(pp/pprint x) ;; pretty-print
x)
搞定。Clojure的鏈式調用本來就可以插函數的(方法調用與函數是同構的):(-&> a .getA .getB .getC) 改成 (-&> a .getA ddprint .getB ddprint .getC) 即可。排完錯直接刪掉就行。而且在Clojure里這個dprint用起來無比方便,我要輸出任何表達式,找到其開始括弧,一鍵在外圍套個括弧,然後直接打dprint就行了。
|(foo x y z)
==&> (|(foo x y z))
==&> (dprint| (foo x y z))
==&> (dprint (foo x |y z))
==&> (dprint (foo x (|y) z))
==&> (dprint (foo x (dprint y) z))
輸出整個foo函數的返回值和參數y的值。排錯完畢,找到原來的表達式,一鍵提升語法樹層級,就還原了,十分方便。
(dprint (|foo x (dpring y) z))
==&> |(foo x (dprint y))
==&> (foo x (dprint y|) z)
==&> (foo x y z)
以上是背景介紹,到目前為止只是使用了簡單的普通函數。這個調試函數還有個美中不足的地方,它只輸出表達式的結果,多起來分不清誰是誰。這時候宏就有用了:
(defmacro dprint [x]
`((fn [x#] do
(print (str "debug: " (quote ~x) " =&> "))
(pp/pprint x#)
x#) ~x))
跟函數版也沒差多少,使用效果是:

更進一步,鏈式取值的表達式一個個輸出也太麻煩了,我想乾脆把鏈式取值的每一步都輸出來,也不難
(defmacro d-&>
[expression forms]
(let [$ (gensym)]
`(as-&> (dprint ~expression) ~$
~@(map (fn [form]
(if (list? form)
(cons `dprint (list (cons (first form) (cons $ (rest form)))))
(cons `dprint (list (cons form (list $))))))
forms))))
使用時用d-&>代替-&>就行了。使用效果

3. 類型判斷後直接轉型
剛剛看到Kotlin提供的這個功能時還是挺驚艷的,然而轉念一想在Clojure里實現一下也是很簡單的 (Clojure雖然是動態類型語言,但如果你提供類型信息的話,編譯器會編譯為原生的Java的方法調用,有一定的查錯輔助功能並大大提供執行效率)
(defmacro when-instance?
"Instance check for a varible then cast it to the class checked.
You can optionally provide a :else cause at the end.
e.g. (when-instance? String a (println (.toUpperCase a))
(when-instance? String a (str (.toUpperCase a)) :else (str "a " (class a))"
[class var forms]
(let [forms-count (count forms)
second-last-form (if (&> forms-count 2) (nth forms (- forms-count 2)))
else-cause (if (= second-last-form :else) (last forms))
forms (keep-v forms :unless else-cause :or (take (- forms-count 2) forms))]
`(if (instance? ~class ~var)
(let [~(vary-meta var assoc :tag class) ~var]
~@forms)
~else-cause)))
4. 延遲執行
這是LISP宏的經典應用場景了,有興趣可以去讀讀相關資料,這裡只給出一個例子讓大家感受一下。
我們用代碼拼字元串時經常會遇到這樣一個場景,字元串的某一個片段包含一個關鍵部分,如果這個關鍵部分是空的,那麼整個片段都不應該出現。也就是說這個片段分為「前綴」,「主體」,「後綴」三個部分,主體為空時則全不輸出。但這三個部分求值代價不小,我希望主體只求值一次,如果其為空,則前綴和後綴部分完全不求值。
Java的代碼看起來是這樣的
在Clojure里,我可以做這樣一個宏 "Build chained appends for StringBuilder/StringBuffer. e.g. (append-sb sb "1" 2 (str 3)) will be expended to You can also use a map with :prefix/:main/:postfix elements, the whole map will be appended only e.g. (append-sb sb 1 {:prefix 2 :main (:none {})} 3) results in "13"" [string-builder appends] 那麼上面的代碼就可以寫成 宏展開後代碼長成這樣: ======= 開始 PS 的分割線 ================== "A human friendly syntax for (if pred form origin). [origin unless-key pred or-key form] ======= 結束 PS 的分割線 ================= Cirru 已經載入 GitHub 了,拿出來扯扯。 特別之處在於 Cirru 認為編程語言應該直接編輯語法樹,而不是用文本寫好代碼然後解析出來語法樹。因為樹是常用的數據結構,實際上比較容易用 GUI 設計編輯器,渲染成複雜界面,用 CSS 控制各種樣式的細節,比如自動布局,代碼摺疊等等。所以程序員們就不用為分號寫不寫,括弧寫不寫,換行不換行,而爭執了,代碼長什麼樣 CSS 直接畫好了,有什麼好操心的。 由於 Cirru 只有語言前端,沒有自己的後端,目前主要是編譯到 Clojure 或者 JavaScript 之後才能運行的,只有一個用戶。 這種東西不需要在py(念piyan)里找,F#就可以,而且編譯器優化效果很好。如 未優化前,ILSpy還原出的C#代碼為: Release版為: 完全符合let 為值綁定的語義預期。但是實測,C#中,前者代碼是不可以優化成後者的。 static Func& 破案,有時候就這麼簡單。 當然,某些時候你還是不得已繼續使用lock。(使用using關鍵字調用ulock完全等價於lock,只是代碼還長一點) 不需要用到介面,因為F#是靜態語言,所以調用getLongID時,編譯器可以檢測你的對象是否具有LongID屬性,再而針對調用它的類生成重載代碼。這個功能是目前我認為最強大的,既模仿了動態語言的靈活性,又使用了靜態語言的嚴格檢測,甚至有時候能搶反射的飯碗,正確使用就可以避免介面滿天飛的情況,寫DSL時這特性很管用。但是只能F#使用,不能給C#做泛型調用,出問題後果自負。 Go: 內置並發,易用性很好,調度器幫你屏蔽了很多複雜的細節; 不用刻意的去用非同步的思維去寫非同步的代碼,寫call back。Go通過 goroutine + channel就能以同步的思維方式達到非同步的效果; 靜態編譯,部署方便,一個二進位,沒什麼依賴,比如:你經常會遇到,要使用xx,你得先在你得機子上再下載一坨xxx;沒完沒了了,越依賴越多的動態鏈接文件等等。 帶gc但卻沒有vm語言那麼龐大,占內存更少,AOT編譯,啟動快,編譯速度快。 性能不錯,開發效率也不弱,平衡取捨的恰到好處;其他語言玩的6的,上手Go沒難度,半天可上項目; 標準庫小巧精悍,代碼質量高,沒有層次不齊的情況,基本上涵蓋服務端編程所需,沒有的輪子也可以很快的利用它根據所需快速的造出來; tooling的支持,battery included。Go官方一整套工具鏈,可以和其他編輯器,IDE靈活搭配,編輯器裡面照樣可以和ide那樣代碼跳轉,重構,分析以及調試; gofmt:你使用過之後,你一定會愛上它。再推薦個goimports; 對語法特性敢於說不,即使站在風口浪尖。幾乎所有編程語言進化過程中最終都會變成堆砌語法特性,語言的正交性,簡單優雅卻在不斷喪失。從這個角度來說,lisp是美麗的語言,C也有它的美。 吉祥物有時很萌,有時又有點邪惡的感覺。 Python 0. 一行代碼即可向世界問好 什麼 import 啊 include 啊都不需要 1. 變數不需要聲明,隨時隨地添加 2. 整數多大都不怕 3. 便捷的循環語句 4. 隨手定義匿名函數 5. 完善的異常機制避免了冗長的 if 要是用 if 的話,就是: 6. 不需要知道這個類是什麼,只需要知道它有什麼功能 要是用 C++ 的話,還需要用虛函數,而且需要一定技巧才能把它們放到同一個 vector 里。 7. 輕輕鬆鬆保存對象 &>&>&> string = pickle.dumps(objects) &>&>&> objects2 = pickle.loads(string) 把 string 扔到文件里就相當於保存遊戲進度了。 沒有人詳細說說Rust么? 正好用這個帖把我在知乎的Rust相關收藏整理分享一下: 最後,大多數錯誤都可以在編譯期找出。C++經常會有Runtime error,但是到Rust這裡很多類似的錯誤過不了編譯器這一關。因為錯誤總是發現的越早越好,原則上說這是個好事,實際上是這樣的: C++:編譯的時候明明好好的,為什麼運行起來就報錯!這讓我查起來多麻煩! Rust:編譯器我求求你了,先讓我通過了先跑起來再說吧…… 補充:知乎日常黑Perl,不過我不知道有多少人真的對Perl比較熟悉。實際上,我感覺Perl在語言層面有很多優點,use strict之後也很嚴格。主要的缺陷其實是API極其噁心,寫個擴展巨麻煩。 ================================ 我想計算第一列加第二列,把兩列輸入和輸出打成三列: 只要deep_dark開頭的行: 確保匹配: 為此,Perl的and、or關鍵字的優先順序比很多操作符都低。 Y(lambda g: Y(function(g) { ((Y (lambda (g) 總感覺C#可以做到其它編程語言的很多亮點啊 (好吧關於可變類型數組是有少量噪音) 0. 一行代碼即可向世界問好 script模式下的C#會默認做常用的reference和using 1. 變數不需要聲明,隨時隨地添加 說實話,沒有了古典C時代的「變數必須定義在過程開頭」這種限制,和隱式類型聲明之後,這個特性已經沒多大意義了 2. 整數多大都不怕 整數這個……BigInteger字面值,我承認有字面值是一件舒服的事情 3. 便捷的循環語句 讓用戶輸入 4 個整數: 配合一下自定義Input就可以了( 找出首字母為 "A" 的單詞: 4. 隨手定義匿名函數 5. 完善的異常機制避免了冗長的 if 呃,你跟我說完善 6. 不需要知道這個類是什麼,只需要知道它有什麼功能 一個List& 7. 輕輕鬆鬆保存對象 講一下Python。 Python,語言本身寫著爽,有舒適的握把和合適的重量。 1. 寫著爽。 語言簡練,很少的代碼就能把你想做的功能寫出來。 環境配置也沒有那麼麻煩,不會環境一小時代碼五分鐘。 2. 庫不僅強大而且調用簡單。 想做什麼import一下就好了,還有漫畫吐槽這件事。 3. 領域廣。 雖然不至於什麼都能做,但也差不多了。 下個視頻有you-get, 還是一個膠水語言。實在有需要別的語言的地方,調用也很方便。 4. 社區和用戶活躍。 技術分享的文章、各類的群、各種相關網站都很活躍,中文資料也很多。 項目也會發展較快。 用戶量也在不斷增加。 另, 以下都是自己的體會,如果有不正確的地方,還請指教。 Python PHP C C++ Java C# Javascript Go Rust Lisp 強大的DSL能力 DSL(領域特定語言)這個概念不是從Ruby這裡誕生的,但是在Ruby社區這裡受到了格外地強調。 這個是用以在固定時間運行任務的javan/whenever包,有沒有覺得讀起來很像英語? 或者說,在Rails裡面,我們還可以用 來表示三小時前的時間。 眾多Ruby的庫和框架無不以寫得像英語為目標。喪心病狂的Rails甚至專門有一部分是處理單複數轉換的!這些東西看上去花哨,而實際上無非是用方法調用、代碼塊、Hash鍵值對的混合實現的,因為可以省略方法調用的括弧和Hash的花括弧,看起來異常地清爽。 動態,徹底的動態 上面那個是怎麼做到的?Ruby允許對自帶類型進行擴展,並且任何東西本質上都是一個對象,包括整數、字元串的字面量。(說到這裡又開始期待C++的Uniform Syntax Function Call了)像我們的整數類型,它具有to_s、to_c這樣的方法,可以把整數轉換成字元串或者複數。而Ruby是一個動態強類型語言,在這一點上比較像Python。Ruby的空是nil,鍵值對是Hash,數組是Array……這一點和JSON挺對應的。那我們要是想把對象序列化成JSON字元串該怎麼辦呢?於是,我們可以 一旦包含了JSON這個包,這些對象就會自動新增一個叫做to_json的方法,返回其對應的JSON字元串。 事實上,要擴展這些已有類型,並不是標準庫或者Rails才有的複雜手法。你只需要「再次定義」那個類即可。比方說,Ruby里所有整數其實都是Fixnum類的實例,那麼我們可以: # 3.excited! Excited! 在其他語言裡面總是顯得和語言特性格格不入的反射功能在Ruby這裡也非常地自然: # 定義了名字分別為abcdefg的七個方法,輸出自己的名字 面向對象 Ruby裡面的「類型」不是什麼奇奇怪怪需要額外語言特性操作的東西,所有的class都是Class類的實例,包括它本身。所以有了類方法和實例方法的區別,所以創建對象不需要額外的new語法,SomeClass.new就行,這個new方法是從Object基類繼承下來的,語法具有高度的一致性。 # Class自己的類型也是Class # Class「是」一個Object,因為從Object繼承下來, # Object也「是」一個Class 同時Ruby也十分強調鴨子類型。 class Frog def wallace(person) 當然,畢竟受Perl影響很大,Ruby也有很多奇奇怪怪的一行寫法,可以參見這裡Ruby One-Liners。 說說Lua。 -- 有多小? 源碼包只有200多KB: 二進位包也只有200KB左右: 帶有豐富示例的完整語法描述,列印成A4紙不到20頁。(是的我打過~手動哭笑不得) Lua:小就是美,對不對,對不對嘛? -- 在解釋型語言中,Lua是性能很高的。在遊戲領域,Lua常常被拿來和JS做對比。 -- 很多語言對類提供了語言一級的支持,Lua並沒有這麼做。 這裡安利一下我以前寫的lua-oop: dingshukai/lua-oop -- 這麼神奇的數據結構,到底怎麼構造的? 簡單來說,Table是數組+哈希表。 下面是隨手寫的示例代碼: 看到前面有同學提到了PHP的Array。 看到前面有同學提到了PHP的Array。 這裡強調一下,Lua的Table和PHP的Array相比,表達能力差不多,但是性能更好,因為存儲結構不同。 Lua的Table,是·真·數組+·真·哈希,即以數組方式寫的元素放在數組裡,以key-value方式寫的元素,放在哈希表中。這個哈希表是無序的。 PHP的Array,官方文檔有提到: PHP的Array是有序關聯數組,針對不同的使用情況做了優化。因為是有序的,必然不如hash快。 PHP的Array是有序關聯數組,針對不同的使用情況做了優化。因為是有序的,必然不如hash快。 最後,貼一段我以前遊戲里的UI布局文件,模仿CSS,支持自定義控制項,屏幕自適應,開發UI效率奇高無比。整個都是以Table寫成的。 -- Lua中的注釋是以兩個減號開頭的。 -- Lua中的注釋是以兩個減號開頭的。 再說說Java。 /** Java的應用場景廣泛,行業多種多樣,各種平台都有。Java的使用人數也是最多的。 證據一: TIOBE語言排行榜: 在排行榜上,Java多年佔據第一。 證據二:IT培訓市場。 /** 剛才提到了培訓市場,也從側面印證了這一點。 /** * 造就了(曾經長期是)最先進的、最好用的IDE:Eclipse。 */ 大家可以回想一下,自己使用的IDE是什麼時候開始支持變數/函數重命名呢? 大家可以回想一下,自己使用的IDE是什麼時候開始支持變數/函數重命名呢? Eclipse整個是插件式的結構,我大概記得,零幾年曾經有一本書專門介紹了Eclipse插件開發,這本書還獲得了jolt award。但是現在我已經查不到這本書了。 有很多工具是基於eclipse開發的: 上圖中總有幾個工具你眼熟吧?ADT、Adobe Flex我都用過。 上圖中總有幾個工具你眼熟吧?ADT、Adobe Flex我都用過。 /** * 孕育了一批先進的軟體開發思想:測試驅動開發、極限編程、敏捷開發、重構等等。 */ 這些思想,都是起源於Java,或者最先大規模應用於Java。 測試驅動開發,是以Java作為例子的,JUnit是主流語言中使用最早最廣泛的單元測試框架。後面其他語言的各種Unit雨後春筍一樣冒出來。 那本講重構的書,就是以Java為例子的。 不得不說,當年這些概念十分火熱,催生了一大批佈道師、技術書籍、軟體諮詢公司。。。 岳雲鵬:word天哪,這麼多牛逼的書,都是講Java的嗎! 我的公眾號dingshukai888,教編程,歡迎關注~ http://weixin.qq.com/r/FDssNGXEljjprSFX924G (二維碼自動識別) 怎麼還沒人說Java? 寫過挺多語言的,Java、C、Js、Node、Python、Scala 可是寫的最多的還是Java。 看似這顯得很啰嗦,可是當你工作了你們好幾個人一起寫了快一年的代碼的時候,你會發現Java雖然啰嗦,但是語法簡單,嚴謹,這可以讓你們這個big project避免很多不必要的bug。特別是當突然出現了一個bug排查起來也是方便很多的。 2.上手簡單 3.第三方豐富 而相比node等語言,不是說其他語言不好,但是在搭建這麼大一個系統的時候選擇真的是比較有限的。自己的小程序還可以隨便寫點腳本簡單實現,但是公司的大項目呢?公司請你來是要你來解決問題而不是創造問題的。如果沒有完善的框架,依賴於一個框架然而那個框架出現了bug,那麼遭殃的可是整個公司啊。 4.資料多 5.垃圾回收 我喜歡把編程語言比喻成各種武器,例如Java是把沉重的大劍,Python是一把鋒利的匕首。我可以用大劍來砍殺一隻戰鬥力只有5的小兵,但是拿著卻那麼重,移動速度都降低了,而用匕首則可以快速解決簡單優雅。但是遇到大boss倘若沒有大劍,匕首當然可以只不過顯得更累。所以編程語言是不同的工具,沒有最好的,只有最合適的,具體情況具體分析。 --- 寫Java寫了一兩年了,發現這是個可靠的語言,寫著寫著雖然啰唆,但是有IDEA這樣神一樣的IDE啊!當然Java還有非常多的優點,這裡就不展開說了。對於Java還有其他語言特別是其他語言還有非常多不熟悉的地方了,如果上文有不正確的地方,請指正哈。 沒人說下kotlin?(後文簡稱kt) 很多人對kt沒有一個正確的定位,可能大家第一反應是拿它去和scala,groovy比較.從語法的角度而言,kt絕對不差(事實上比scala要略差一籌),但他們都是非常優秀的jvm語言,總體來說是難分伯仲的,kt也沒辦法將他們甩出一個身位.但我必須得說,絕大部分情況下(指常規開發),如果你選擇kt作為你的第二jvm語言,比用scala,groovy等,開發工程中的收益要多的多的多...(注意我不是單單在說語言層面了) 我簡單談談我自己的體會: 2.kotlin官網首頁重點強調的:100% interoperable with Java?. 這就決定了,從java切換到kt,相比其他語言,成本要少的多.比如我司有一套代碼生成器,當然,生成的是java代碼,那麼我用kotlin寫bussiness,其實不需要任何操心,repository拿過來就用,反之亦然,我用kotlin寫的lib,或者封裝了一些輔助方法的base類,java如果要用的話,同樣是拿過來即用,不像scala還需要做一層適配.事實上,java那邊根本感知不到其實他調用的是scala代碼. 3.基於2的優勢,kt推薦的是盡量少造輪子,時間處理還是用的joda,http還是Apache的HttpClient,也會糾結netty還是akka.大家回憶下自己學習語言的過程,是花在語法上的時間多,還是花在熟悉標準庫上的時間多?(python玩家這點應該感觸最深吧?),所以學習kt,真的就只有一個學習語法的成本.這點還會帶來一個優勢,就是kt項目,jar包的大小不會受多大的影響,這在android上更是一個大優勢. 4.最後,也是最重要的優勢:jetBrains爸爸全方位無死角超貼心的配套支持 說了這麼多文字,且廢話占多數,想必大家也有點煩了,那我下面就以java和kt的比較為切入點,介紹一些kt的特點吧. val 定義的變數不可變 默認參數,及參數名傳參: labmda寫法改進 nullable 擴展方法, 模式匹配,抄的scala 不多說 談一談我對語法糖的看法: 第二談談激進技術的風險和收益,與不同階層人員關注點的不同. 那時候我只是個開發 leader,這種層面肯定是沒有太大的決策權的,他們的想法我也理解,公司幾百號開發,提高一點點效率,相比引入新技術棧的風險,肯定是穩定壓倒一切啦. Idris 用haskell求周長為21,邊長為整數的所有三角形 結果如下: [(7,7,7),(6,7,8),(5,8,8),(6,6,9),(5,7,9),(4,8,9),(3,9,9),(5,6,10),(4,7,10),(3,8,10),(2,9,10),(1,10,10)] ※PHP7 出來之後,HHVM 還有什麼優勢嗎? TAG:JavaScript | Python | PHP | Nodejs | StringBuilder sb = new StringBuilder();
sb.append("&
(defmacro append-sb
(-&> sb (.append "1") (.append 2) (.append (str 3)))
if the :main element is not null.
(let [$ (gensym "$)
forms (reduce
(fn [fms v]
(cond
(and (map? v)
(contains? v :main))
(let [main (:main v)]
(keep-v fms :unless main :or
(let [mv (gensym)
when-cause (as-&> ["do] wc
(if (:prefix v) (conj wc (cons ".append (cons $ (list (:prefix v))))) wc)
(conj wc (cons ".append (cons $ (list mv))))
(if (:postfix v) (conj wc (cons ".append (cons $ (list (:postfix v))))) wc))]
(conj fms
`(let [~mv (str ~main)]
(if-not (clojure.string/blank? (str ~mv))
~(into "() (reverse when-cause))
~$))))))
:else (conj fms (cons ".append (cons $ (list v))))))
[]
appends)]
`(as-&> ~string-builder ~$ ~@forms ~$)))
(doto (StringBuilder.)
(append-sb "&

(defmacro keep-v
Usage: (keep origin :unless pred :or form)"
(if (not= :unless unless-key) (throw (IllegalArgumentException. "Missing unless cause.")))
(if (not= :or or-key) (throw (IllegalArgumentException. "Missing or cause")))
`(if ~pred
~form
~origin))
我就安利一個小眾語言F#吧:F#是C#的同門師兄弟,所以我就直接拿C#來比較。亮點有:1、let,let 的語義是符號綁定一個值,而不是聲明一個不可變變數,雖然實際上往往是通過後者實現,但是因為這個語義,我們思維上可以簡化編程的邏輯。如: let (a, b) = (b, a)
let Subtract a b =
let (a, b) = (b, a)
a - b
public static int Subtract(int a, int b)
{
Tuple&
int b2 = expr_07.Item2;
return expr_07.Item1 - b2;
}
public static int Subtract(int a, int b)
{
return b - a;
}
static Func&
{
return x =&> f(Fix(f))(x);
}
{
return (x, y) =&> f(Fix(f))(x, y);
}
我們換成F#怎樣呢? let rec Y f x = f (Y f) x
let ulock lockObj =
let mutable token = false
try
Monitor.Enter (lockObj, token)
{ new IDisposable with
member __.Dispose() = Monitor.Exit lockObj }
with _ -&>
if token then Monitor.Exit lockObj
reraise()
use __ = ulock xx
。。。。
let inline getLongID (node: ^t) = (^t:(member LongID: string) node)

&>&>&> print("Hello, World!")
Hello, World!
&>&>&> class C:
... def __init__(self):
... self.data = "Some data"
...
&>&>&> obj = C()
&>&>&> obj.extra_data = "Some extra data"
&>&>&> print(obj.extra_data)
Some extra data
&>&>&> 2**1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376
&>&>&> from decimal import Decimal, getcontext
&>&>&> getcontext().prec = 100
&>&>&> Decimal(2)**Decimal(0.5)
Decimal("1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573")
&>&>&> para = [int(input("para[%d]: " % i)) for i in range(4)]
para[0]: 1234
para[1]: 4321
para[2]: 1000
para[3]: 0
&>&>&> print(para)
[1234, 4321, 1000, 0]
&>&>&> words = ["Apple", "Bob", "Alice", "Python"]
&>&>&> print([word for word in words if word[0] == "A"])
["Apple", "Alice"]
在
處的值:
&>&>&> y = list(map(lambda x: (x + 1)*(x - 2), range(1, 11)))
&>&>&> print(y)
[-2, 0, 4, 10, 18, 28, 40, 54, 70, 88]
try:
print("Data: ", a.data[key])
except AttributeError:
print("Data not found!")
except KeyError:
print("Data exists, but not data with key "%d"." % key)
if not hasattr(a, "data"):
...
elif key not in a.data.keys():
...
else:
...
&>&>&> class Block:
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def __repr__(self):
... return "%s at point (%d, %d)" % (type(self).__name__, self.x, self.y)
...
&>&>&> class Man(Block):
... def move(self):
... self.x += 10
...
&>&>&> class Car(Block):
... def move(self):
... self.y += 100
...
&>&>&> objects = [Man(30,6), Car(10, 20), Car(-20,0)]
&>&>&> print(objects)
[Man at point (30, 6), Car at point (10, 20), Car at point (-20, 0)]
&>&>&> [obj.move() for obj in objects]
[None, None, None]
&>&>&> print(objects)
[Man at point (40, 6), Car at point (10, 120), Car at point (-20, 100)]
&>&>&> import pickle
&>&>&> print(string)
b"x80x03]qx00(c__main__
Man
qx01)x81qx02}qx03(Xx01x00x00x00yqx04Kx06Xx01x00x00x00xqx05K(ubc__main__
Car
qx06)x81qx07}qx08(hx04Kxhx05K
ubhx06)x81q }q
(hx04Kdhx05Jxecxffxffxffube."
&>&>&> objects2
[Man at point (40, 6), Car at point (10, 120), Car at point (-20, 100)]
$ perl -E "say foreach 1..7"
1
2
3
4
5
6
7
$ perl -E "say rand foreach 1..10"
$ perl -F " " -E "say $F[1]" &$ perl -F " " -E "say join " ", $F[0],$F[1],$F[0]+$F[1]" &$ perl -E "print if /^deep_dark/" &# 假設列表裡每個元素都是一個哈希引用,裡面有foo和bar兩個值,前者是數字,後者是字元串。
# 我想先按foo排,後按bar排:
my @result_list = sort {
$a-&>{foo} &<=&> $b-&>{foo} or $a-&>{bar} cmp $b-&>{bar}
} @input_list;
my %wanted1;
my %wanted2;
my @result_list = grep {
exists $wanted1{$_} and exists $wanted2{$_}
} @input_list;
my @result_list = map {$_, $_+1, $_+2} @input_list;
open my $handle, "&<", "input_file" or die "failed to open input_file because $!";
$content =~ /some_(regular)_expression/ or die "failed to parse $content";
my $wanted_part = $1; # 你在這裡得到"regular"
其實每個語言都有很多有意思的地方,關鍵看自己會不會玩。比如:Python:Y = lambda fn: (lambda f: f(f))(lambda f: fn(lambda s: f(f)(s)))
lambda s:
s if len(s)==0
else g(s[:len(s)-1]) + s[len(s)-1].upper())("abcdefghijklmnopqrstuvwxyz.")
const Y = function(fn) {
return (function (f) {
return f(f);
} (function (f) {
return fn(function (s) {
return f(f)(s);
});
}));
}
return function(s) {
let n = s.length;
if (n === 0) {
return s;
}
return g(s.substring(0, n-1)) + s.substring(n-1).toUpperCase();
}
})("abcdefghijklmnopqrstuvwxyz.")
(define Y
(lambda (fn)
((lambda (f)
(f f)) (lambda (f)
(fn (lambda (s) ((f f) s)))))))
(lambda (s)
(cond
((null? s) "())
(else
(cons (string-upcase (car s)) (g (cdr s)))))))) "("a" "b" "c" "d" "e"))
要論亮點,那就不得不提 Scala。
class Foo(val bar: Int, val baz: String){}
val foo: String = _
List(1, 2, 3).reduce(_ + _)
如果傳入的函數只有需要一個參數的話,下劃線也省了吧,比如
List(1,2,3).foreach(println)
import com.oo.xx.{ Foo =&> Bar , _ }
foo match {
case Some(_) =&> true
case _ =&> false
}
trait Functor[C[_]]{
def op[P, Q](x: C[P])(f: P =&> Q): C[Q]
}

WriteLine(1.ToJson());
WriteLine(null.ToJson());
WriteLine(new JArray
{
new { elder = 1926, qinding = false },
"Excited!"
}.ToJson());
using static System.Linq.Enumerable;//放前面是為了下一行簡潔
var para = Range(0, 4).Select(i =&> int.Parse(Input($"para{i}: "))).ToArray();
string Input(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
words.Where(word =&> word[0] == "A");
var y = Range(1, 10).Select(x =&> (x + 1)*(x - 2)).ToList();
try
{
...
}
catch (AException e)
{
WriteLine(e.Message);
}
catch (BException e)
{
WriteLine(e.StatusNumber);
}
var s = new XmlSerializer();//各種Serializer都可以
var str = s.Serialize(objects);
var objects2 = s.Deserialize(str) as List&
ditto = 0
ditto = "diiiiiiiiitto"
ditto = []
ditto = {}
a, b = b, a

Ruby.這個語言沒有那麼大眾化。相比於為Web而生的PHP,瀏覽器欽定的JavaScript和準備在計算機入門語言及科研人士的編程語言中越走越遠的Python相比,一個大學裡的學妹問學長在課程之外應該學學什麼東西提高自己的知識水平,學長有極大概率不會推薦Ruby。所以反過來,能給你推薦Ruby的人靠譜的概率會大一點……好吧扯遠了。相信大部分人接觸Ruby的時候都是因為Ruby on Rails這個閃亮的框架。無人能說清楚到底是Ruby成就了Rails還是Rails成就了Ruby。不過Rails之父DHH一開始選擇用Ruby而放棄了PHP用於公司業務的時候,一定是被植根於這個語言深處的某些設計思想打動了。儘管Rails到現在有無數人批評它過時老舊、龜速臃腫,和其他語言的Web框架好像也沒有什麼代差。不過須知Ruby on Rails開源發布的時候是2004年,想一想那個時代的Web技術吧。某種程度上,Ruby是Smalltalk、Perl和Lisp的混合體,分別象徵著面向對象、腳本語言和函數式的思想。編程語言的歷史問題可不能說太多,容易讓人摸不著頭腦。像我幾年以前看過一本Go的書,一開始講了很長一段的Plan9,彷彿在講述編程語言界的艾澤拉斯。Ruby並不像某些語言一樣吸取無數語法然後雜糅在一起。相反,這些特性在Ruby里體現得非常自然,也許同松本行弘受C++影響比較深有關。every :day, :at =&> "12:20am", :roles =&> [:app] do
rake "app_server:task"
end
3.hours.ago
require "json"
puts 1.to_json
puts nil.to_json
puts [{elder: 1926, qinding: false}, "Excited!"].to_json
class Fixnum
def excited!
self.times do
puts "+1s"
end
end
end
# +1s
# +1s
# +1s
puts 1.class # class即是這個對象的類型
"abcdefg".each_char do |c|
define_method :c do
puts c
end
end
# String類型本身也有類型,就是Class
puts String.class
# =&> Class
puts Class.class
# =&> Class
puts Class.ancestors
# =&> [Class, Module, Object, Kernel, BasicObject]
puts Object.class
# =&> Class
class Dictator
def walk; end
def swim; end
def call; end
end
def walk; end
def swim; end
def call; end
end
right = [:walk, :swim, :call].all? do |action|
person.respond_to? action
end
if right
puts "ha"
else
puts "gua"
end
end

作為一個遊戲開發者,我對Lua非常熟悉。
Lua有很多亮點,下面這幾個,最值得說一說。
Lua非常「小」。
--

Lua性能高。
--
JS比Lua的好處是,瀏覽器也可以運行。
但是Lua比JS的好處是,性能高,而且語言簡單,容易上手。
LuaJIT還可以執行編譯後的Lua位元組碼,速度更快。
參見這裡的討論: Cocos2dx+lua合適還是Cocos2dx+js合適? - JavaScript - 知乎
Lua不限制編程範式。
--
Lua是基於原型的語言(prototype-based language)。在Lua里非常容易地模擬面向對象。
一個面向對象編程的lua框架,支持多繼承,性能棒棒噠,在多個線上遊戲中使用。
我本人非常喜歡基於組件的編程,這個說起來又是一個長篇(贊多的話可以考慮寫一寫。。。),我在lua中實現組件編程的方式就是多繼承。非常好用!
另外,在寫這個框架之前,我使用過lua官方網站推薦的幾個面向對象庫,基本都是玩具級的,在複雜使用環境下,性能瓶頸大。
所以,就寫了這麼一個框架,遊戲開發狗可以試試。
Table是Lua里最基本的、最常用的、幾乎唯一的、幾乎萬能的數據結構。
--



* 使用最廣泛的語言。
*/

IT培訓中業務最多的就是Java,就業起步工資雖然不高,但是工作機會多啊,什麼移動開發、網站開發、企業軟體開發、遊戲開發等等,都有java的用武之地。
光Java培訓就可以容納幾個上市公司,我說的還是中國。
英語作為一門人類語言,其培訓市場非常大,而Java作為一門計算機語言,如果只看培訓的話,可以與英語匹敵了。。。(大霧)
* 最容易學的全能語言。
*/
下面說的這些話,就很主觀了。
如果只考慮全能語言,Java即使不是最好學的,也是最好學的之一。
很多語言也很好學,比如Lua,但是應用領域相對較窄。
Bash是不是很簡單?但是對初學者非常不友好,可能少一個空格就跑不起來。
Python呢?從網上複製粘貼的代碼,居然不能直接用!不友好!(格式丟失造成的。。)
C和C++就不用說了,初學者可能連編譯錯誤和鏈接錯誤都分不清。
當然PHP、JS這些都是相對容易入門的,但是其應用領域都不如Java廣泛。
Java:比我好學的沒有我應用廣泛,比我應用廣泛的沒有我好學。完美的平衡!
詳細的時間很難去考證了,記憶中至少在2004年的eclipse是支持這一功能的。
再想想,游標移動到一個名字上,這個名字所有出現的位置都高亮,你的IDE是什麼時候支持的?
其他還有:查看一個名字的所有出現地方(Find References in...)、跳轉到定義、快速打開類、查看類結構、提取函數等等。
在十多年後的今天,vs2015的C++編輯器,這些功能有了一部分,但是有時候很卡、或者不全。
也許是因為C++太複雜不好實現這些功能,但是情況就是這樣,用Eclipse開發起來非常快。
如果只說Eclipse可能有同學不服:IDEA比Eclipse好用多了!
嗯,IDEA也是非常好用的,Eclipse甚至向IDEA以及早期的JBuilder借鑒了很多東西。
但是Eclipse還有一個很厲害的地方:Eclipse是一個平台。
而這只是以字母A開頭的基於Eclipse的工具,全部列下來會非常長的。。。



int a = 10;
String b = "hello world";
var a = 10;
var b = "hello world";










MATLAB, 用過了很多科學計算相關的語言, 比如R, Python, Mathematica等, 感覺還是MATLAB最適合我。亮點一: 功能一站式。 比如R, Python, 不同功能要用不同的包, 這些包的作者不同, 代碼質量參差不齊, 風格各異, 學習成本較高, 還要自己識別優劣。亮點二: 大部分函數是開源的, 可以根據自己需要改寫或加速,這點Mathematica做不到。亮點三: IDE強大,針對數據分析的。亮點四: 矩陣運算語法簡潔,速度快。可以很方便從數學公式轉化為代碼。亮點五: 大部分代碼可以自動轉化為C/C++其他語言沒有包含所有三個亮點的。
[(a,b,c) | c&<-[1..10], b&<-[1..c], a&<-[1..b], a&
推薦閱讀:
※swoole啟動2萬個定時器對性能有影響嗎?
※現在國內中小型的IT行業的公司,asp.net和php哪個應用得比較普遍?
※如何編寫不可維護的php代碼?
※「Facebook 開發的高性能PHP虛擬機 HHVM 比官方的 PHP解釋器 快超過9倍」的說法是否屬實?
