6 個新奇的編程方式,改變你對編碼的認知

LavonLKAQ 6年前發布 | 42K 次閱讀 軟件開發

我時不時會發現一種編程語言的不同用法它有時候會改變我對編程的看法啊。這篇文章中,我想分享一下讓我驚訝的發現。這不是類似于高呼“函數式編程會改變世界!”博客文章。我敢打賭,大多數讀者都沒有聽說過下面的大多數語言和范例,所以你應該也會被這些新概念吸引。

注意:我對以下大多數語言的使用經驗都很少,但是我發現他們背后的想法非常吸引人,但對其沒有專業知識,所以有任何錯誤請指出并指導更正。如果您也有新的范例和想法,歡迎分享。

默認并發

示例語言: ANIPlaid

讓我們用一個哲學家的思想來解決問題吧:有些編程語言是默認情況下并發的,也就是說,每行代碼都是并行執行的。

例如,假設你寫了三行代碼,A,B和C:

A;
B;
C;

在大多數編程語言中,A先執行,然后執行B,最后執行C。在像 ANI 這樣的語言中,A,B和C都將同時執行。

ANI 中代碼行之間的控制流或排序,僅僅是代碼行之間顯式依賴關系的副作用。例如,如果B引用了A中定義的變量,則A和C將同時執行,而B只會在A完成后執行。

以下是ANI中的“Hello World”示例:

"Hello, World!" ->std.out

在ANI術語中,我們將 "Hello, World!" 對象(字符串)發送到 std.out 流。如果我們發送另一個字符串,會發生  std.out 什么

"Hello, World!" ->std.out
"Goodbye, World!" ->std.out

這兩行代碼并行執行,因此它們可以在控制臺中以任何順序結束。現在,看看當我們在一行上引入一個變量并參考它會發生什么:

s = [string\];
"Hello, World!" ->s;
\s ->std.out;

第一行聲明一個“鎖存(latch)”(鎖存器有點像變量),調用 s 它包含一個字符串; 第二行將文本賦值  "Hello, World!" 給 s ; 第三行“解鎖”  s 并將內容發送給 std.out 。在這里,您可以看到ANI的隱式程序排序:由于每行都依賴于前一行,因此此代碼將按寫入的順序執行。

Plaid 語言還聲稱通過默認支持并發性,但使用權限模型,如在本文中,設置控制流程。多核技術正在興起,并發性仍然是大多數語言中難點。ANI 和 Plaid 提供了一個新的解決方案,可以帶來驚人的性能提升;,問題在于“默認并行”是否會改變開發的狀態。有關更多信息,請參閱 并發性并行性

相關類型

示例語言: Idris ,  Agda ,  Coq

你可能習慣使用C和Java等語言來鍵入系統,編譯器可以檢查變量是整數,列表還是字符串。但是如果你的編譯器能檢查一個變量是“一個正整數”,“一個長度為2的列表”還是“一個回文字符串”呢?

下面是如何聲明一個 Vector 包含無形庫1,2,3的值:

val l1 = 1 :#: 2 :#: 3 :#: VNil

這將創建一個變量 l1 ,它的類型簽名不僅指定它 Vector 是包含的 Ints ,而且指定它的 Vector 長度為3.編譯器可以使用此信息來捕獲錯誤。讓我們使用該 vAdd 方法在 Vector 兩個之間執行成對加法 Vectors :

val l1 = 1 :#: 2 :#: 3 :#: VNil
val l2 = 1 :#: 2 :#: 3 :#: VNil

val l3 = l1 vAdd l2

// Result: l3 = 2 :#: 4 :#: 6 :#: VNil

上面的例子工作正常,因為類型系統知道兩者 Vectors 都有長度3.但是,如果我們嘗試了 vAdd 兩種 Vectors 不同的長度,我們沒等到運行時就會在編譯時得到一個錯誤。

val l1 = 1 :#: 2 :#: 3 :#: VNil
val l2 = 1 :#: 2 :#: VNil

val l3 = l1 vAdd l2

// Result: a *compile* error because you can't pairwise add vectors 
// of different lengths!

Shapeless 是一個仍然有點粗糙的庫,只支持依賴類型的一個子集,并有相當冗長的代碼和類型簽名。相反, Idris 使得類型成為編程語言的第一類成員,因此依賴類型系統似乎更加強大和干凈。為了進行比較,請查看 Scala vs Idris:相關類型,現在以及未來的 討論。

連貫語言

示例語言: Forth ,  catjoy

有沒有想過不用變量和函數應用程序編程會是什么樣子?至少我沒有,但顯然有些人這么想了,他們提出了 連續編程 。這個想法是,語言中的所有內容都是將數據推送到堆棧或從堆棧中彈出數據的函數; 程序幾乎完全通過功能組合(串聯組合)來構建。

這聽起來很抽象,所以我們來看看 cat 中的一個簡單例子 :

2 3 +

在這里,我們將兩個數字推入堆棧,然后調用該 + 函數,將兩個數字從堆棧中彈出,并將其添加到堆棧中的結果:代碼的輸出為5。下面是一個稍微有趣的示例:

def foo {
  10 <
  [ 0 ]
  [ 42 ]
  if
}

20
foo

讓我們一行一行地瀏覽一下:

  1. 首先,我們聲明一個函數 foo 。請注意,cat中的函數沒有指定輸入參數:所有參數都從堆棧中隱式讀取。

  2. foo 調用 < 函數,該函數彈出的第一項在堆棧中,將它與10,并且推動任一 True 或  False 背面壓入堆棧。

  3. 接下來,我們將值0和42輸入堆棧:我們將它們包括在括號中以確保它們未被執行就推入堆棧。這是因為它們將分別用于調用 if 下一行函數的“then”和“else”分支。

  4. 該 if 函數從堆棧中彈出3個項目:布爾條件,“then”分支和“else”分支。根據布爾條件的值,它會將“then”或“else”分支的結果輸回堆棧。

  5. 最后,我們將20輸入堆棧并調用 foo 函數。

  6. 當所有的事情都做完后,會得到一個42的結果

