欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Haskell入門(mén) - Delphi,Linux,Python -

這篇文檔是我前段時(shí)間在瞎搞搞Haskell的時(shí)候,從這里找到,為了方便自己看,我把它整理成了一篇文檔,現在貼上來(lái),方便剛學(xué)習Haskell的初學(xué)者。因為,原文看起來(lái)實(shí)在是麻煩。
——————————————————————————————

作者:David Mertz 是一名作家、程序員和教師

作者聯(lián)系方式:mertz@gnosis.cx

正文開(kāi)始:

我適合此教程嗎?

本教程的對象是那些要了解用 Haskell 語(yǔ)言進(jìn)行函數型編程的命令式語(yǔ)言程序員。如果您曾用 C、Pascal、Fortran、C++、Java、Cobol、Ada、Perl、TCL、REXX、JavaScript、Visual Basic 或其它許多語(yǔ)言編過(guò)程序,那么,您就一直在使用命令式范例。本教程用平和的語(yǔ)言介紹了函數型編程的范例,并用 Haskell 98 語(yǔ)言進(jìn)行特定說(shuō)明。
具有函數型編程背景的程序員可能會(huì )覺(jué)得本教程的進(jìn)度有些緩慢;但是,特別對于那些從未使用過(guò)
Haskell 98 的程序員來(lái)說(shuō),仍能通過(guò)瀏覽本教程對這一語(yǔ)言有個(gè)大致了解。

未涉及哪些內容?

在介紹性教程中,無(wú)法涉及 Haskell 的許多強大和復雜的特征。尤其是,類(lèi)型類(lèi)和代數類(lèi)型(包括抽象數據類(lèi)型)的整個(gè)范圍對于初次介紹有點(diǎn)過(guò)多。對于那些興趣沒(méi)有得到滿(mǎn)足的讀者,我會(huì )指出 Haskell 允許您創(chuàng )建自己的數據類(lèi)型,并繼承類(lèi)型實(shí)例中那些數據類(lèi)型的特性。Haskell 類(lèi)型系統包含了面向對象編程的基本特性(繼承、多態(tài)性、封裝);但是它所用的方法在 C++/Java/Smalltalk/Eiffel 編程思想方法中幾乎是不可領(lǐng)會(huì )的。
本教程中省略的另一個(gè)重要元素是對單目的討論,因此也不討論
I/O。編寫(xiě)的教程甚至不以“Hello World!”程序開(kāi)始好象挺奇怪,但以函數型編程思想方法考慮需要一些變化。雖然“Hello World!”相當簡(jiǎn)單,但它也牽涉到單目的“偽命令”世界。初學(xué)者容易被偽命令樣式的 I/O 所哄騙,并忘掉真正要做什么。學(xué)習游泳的最好方法就是跳進(jìn)水中。

關(guān)于 Haskell

Haskell 只是眾多函數型編程語(yǔ)言中的一個(gè)。其它包括 Lisp、Scheme、Erlang、Clean、Mercury、ML、OCaml 等等。常用的附屬語(yǔ)言 SQL XSL 也是函數型的。與函數型語(yǔ)言相同,諸如 Prolog 語(yǔ)言等邏輯型或基于約束的語(yǔ)言都是聲明型的。相反,過(guò)程和面向對象的語(yǔ)言(廣義上)都是命令式的。有些語(yǔ)言(如 Python、Scheme、Perl Ruby)跨越這些范例邊界;但是在很大程度上,編程語(yǔ)言都有一個(gè)特定的主要關(guān)注焦點(diǎn)。

在函數型語(yǔ)言中,Haskell 在許多方面都是最理想化的語(yǔ)言。Haskell 是一個(gè)純函數型語(yǔ)言,這意味著(zhù)它避開(kāi)了所有的副作用(后面會(huì )詳細說(shuō)明)。Haskell 有一個(gè)非嚴格或延遲求值模型,而且它有嚴格的類(lèi)型(但是有一些允許特定的多態(tài)性)。其它函數型語(yǔ)言在這些特性中有所不同 因為這對它們的設計理念很重要 但是這種特性集合會(huì )使人們(有待論證)最大程度地以函數型方式考慮程序。

有一點(diǎn)要略加注意,Haskell 在語(yǔ)法上比列表派生的語(yǔ)言(特別是對于那些輕松地使用諸如 Python、TCL REXX 等標點(diǎn)分隔的語(yǔ)言的程序員)更容易理解。大多數運算符都是插入詞,而不是前綴??s排和模塊組織看上去相當熟悉。最引人注目的可能是,避免了嵌套括號過(guò)分地深(如我們在 Lisp 中所見(jiàn)到那樣)。

