計算機語言設計:列表的理解以及為什麼它是有害的

發表於2016-02-11

本文將列舉不同程式語言的幾個小例子,嘗試解釋什麼是列表解析。並且試圖宣揚我的觀點:“列表解析”不論從概念上還是技術上,完全是函數語言程式設計的一根闌尾——多餘而且某種程度上還是有害的。

何為列表解析?

以python中的列表解析為例:

它產生一個0到8的列表,將奇數從該列表中移除,最後將剩餘元素乘2,最後返回所得列表。

Python的列表解析(LC)的語法如下:

總而言之,這種特殊語法產生一個列表,並且允許程式設計師對其中元素進行過濾,以及將其中元素作為引數傳給一個函式,但是所有這些都已“表示式”的形式出現。

(譯者注:這個語法本身也是一個“表示式”,所以可以巢狀使用。)

 

列表解析的函式式的寫法是這樣的:

其他語言的列表解析是相似的。這兒有幾個來自維基百科的例子。在下面的例子裡,x^2>3作為條件,然後把每個元素乘以2返回結果.

Haskell

F#

OCaml

Clojure

Common Lisp

Erlang

Scala

這裡是維基百科對
List comprehension
的解釋,引用如下:

A list comprehension is a 
syntactic construct
 available in some programming languages for creating a list based on existing lists.

列表理解(LC)有以下特徵:

*1.一個直接的列表生成器,可以對元素進行過濾,並且對每個元素應用一個函式

*2.是某些語言裡面的特殊語法

*3.這種語法是一個單獨的表示式,而不是由單獨的函式組成

為什麼列表理解是有害的?

  • 列表理解就像一個不透明的俚語一樣,它妨礙溝通,造成誤會
  • 列表理解是程式設計裡面一個冗餘的概念。它只是一個簡單的列表生成器。他可以被簡單的功能函式formmap(func,filter(list,predicate))代替,或者被一些語句代替,比如perl:for (0..9) { if ( ($_ % 2) == 0) {push @result, $_*2 }}.
  • 這種存在於多種語言中的特殊語法,其實不是必要的。如果需要這樣的函式,那麼它可以直接是一個一般的函式,比如LC(function,list,predicate).

列表解析語法並不是很有必要。一個更好更一致的方法是用函式式語言的精髓,使用普通函式的組合。

這是python的語法
:

在Mathematica中,可以這樣寫

在Mathematica中,算術操作符可以不使用Map而直接對映到列表,因此上面的程式碼可以這樣寫:

還可以寫成線性字首風格:

或者線性字尾風格:

在上面,我們就像Unix裡的管道那樣排列函式在一起。我們從9開始,使用“Range”來獲取一個1到9的列表,然後使用一個函式來過濾出偶數,接著我們使用一個函式來把過濾出來的每個數字乘以2。符號“//”是一個字尾符號,類似於bash(shell)的“|”符號,同時,“@”是一個與“|”相反的符號。

(☛ Short Intro of Mathematica For Lisp Programers)

無需特殊語法的列表理解函式

在函式式語言中,假如我們想要“列表解析”這一特性。通常地,預設情況這可以這樣做

但這種用法會很頻繁,我們想為此建立一個更方便的函式。作為一個獨立的函式,它更容易被編譯器優化。因此,我們可以建立一個函式LC像這樣:

這個關係到一種語言是否應該建立一個更方便的新函式,否則就需要3個函式的組合。Common Lisp和Scheme Lisp是極端對立的典型例子。

注意,這裡沒涉及到新的語法。

假設,某人對下面的有爭論:

實際上, 這個語法:

遠比這個語法方便:

 

那麼這個:

與以下的比較

 

這函式式格式:

  • 更短小
  • 沒有其他特別的新語法

 

建立一個新函式的問題和決策

假設我們決定通過過濾器生成列表,這個操作很頻繁,是值得建立的一個函式。

現在,在函式式語言中,一般的設計原則是你應該減少函式的數目,除非你真的需要。因為在你的語言裡相關函式的任何組合列表可能成為一個新的函式。因此,如果我們確實認為列表解析是有用的,我們可能會
推廣到最大限度地發揮一個函式的作用。

舉個例子,我們會考慮是不是值得增加第四個引數來讓使用者指定只返回前n個元。如

那麼再將它分割成m個子列表呢?

要是再將它分割成更廣泛通用的呢?即弄成m、m1、m2等等。

還有排序?也許你用到列表的時候這些功能總是需要一起使用的。

當我們需要將一個函式放進列表中,有時候我們會想對映到每個分支。(就像Common Lisp中的map),所以我們可能還需要有其它的可選引數,例如:

如果…

所以,在預設情況下,通過一個或多個函式的序列,上面這些或這些的組合將被程式語言處理(即組成)。 但當我們建立一個新的函式時,我們應當仔細權衡它是否必要,要知道,若不這樣的話,語言將成為裝滿了非比要的,混亂的函式的袋子。

因此現在的問題是,難道通過列表解析生成一個列表的確是一個非常頻繁使用的動作嗎?如果是的話,那為什麼我們要去建立一種特殊的語法,諸如[expr for var in list if P]這樣,而不是使用一個函式LC(func,list,P)呢?

同時注意一下,上面列出的函式LC並非一個強大到可以生產任意巢狀列表的函式。如果大家想了解一下更強大列表生成函式,那這裡有一個例子,它可以生成任意巢狀的樹狀結構列表,參見 Mathematica 的 Table函式:

 