這種編程風格頗有趣味:程序可以以無數種方式拆分和連接以創建新程序; 非常簡單的語法(甚至比LISP更簡單),導致非常簡潔的程序; 也具有強大的元編程支持。看起來你必須記住或想象堆棧的當前狀態,而不是能夠從代碼中的變量名稱中讀取它,這可能使得很難推斷代碼。

聲明式編程

示例語言: Prolog ,  SQL

聲明式編程 已經存在了很多年,但大多數程序員仍然不知道這個概念。這里的要點是:在大多數主流語言中,你需要描述如何解決一個特定的問題; 在聲明性語言中,你只需描述你想要的結果,而語言本身就能找出到達那里的方法。

例如,如果您在C中從頭開始編寫排序算法,例如編寫合并排序的指令,該指令逐步描述如何遞歸地將數據集分成一半并按排序順序合并到一起。如果您使用像 Prolog 這樣的聲明性語言對數字進行排序 ,則應該描述所需的輸出:“我需要相同的值列表,但索引中的每個項目  i 應小于或等于索引處的項目 i + 1 ”。將以前的C解決方案與此Prolog代碼進行比較:

sort_list(Input, Output) :-
  permutation(Input, Output),
  check_order(Output).

check_order([]).
check_order([Head]).
check_order([First, Second | Tail]) :-
  First =< Second,
  check_order([Second | Tail]).

如果你使用過SQL,那么你已經完成了一種聲明式編程,你可能沒有意識到這一點:當你發出查詢時 select X from Y where Z ,你正在描述你想要返回的數據集;它是真正計算出如何執行查詢的數據庫引擎。

聲明性語言的美妙之處在于它允許你在更高層次的抽象中工作:你只需要描述所需輸出的規范。例如, prolog中 簡單 數獨求解器 的代碼,只是列出了解決的數獨謎題的每行,每列和對角線應該是什么樣的:

sudoku(Puzzle, Solution) :-
  Solution = Puzzle,

  Puzzle = [S11, S12, S13, S14,
            S21, S22, S23, S24,
            S31, S32, S33, S34,
            S41, S42, S43, S44],

  fd_domain(Solution, 1, 4),

  Row1 = [S11, S12, S13, S14],
  Row2 = [S21, S22, S23, S24],
  Row3 = [S31, S32, S33, S34],
  Row4 = [S41, S42, S43, S44],      

  Col1 = [S11, S21, S31, S41],
  Col2 = [S12, S22, S32, S42],
  Col3 = [S13, S23, S33, S43],
  Col4 = [S14, S24, S34, S44],      

  Square1 = [S11, S12, S21, S22],
  Square2 = [S13, S14, S23, S24],
  Square3 = [S31, S32, S41, S42],
  Square4 = [S33, S34, S43, S44],      

  valid([Row1, Row2, Row3, Row4,
         Col1, Col2, Col3, Col4,
         Square1, Square2, Square3, Square4]).

valid([]).
valid([Head | Tail]) :- fd_all_different(Head), valid(Tail).

以下是數獨解算器的運行結果:

| ?- sudoku([_, _, 2, 3,
             _, _, _, _,
             _, _, _, _,
             3, 4, _, _],
             Solution).


S = [4,1,2,3,2,3,4,1,1,2,3,4,3,4,1,2]

不幸的是,聲明式編程語言很容易造成性能瓶頸。上面的排序算法很可能 O(n!) 讓數獨解算器進行了一次強力搜索; 而且大多數開發人員必須提供數據庫提示和額外索引,避免執行SQL查詢時出現代價高昂且效率低下的情況。

符號編程

示例語言: Aurora

Aurora 語言是一個典型的 符號編程 的例子:它不僅包括純文本編程,而且還包括圖像,數學方程,圖形,圖表等。這能夠用該數據的原始格式操作和描述各種數據,而不是用文本描述所有數據。Aurora也是完全互動的,可以立即顯示每行代碼的結果,例如 REPL。

Aurora語言由Chris Granger創建,他也創建了Light Table IDE。Chris在他的文章中概述了Aurora的動機:實現更好的編程。目標是使編程更加具有可觀察性,直接并減少偶然的復雜性。欲了解更多信息,請務必查看Bret Victor令人難以置信的會談: Inventing on PrincipleMedia for Thinking the Unthinkable , and  Learnable Programming

基于知識的編程

示例: Wolfram語言

像上面提到的 Aurora 語言一樣,Wolfram語言也是基于符號編程的。但是,符號層僅僅是為Wolfram語言的核心提供一致的接口的一種方式,這是基于知識的編程:內置于大量的庫,算法和數據。這使得從繪制非死book連接到操縱圖像,查看天氣,處理自然語言查詢,繪制地圖上的方向,求解數學方程式等等都可以輕松地完成。

我懷疑 Wolfram 語言是否有最大的“標準庫”和有任何語言的數據集。互聯網的連接是編寫代碼的內在組成部分的想法讓我感到興奮:它幾乎就像一個自動完成功能進行谷歌搜索的IDE。符號編程模型是否像Wolfram聲稱的那樣靈活,而且可以真正利用所有這些數據?如果是的話,會很有趣。

更新:雖然Wolfram聲稱Wolfram語言支持“符號編程”和“知識編程”,但這些術語的定義略有不同。更多有關信息,請參閱 知識編程符號編程 wiki。

來自: reddit

 

 本文由用戶 LavonLKAQ 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!