獲得 Haskell

Haskell 有幾個(gè)用于多平臺的實(shí)現。它們包括一個(gè)名為 Hugs 的解釋型版本和幾個(gè) Haskell 編譯器。所有這些的最佳起點(diǎn)是 Haskell.org。鏈接會(huì )引導您通向各種 Haskell 實(shí)現。根據您的操作系統以及它的打包系統,可能已安裝了 Haskell,或者可能有一個(gè)標準方法來(lái)安裝運行就緒的版本。建議那些學(xué)習本教程的人員如果要開(kāi)始實(shí)踐并與本教程一起執行的話(huà),就獲取 Hugs,如果您愿意的話(huà)。

作出放棄

Haskell 開(kāi)始編程的最困難部分就是放棄在命令式編程思想中的許多非常熟悉的技術(shù)和方法。第一印象常常是:如果您不能處理 X、Y Z,特別是在 X、Y Z 是“常規”命令式編程中最常用的模式時(shí),那么編寫(xiě)一個(gè)計算機程序一定是完全不可能的。在這一節中,讓我們看一看 Haskell(和一般的函數型編程) 最“令人震驚”的一些特性。

沒(méi)有可變變量

在命令式編程中最常用的編程習慣之一是:將一個(gè)值賦給一個(gè)變量,然后將另一個(gè)值賦給該變量;可能在這個(gè)方法中,而這可能就發(fā)生在我們測試該變量是否獲得了某些鍵值的過(guò)程中。象下面的 C 示例那樣的結構是隨處可見(jiàn)的(其它命令式語(yǔ)言都是類(lèi)似的):

if (myVar==37) {…}

myVar += 2

for (myVar=0; myVar<37; myVar++) {...}

相反,在 Haskell 中,這類(lèi)變量根本不存在??梢詫⒁粋€(gè)名稱(chēng)與一個(gè)值綁定,但一旦指定,這個(gè)名稱(chēng)在整個(gè)程序中只表示這個(gè)值。不允許任何更改。

Haskell 中,“變量”與數學(xué)等式中的變量非常相似。它們需要滿(mǎn)足特定規則,但是它們不是命令式編程樣式中的“計數器”或“容器”。為了先大致了解什么是正確思維,請考慮下面的線(xiàn)性方程以獲取靈感:

10x + 5y - 7z + 1 = 0

17x + 5y - 10z + 3 = 0

5x - 4y + 3z - 6 = 0

在這類(lèi)描述中,我們有“未知數”,但這些未知數在我們計算它們時(shí)并不更改它們的值。

隔離副作用

Haskell 中,函數計算在程序中不能有副作用。命令式程序中的多數副作用大概都是上一屏中提及的變量重新賦值那一類(lèi)(不管是全局變量、局部變量還是字典、列表或其它存儲結構),但是每個(gè) I/O 事件也是一種副作用。I/O 改變了世界而不是計算本身的一部分。自然地,許多時(shí)候您希望以某種方法改變世界(如果不這樣,甚至無(wú)法知道程序已在運行)。Haskell 將所有這樣的副作用限制在一個(gè)稱(chēng)為單目 IO 的狹小“框”中。單目中的任何事物都無(wú)法出來(lái),而單目外的任何事物也無(wú)法進(jìn)入。

通常,結構化的命令式編程接近了函數型編程限制 I/O 的目標。好的設計可能要求輸入和輸出只在一組有限的適當命名的函數中發(fā)生。很少有結構化編程會(huì )到處和以難以預知的方式對 STDIO、文件、圖形設備等進(jìn)行讀寫(xiě)。函數型編程將這個(gè)界限提到一個(gè)高得多的級別。

沒(méi)有循環(huán)

Haskell 另一個(gè)有趣的特性是它沒(méi)有任何循環(huán)結構。沒(méi)有 for while。沒(méi)有 GOTO branch jmp break。人們可能認為在沒(méi)有這種基本(命令式)結構的情況下,不可能控制程序去執行什么;但是擺脫這些結構實(shí)際上是徹底的解放。

沒(méi)有循環(huán)實(shí)際上等同于沒(méi)有副作用問(wèn)題。因為一次循環(huán)中的變量值不能與另一次循環(huán)中的變量值不同,所以無(wú)法區分它們;而且通常是為了執行另一個(gè)程序活動(dòng)時(shí)才需要分支。因為函數型編程沒(méi)有活動(dòng),只有定義,那么還有分支干擾嗎?