列表解析,特定語法,命令式語言

列表解析真正的“優勢”在於其特殊的語法,及由命令式語言所組成的特定語法。這是因為在命令式語言中,每個結構都有可能有一大堆的特定語法和關鍵字。這非常常見,比如:i++,++i,for(;;){},while(){},0x123,expr1 ? expr2 : expr3,sprint(…%s…,…), ….

對於那些發現命令式語言語法的好處的人來說,由於“列表解析”為語言新增了額外的獨特語法,它也許也是有益處的。特定語法和冗長的關鍵字可以幫助人們理解程式碼的含義。舉個例子: 在語法[… for … in …]中,程式設計師可以通過方括號來得知其值為一個列表(list),並且關鍵字for和in幫助程式設計師瞭解該結構的部分用途。如果使用函式式語法LC(…, …, …),就沒有這些提示了,則程式設計師必須理解函式的引數才行。

錯誤術語以及如何判定

有人這樣寫道:

術語“列表解析”是直觀的,它是基於數學符號。

術語“列表解析”是含糊不清的。它阻礙了交流並且增加了誤會。更好的名字是“列表生成器”。

你憑什麼說“列表解析”是直觀的?有沒有任何統計、調查、研究和參考?

要將它放在特定的語境中,你能說“ lambda”也是直觀的嗎?“let”是直觀的?“for”是直觀的?“when”是直觀的?我的意思是說,給你一些常用的計算機術語來評估,然後告訴我們哪些是好的,哪些是壞的。所以我們要在特定環境下評估你的專業術語。
例如,我們想知道,以你的觀點這些術語好嗎:
currying (偏函式),
lisp1 lisp2
tail recursion (尾遞迴),
closure (閉包),subroutine(子程式),command(命令),
object (物件)。或者,也許是讓你論述一下“module”、“package”、“add-on”和“library”相比較下各自的優點和意義。我想要了解你對此的觀點的話,至少 每項都得 要有幾段分析吧。如果你敘述或寫了關於此話題千字以上的文章,那麼我們 所有人就 都可以 對你在這方面的熟悉和了解程度 做下評估了。

同樣,“直觀”並不是考慮一個術語是好是壞的唯一方面。舉例來說,emacs中所使用的術語“frame”。它非常直觀,因為frame是一個普遍的英語單詞,任何人都認識。我們有門框、窗框、圖框中的“frame”,都與電腦中emacs中的“frame”意思相近。無論如何,由於歷史原因,通常在電腦軟體中我們稱之為“window”,並且碰巧術語“window”同樣在emacs中有技術含義,我們今天稱它“split window”或“frame”。所以,在emacs中術語“frame”和“window”的概念是混淆的,因為emacs的“frame”就是我們叫“window”的東西,同樣emacs中的“window”被我們稱作frame。這個例子告訴我們,即便當一個術語非常直觀,它可能仍然是糟糕的。

作為另一個例子,目標人群對被使用術語的通常理解,也是一個重要的方面。舉例來說,術語“lambda”,它是一個希臘字母,並不能很好地傳達它所起的作用。這個單詞本身的意義並不會使人聯想到函式的概念。字母“λ”偶然地被一位邏輯學家在他的名為“lambda演算”的研究中當作速記標記來使用(其中“演算”部分作為系統科學在17世紀的基本術語,特別是有關於機器推理方面)。無論如何,術語“lambda”以這種方式使用在電腦科學與程式設計中已經很久並且非常廣泛,近期歷史大約有50年(如果我們溯本遂源會更久)。所以,由於已經被使用,實際上它已經成了普遍用法,這樣它便減弱我們認為它是一個壞術語的程度。即便如此,請記住那僅僅因為一個術語已經被使用,但要是術語本身在其它方面非常糟的話,它可能仍需要被改變。由於這個原因,這些術語對新生代而言會導致學習曲線問題。

看到了吧,當你判斷一個術語的時候,你不得不考慮許多方面。這是相當棘手。當你判斷這些”行話”的時候,你就會有這樣的疑問:

• 這個術語是否準確傳達了它的本意?(即作為一個單詞是否有效地促進交流)

• 社會上的其他人都理解這個術語嗎? (比如 更科學的來講:佔多大比例?)

 

以上每個簡單的問題都牽涉到很複雜的東西。比如,它需要:

  • 語言專家(很多相關子領域:語用學、語言歷史、詞源學)在這些領域的專家可以恰當地給我們一些線索,從而判斷一個新術語在語言理論、實踐、歷史方面是否合適。
  • 該領域的實踐經驗(程式設計或電腦科學)。在工業環境中、從他們日常讀寫文件的經驗、在他們與其它程式設計師的交流中,使計算機程式設計師具備專業知識以便可以告訴我們哪個術語是好,哪個是壞。
  • 學術專家(例如教育者,教授,程式設計書籍作者/老師)。老師,以一個傳授知識給學生的角度告訴告訴我們一個術語的用處。舉例來說,老師可以告訴我們哪些術語經常讓學生感到困惑。
  • 科學調查,社交科學。從理論或實踐的科學研究,能以資料或其它科學方式告訴我們一個術語質量的好壞。

此外,您可能不知道,其實有一些專業科學家專門製造專業術語“O認為這是很好的,因為對我來說直觀的

致謝

感謝 w_a_x_man 提供一下這些 ruby 程式碼:

注意,這是沒有列表理解因為它不使用特殊的語法但它符合Ruby的風格

相關文章