但是,我應該嘗試站在公正的立場(chǎng)。實(shí)際證明可以模擬幾乎所有的常見(jiàn)循環(huán)結構,常常使用與其它語(yǔ)言相同的關(guān)鍵字,并且其樣式與命令式結構驚人地相似。Simon Thompson 在他的書(shū)籍中提供了許多這樣的示例(請參閱本教程末尾處的“參考資料”)。

沒(méi)有程序次序

Haskell 沒(méi)有的(或不需要的)另一樣事物是程序次序。組成程序的一組定義可以任何次序發(fā)生。當定義看上去非常象命令式語(yǔ)言中的賦值時(shí),這可能有點(diǎn)奇怪。例如,這樣看上去好象有問(wèn)題:

– Program excerpt

j = 1+i

i = 5

– Hugs session after loading above program

– Main> i

– 5 :: Integer

– Main> j

– 6 :: Integer

要理解與上面程序相似的程序,需要知道 i j 并不是賦的值,而是以給定方式定義的。事實(shí)上,在上例中,i j 都是函數,上面的示例都是函數定義。在許多命令式編程語(yǔ)言中,也不允許多次定義函數(至少在同一作用域中)。

Haskell 程序中有什么?

一個(gè) Haskell 程序基本上由一組函數定義組成。函數與名稱(chēng)的綁定方法非常類(lèi)似于其它語(yǔ)言中的變量賦值。但是,這實(shí)際并不相同;一個(gè) Haskell 綁定名稱(chēng)更類(lèi)似于數學(xué)證明中的綁定。在數學(xué)證明中,我們可以說(shuō)“讓 tau 表示一個(gè)等式 …”名稱(chēng)綁定只提供一個(gè)簡(jiǎn)寫(xiě)供等式稍后使用,但是名稱(chēng)在一個(gè)程序中只能綁定一次 嘗試更改它時(shí)會(huì )發(fā)生程序錯誤。

myNum :: Int — int myNum() {

myNum = 12+13 — return 12+13; }

square :: Int -> Int — int square(int n) {

square n = n*n — return = n*n; }

double :: Int -> Int — int double(int n) {

double n = 2*n — return 2*n; }

dubSqr :: Int -> Int — int dubSqr(int n) {

dubSqr n = square (double n) — return square(double(n)); }

定義函數

一個(gè)函數定義有兩個(gè)可選部分。第一個(gè)部分(概念上,在清單中不是必需的)是函數的類(lèi)型說(shuō)明。在函數中,類(lèi)型說(shuō)明定義輸入的所有類(lèi)型和輸出的類(lèi)型。在示例中的行尾注釋中提供了一些類(lèi)似的 C 定義。

函數定義的第二部分是函數的實(shí)際計算。在這個(gè)部分中,通常(但非總是)將一些特別變量提供給等號左側,這些變量參與對右側的計算。但是,與 C 中的變量不同,Haskell 變量更象是數學(xué)中的變量,它指等號兩邊完全相同的“未知量”(而不引用保存值的“容器”)。

在函數定義中,完全繞過(guò)變量的明確命名常常也是可能的。在 dubSqr2 中,完全可以表示不管對什么事物進(jìn)行 double 操作,我們都應該進(jìn)行 square 運算。對此,不需要提及變量名,因為經(jīng)過(guò) dubSqr2 的事物只是那些在以后的表達式中跟在綁定名稱(chēng)的表達式。當然,double 本身必須采用 dubSqr2 期望的同一輸入類(lèi)型,然后輸出 square 需要作為輸入的輸出類(lèi)型。

example :: Int

example = double (myNum - square (2+2))

dubSqr2 :: Int -> Int

dubSqr2 = square . double — Function composition

更簡(jiǎn)單的函數定義

C 一樣,Haskell 有嚴格的類(lèi)型。averageThree 是一個(gè)很好的函數示例,為了返回正確的值類(lèi)型,它需要對類(lèi)型進(jìn)行強制轉換。但是,difSquare 函數顯示了與 Haskell 的一些差異。difSquare 沒(méi)有類(lèi)型說(shuō)明,所以 Haskell 將根據函數定義中涉及的操作來(lái)推斷適當的類(lèi)型說(shuō)明。首先從外表看,它好象與那些動(dòng)態(tài)類(lèi)型或類(lèi)型寬松的語(yǔ)言執行的相同;但是 Haskell 所執行的完全不同。difSquare 在編譯時(shí)(對此沒(méi)有運行時(shí)動(dòng)態(tài)一說(shuō))是有嚴格類(lèi)型的,但是 difSquare 的類(lèi)型有一個(gè)類(lèi)型類(lèi),它包括了整數和浮點(diǎn)數(還有有理數和復數等)。我們可以在 Hugs 中找到推理類(lèi)型:

Main> :type difSquare

difSquare :: Num a => a -> a -> a

也就是,將兩個(gè)輸入自變量和輸出都推斷為具有“類(lèi)型類(lèi)”Num。如果我們顯式地聲明一個(gè)諸如 Int 的類(lèi)型,則函數將對更小范圍的值進(jìn)行操作(根據我們的需要,這樣做有利有弊)。

– Average of three Integers as floating point

averageThree :: Int -> Int -> Int -> Float

averageThree l m n = fromInt(l+m+n) / 3

— float averageThree(int l, int m, int n) {

— return ((float)(l+m+n))/3; }

difSquare x y = (x-y)^2 — C lacks polymorphic type inference

遞歸

沒(méi)有循環(huán)結構,Haskell 程序中的流程通常表示為遞歸。依據遞歸考慮所有流程要花一些工作,但是它證實(shí)是與其它語(yǔ)言中的 while for 結構具有恰恰相同的表達和強大功能。

遞歸的訣竅是我們希望它最終終止(至少通常是這樣的)。保證遞歸終止的一種方法是使用原語(yǔ)遞歸。它實(shí)際使一個(gè)“事物”遞歸,并確保下一個(gè)調用比當前的調用更接近終止條件。實(shí)際上,要確保這樣,我們可以在每次調用時(shí)遞減一個(gè)整數(并且在零或其它目標時(shí)終止),或對每個(gè)連續調用只取列表的末尾(并在列表為空時(shí)終止)。示例中列出的兩個(gè)版本的階乘都假設它們將傳遞一個(gè)大于零的整數(否則,會(huì )失??;練習:如何進(jìn)行?)。

也存在非原語(yǔ)遞歸,但是很難確定知道遞歸將終止。還允許函數之間的相互遞歸(常會(huì )遇到),但是原語(yǔ)遞歸仍是最安全最常用的形式。

– Factorial by primitive recursion on decreasing num

fac1 :: Int -> Int

fac1 n = if n==1 then 1 else (n * fac1 (n-1))

– Factorial by primitive recursion on list tail

fac2 :: Int -> Int

fac2 n = prodList [1 .. n]

prodList lst =

if (length lst)==1 then head lst

else head lst*(prodList (tail lst))

模式匹配

在函數型編程中,我們“更多地會(huì )關(guān)心如何定義而不是如何計算的細節”(但是要有保留地記住這句座右銘,效率在某些情況下仍很要緊)。思路是:得出如何獲得解決方案是編譯器或解釋器的工作,而不是讓程序員去做。

指定如何定義一個(gè)函數的有用方法是描述在給定各種輸入類(lèi)型的情況下,它將返回什么樣的結果。在 Haskell 中描述“輸入的各種類(lèi)型”的功能強大的方法是使用模式匹配。我們可以為一個(gè)函數提供多個(gè)定義,每個(gè)定義都有一個(gè)對輸入自變量的特定模式。成功與給定的函數調用匹配的第一個(gè)列出的定義就是用于該調用的。用這種方法,可以抽出列表的頭尾,匹配特定的輸入值,將空的列表標識為自變量(通常用于遞歸)并分析其它模式。但是,不能用模式匹配執行值比較(例如,必須以不同方法檢測“n <= 3)。下劃線(xiàn)應該在與某內容匹配的位置處使用,而該位置的匹配值不在定義中使用。

prodLst2 [] = 0 — Return 0 as product of empty list

prodLst2 [x] = x — Return elem as prod of one-elem list

prodLst2 (x:xs) = x * prodLst2 xs

third (a,b,c,d) = c — The third item of a four item tuple

three = third (1,2,3,4) — ‘three’ is 3

– Is a sequence a sub-sequence of another sequence?

isSubseq [] _ = True

isSubseq _ [] = False

isSubseq lst (x:xs) = (lst==start) || isSubseq lst xs

where start = take (length lst) (x:xs)

保護信息

有些類(lèi)似于模式匹配,并且與 if .. then .. else 也相似,這些結構(在前期示例中看到的)在函數定義中是保護信息。保護信息只是可能獲取的條件和屬于該情況的函數的定義。任何可以用模式匹配聲明的事物也可以改述為保護信息,但是保護信息還允許使用另外的測試。只要是第一個(gè)匹配的保護信息(按列出的次序),就成為特定應用程序的函數定義(其它保護信息也可能匹配,但如果它們列在后面,則它們就不用于調用)。

在效率方面,如果可能,模式匹配通常是最佳的。經(jīng)??赡軐⒈Wo信息與模式匹配相結合,如 isSublist 示例中。

prodLst3 lst — Guard version of list product

| length lst==0 = 0

| length lst==1 = head lst

| otherwise = head lst * prodLst3 (tail lst)

– A sublist is a string that occurs in order, but not

– necessarily contiguously in another list

isSublist [] _ = True

isSublist _ [] = False

isSublist (e:es) (x:xs)

| e==x && isSublist es xs = True

| otherwise = sublist (e:es) xs

列表包含

Haskell 中功能最強的結構之一是列表包含(從數學(xué)家的角度看:本術(shù)語(yǔ)源自 Zermelo-Frankel 集合論的“包含公理”)。與其它函數型語(yǔ)言相似,Haskell 除了操作列表外,還構建了許多功能。但是,在 Haskell 中,可能會(huì )生成一份壓縮格式的列表,簡(jiǎn)單聲明列表元素的來(lái)源以及元素滿(mǎn)足哪些標準。用列表包含描述的列表必須從其它原始列表中生成;但是幸運地是,Haskell 還提供了一個(gè)快速“枚舉”語(yǔ)法來(lái)指定原始列表。

– Odd little list of even i’s, multiple-of-three j’s,

– and their product; but limited to i,j elements

– whose sum is divisible by seven.

myLst :: [(Int,Int,Int)]

myLst = [(i,j,i*j) | i <- [2,4..100],

j <- [3,6..100],

0==((i+j) `rem` 7)]

– Quick sort algorithm with list comp and pattern matching

– ‘++’ is the list concatenation operator; we recurse on both

– the list of “small” elements and the list of “big” elements

qsort [] = []

qsort (x:xs) = qsort [y | y<-xs, y<=x] ++ [x] ++ qsort [y | y<-xs, y>x]

延遲求值 I

在命令式語(yǔ)言中(及在某些函數型語(yǔ)言中),表達式求值是精確且直接的。例如,如果用 C 語(yǔ)言編寫(xiě) x = y+z;,則您告訴計算機馬上進(jìn)行計算,并將一個(gè)值放入名為“x”的內存中?。o(wú)論何時(shí)遇到這一代碼)。相反,在 Haskell 中,求值是延遲的 僅在差不多需要表達式求值時(shí),才對表達式求值(很明顯,C 包含了布爾表達式的快捷方法,它是一種很小的延遲)。示例中的 f g 的定義顯示了這種差異的簡(jiǎn)單形式。

g 那樣的函數有點(diǎn)愚蠢,因為恰好不使用 y,具有模式匹配或保護信息的函數經(jīng)常僅在某些情況下才會(huì )使用特定自變量。如果一些自變量有一定的特性,則對于給定計算,它們或其它自變量可能不是必需的。在這種情況下,不執行不必要的計算。而且,當以計算方法(列表包含和枚舉省略號形式)表示列表時(shí),實(shí)際只計算那些真正使用的列表元素。

f x y = x+y — Non-lazy function definition

comp1 = f (4*5) (17-12) — Must compute arg vals in full

g x y = x+37 — Lazy function definition

comp2 = g (4*5) (17-12) — ‘17-12′ is never computed

– Lazy guards and patterns

– Find the product of head of three lists

prodHeads :: [Int] -> [Int] -> [Int] -> Int

prodHeads [] _ _ = 0 — empty list give zero product

prodHeads _ [] _ = 0

prodHeads _ _ [] = 0

prodHeads (x:xs) (y:ys) (z:zs) = x*y*z

– Nothing computed because empty list matched

comp3 = prodHeads [1..100] [] [n | n <- [1..1000], (n `rem` 37)==0]

– Only first elem of first, third list computed by lazy evaluation

comp4 = prodHeads [1..100] [55] [n | n <- [1..1000], (n `rem` 37)==0]

延遲求值 II

關(guān)于 Haskell(和延遲求值)有一點(diǎn)真的值得注意,那就是可能使用無(wú)限列表。不僅僅是大型列表,而是真的無(wú)限!當然,這個(gè)訣竅就是不明確計算列表中特定計算不需要的部分(只是由運行時(shí)環(huán)境保持它們擴展的規則)。

找到素數的著(zhù)名且古老的算法是“愛(ài)拉托遜斯篩法”。思路是保持整數列表的初始元素,但根據可能的素數去掉它的倍數。示例就是這樣做的,但是它只是按特定計算需要執行。但是,列表 prime 實(shí)際就是全體素數的列表。

– Define a list of ALL the prime numbers

primes :: [Int]

primes = sieve [2 .. ] — Sieve of Eratosthenes

sieve (x:xs) = x : sieve [y | y <- xs, (y `rem` x)/=0]

memberOrd :: Ord a => [a] -> a -> Bool

memberOrd (x:xs) n

| x

| x==n = True

| otherwise = False

isPrime n = memberOrd primes n

– isPrime 37 is True

– isPrime 427 is False

第一類(lèi)函數(傳遞函數)

Haskell(及所有函數型編程)的一個(gè)強大特性就是函數都是第一類(lèi)的。函數的第一類(lèi)狀態(tài)表示函數本身只是值。就象您將一個(gè)整數作為自變量傳遞給函數一樣,在 Haskell 中,您可以將一個(gè)函數傳遞給另一個(gè)函數。在有限范圍內,可以用諸如 C 語(yǔ)言等語(yǔ)言中的函數指針來(lái)這樣做,但是 Haskell 的用途更為廣泛。

Haskell 的第一類(lèi)函數的能力很大程度上存在于 Haskell 的類(lèi)型檢查系統。在 C 中,可能編寫(xiě)一個(gè)“快速排序”函數,將函數指針作為自變量接受,很象 Haskell 示例。但是,在 C 中,您很難確保(所指向的)函數有正確的類(lèi)型說(shuō)明。即,無(wú)論哪個(gè)函數作為自變量傳遞給 qsortF,都必須取兩個(gè)同一類(lèi)型的自變量(“a”代表一般類(lèi)型)并產(chǎn)生一個(gè) Bool 結果。當然,作為第二個(gè)自變量傳遞給 qsortF 的列表也必須是同一類(lèi)型“a”。還要注意,樣本代碼中給定的類(lèi)型說(shuō)明只是文檔編制所需要的。如果省去說(shuō)明,則 Haskell 會(huì )自動(dòng)推斷所有這些類(lèi)型的約束。tailComp 滿(mǎn)足正確的類(lèi)型說(shuō)明,其類(lèi)型為 String,這是 qsortF 自變量中允許的特殊化的常規類(lèi)型(不同的比較函數可能對不同的類(lèi)型或類(lèi)型類(lèi)進(jìn)行操作)。

– Quick sort algorithm with arbitrary comparison function

qsortF :: (a -> a -> Bool) -> [a] -> [a]

qsortF f [] = []

qsortF f (x:xs) = qsortF f [y | y<-xs, f y x] ++

[x] ++

qsortF f [y | y<-xs, not (f y x)]

– Comparison func that alphabetizes from last letter back

tailComp :: String -> String -> Bool

tailComp s t = reverse s < reverse t

– List of sample words

myWords = [”foo”, “bar”, “baz”, “fubar”, “bat”]

– tOrd is [”foo”,”bar”,”fubar”,”bat”,”baz”]

tOrd = qsortF tailComp myWords

– hOrd is [”bar”,”bat”,”baz”,”foo”,”fubar”]

hOrd = qsortF (<) myWords

第一類(lèi)函數(函數工廠(chǎng))

將函數傳遞給其它函數只是第一類(lèi)函數的部分功能。函數也可以當作工廠(chǎng),并由此產(chǎn)生一些新函數。在程序工具中創(chuàng )建具有任意功能的函數的能力可以相當強大。例如,可以通過(guò)計算產(chǎn)生一個(gè)新的比較函數,接著(zhù)將它傳遞給前一屏中的 qsortF 函數。

一般,創(chuàng )建函數的方法是使用 lambda 記號。具有函數型特性的許多語(yǔ)言都將字“lambda”用作運算符的名稱(chēng),但是 Haskell 使用反斜杠字符(因為它優(yōu)點(diǎn)類(lèi)似于希臘字母 lambda)。lambda 記號非常象類(lèi)型說(shuō)明。箭頭表明 lambda 記號描述從一種事物(反斜杠后的事物)到另一種事物(在箭頭后的事物)的函數。

示例工廠(chǎng) mkFunc 將一定量的描述壓縮成一個(gè)簡(jiǎn)短描述。主要需注意的是 lambda 表明從 n 到結果的函數。通過(guò)類(lèi)型說(shuō)明,雖然類(lèi)型推斷允許一個(gè)更寬的類(lèi)型,但每一個(gè)都是 Int。函數定義的格式是原語(yǔ)遞歸。一個(gè)空列表產(chǎn)生的結果為零。非空列表產(chǎn)生的結果由它的 head 對提供或是如果只考慮它的末尾(并且末尾最終通過(guò)遞歸趨于空)而產(chǎn)生的結果。

– Make an “adder” from an Int

mkAdder n = addN where addN m = n+m

add7 = mkAdder 7 — e.g. ‘add7 3′ is 10

– Make a function from a mapping; first item in pair maps

– to second item in pair, all other integers map to zero

mkFunc :: [(Int,Int)] -> (Int -> Int)

mkFunc [] = (\n -> 0)

mkFunc ((i,j):ps) = (\n -> if n==i then j else (mkFunc ps) n)

f = mkFunc [(1,4),(2,3),(5,7)]

– Hugs session:

– Main> f 1

– 4 :: Int

– Main> f 3

– 0 :: Int

– Main> f 5

– 7 :: Int

– Main> f 37

– 0 :: Int

基本語(yǔ)法

本教程至此,我們以非正式方法已看了一些 Haskell 代碼。在最后一部分中,我們將明確一些我們已在做的內容。事實(shí)上,Haskell 的語(yǔ)法非常直觀(guān),而且易懂。通常,最簡(jiǎn)單的規則就是“寫(xiě)下您的意思”。

Haskell和文字Haskell

本教程中的示例都使用了標準 Haskell 格式。在標準格式中,在注釋的左邊用雙破折號表示注釋。示例中的所有注釋都是行尾注釋?zhuān)瑏?lái)表示行中雙破折號后的所有內容都是注釋。也可以用一對“{-”和“-}”將塊括起來(lái),創(chuàng )建多行注釋。標準 Haskell 文件應該用 .hs 擴展名命名。

文字腳本編制是 Haskell 源文件的一種替代格式。在以 .lhs 擴展名命名的文件中,所有程序行都以大于字符開(kāi)始。非程序行的內容就是注釋。這種樣式強調對程序的描述甚于程序的實(shí)現。它類(lèi)似于:

Factorial by primitive recursion on decreasing num

> fac1 :: Int -> Int

> fac1 n = if n==1 then 1 else (n * fac1 (n-1))

Make an “adder” from an Int

> mkAdder n = addN where addN m = n+m

> add7 = mkAdder 7

越位規則

有時(shí),在 Haskell 程序中,函數定義將跨多行,并由多個(gè)元素組成。同一概念級別上的元素塊的規則是它們應該縮進(jìn)相同的量。屬于更高級別元素的元素應該縮進(jìn)得更多。只要發(fā)生凸出,則提示更深的行都倒退一個(gè)概念級。實(shí)際上,這是顯而易見(jiàn)的,Haskell 幾乎會(huì )一直報錯。

– Is a function monotonic over Ints up to n?

isMonotonic f n

= mapping == qsort mapping — Is range list the same sorted?

where — “where” clause is indented below “=”

mapping = map f range — “where” definition remain at least as

range = [0..n] — indented (more would be OK)

– Iterate a function application n times

iter n f x

| n == 0 = x — Guards are indented below func name

| otherwise = f (iter (n-1) f x)

我發(fā)現兩個(gè)空格的縮排對子元素的外觀(guān)比較好看,但是為了可讀性,在格式編排中可以有許多自由性(只是在同一級別中不要凸出)。

運算符和函數優(yōu)先級

Haskell 中的運算符分為多個(gè)優(yōu)先級。大多數優(yōu)先級與您從其它編程語(yǔ)言期望的相同。乘法比加法的優(yōu)先級高,等等(所以“2*3+4 10,不是 14)。Haskell 的標準文檔可以提供詳細信息。

但是,Haskell 優(yōu)先級中有一個(gè)“gotcha”很容易出錯。函數比運算符的優(yōu)先級高。結果是,表達式“f g 5表示“將 g(和 5)作為 f 的自變量應用”,而不是“將 (g 5) 的結果應用于 f”。更多的時(shí)候,這種錯誤會(huì )產(chǎn)生編譯器錯誤消息,因為(例如)f 會(huì )需要一個(gè) Int 作為自變量,而不是另一個(gè)函數。但是,有時(shí)情況可能更糟,并且您能編寫(xiě)有效內容,但是是錯誤的:

double n = n*2

res1 = double 5^2 — ‘res1′ is 100, i.e. (5*2)^2

res2 = double (5^2) — ‘res2′ is 50, i.e. (5^2)*2

res3 = double double 5 — Causes a compile-time error

res4 = double (double 5) — ‘res4 is 20, i.e. (5*2)*2

象其它語(yǔ)言一樣,括號在消除表達式歧義時(shí)非常有用,當您有對優(yōu)先級有點(diǎn)疑惑時(shí)(或只是要明確記錄意圖時(shí))。順便說(shuō)一下,注意:括號不用于 Haskell 中的函數自變量;但是對它們進(jìn)行偽裝沒(méi)有什么危害,只是創(chuàng )建一個(gè)額外表達式分組(如上面的 res2)。

名稱(chēng)的作用域

您可能想到本教程中的兩點(diǎn)之間會(huì )有沖突。一方面,我們已經(jīng)說(shuō)過(guò)僅在程序中把名稱(chēng)定義為表達式一次;另一方面,許多示例都重復使用相同的變量名。這兩點(diǎn)都是真的,但是需要優(yōu)化。

每個(gè)名稱(chēng)實(shí)際只在給定作用域內定義一次。每個(gè)函數定義定義它自己的作用域,而且定義內的一些結構定義它們自己更窄的作用域。幸運地是,定義子元素的“越位規則”也精確定義了變量作用域。一個(gè)變量(實(shí)際是名稱(chēng))只能在給定的縮排塊操作中出現一次。讓我們看一個(gè)示例,它與前面的示例很相似:

x x y — ‘x’ as arg is in different scope than func name

| y==1 = y*x*z — ‘y’ from arg scope, but ‘x’ from ‘where’ scope

| otherwise = x*x — ‘x’ comes from ‘where’ scope

where

x = 12 — define ‘x’ within the guards

z = 5 — define ‘z’ within the guards

n1 = x 1 2 — ‘n1′ is 144 (’x’ is the function name)

n2 = x 33 1 — ‘n2′ is 60 (’x’ is the function name)

無(wú)需多說(shuō),這個(gè)示例引起不必要的混淆。但是,它是可以理解的,特別是因為自變量在特定函數定義內只有一個(gè)作用域(而且在其它函數定義中可以使用相同的名稱(chēng))。

分解問(wèn)題

您可能已注意到一點(diǎn):Haskell 中的函數定義與其它語(yǔ)言相比較,非常的簡(jiǎn)短。Haskell 語(yǔ)法簡(jiǎn)練是其原因之一,但是更重要的原因是在函數型編程中強調將問(wèn)題分解成它們的多個(gè)組件部分(而不是象命令式程序中只是在每個(gè)點(diǎn)上“執行需要執行的內容”)。這增強了各部分的可重用性,并允許更好地驗證每個(gè)部分實(shí)際執行了希望它所做的內容。

可以用多種方法分離出函數定義的各個(gè)小部分。一種方法是在源文件中定義多個(gè)有用的支持函數,并按需要使用它們。本教程中的示例主要都是這樣做的。但是,還有兩種(等價(jià)的)方法在單個(gè)函數定義的狹小作用域中定義支持函數:let 子句和 where 子句。下面是一個(gè)簡(jiǎn)單示例。

f n = n+n*n

f2 n

= let sq = n*n

in sq+n

f3 n

= sq+n

where sq = n*n

這三個(gè)定義是等價(jià)的,但是 f2 f3 選擇在函數作用域中定義一個(gè)(瑣碎的)支持函數 sq。

導入/導出

Haskell 還支持一個(gè)模塊系統,允許用于較大規模的函數模塊性(還可用于本介紹性教程中未涉及的類(lèi)型)。模塊控制的兩個(gè)基本元素是導入規范和導出規范。前者與 import 說(shuō)明一起使用;后者與 module 聲明一起使用。有些示例包括:

– declare the current module, and export only the objects listed

module MyNumeric ( isPrime, factorial, primes, sumSquares ) where

import MyStrings — import everything MyStrings has to offer

— import only listed functions from MyLists

import MyLists ( quicksort, findMax, satisfice )

— import everything in MyTrees EXCEPT normalize

import MyTrees hiding ( normalize )

— import MyTuples as qualified names, e.g.

— three = MyTuples.third (1,2,3,4,5,6)

import qualified MyTuples

您會(huì )發(fā)現 Haskell 對其它函數可以看見(jiàn)函數定義的位置提供了相當多的細顆粒度的控制。這一模塊系統幫助構建大規模組件化的系統。

參考資料

  • 要獲取 Haskell,請訪(fǎng)問(wèn) Haskell.org。Haskell 有幾個(gè)用于多平臺的實(shí)現。它們包括一個(gè)名為 Hugs 的解釋型版本和幾個(gè) Haskell 編譯器。
  • 最近有兩本關(guān)于 Haskell 新書(shū)可以幫助您更好地學(xué)習:
    • Haskell: The Craft of Functional Programming (Second Edition),由 Simon Thompson 著(zhù)(Addison-Wesley,1999
    • The Haskell School of Expression: Learning Functional Programming through Multimedia ,由 Paul Hudak 著(zhù)(Cambridge University Press,2000
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Haskell教程
C++中Static作用和使用方法
常微分方程的解
讓程序員更加優(yōu)秀:函數式思維和函數式編程
C++ 引用傳遞
函數的定義是什么?如何理解函數的定義?
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久