2012-02-13 74 views
1

目前,我做這樣的事情在我的代碼:創建列表的另一種方法比理解更多嗎?

--Generate a list of n 'Foo's 
generateFoos n = [createFoo (show i) | i <- [1..n]] 

-- Create a Foo with a given name 
createFoo :: String -> Foo 

,我很困惑,如果有這樣做比創建範圍[1..n]所有的時間的另一種方式......

+0

有什麼不對的範圍? – hammar 2012-02-13 16:10:24

+0

一點都沒有,但只是檢查是否有其他方式做事。 – drozzy 2012-02-13 16:29:04

回答

7

我會說不用擔心。 「創建範圍[1..n]」在這裏並不是一個明確的步驟;那[1..n] desugars到enumFromTo 1 n,它的構造懶洋洋地像其他所有東西一樣。這裏沒有隱藏的成本,需要消除。

+2

此外,'[1..n]'可以參與列表融合,因此列表可能被完全消除。 – 2012-02-13 17:12:28

2

我喜歡它那樣:

generateFoos n = map (createFoo . show) [1..n] 

或者是範圍本身的問題?然後我推薦:

generateFoos n = map (createFoo . show) (enumFromTo 1 n) 
+0

我知道一個邪惡會想出一個方法來適應'地圖'這個! – drozzy 2012-02-13 15:54:50

+1

@drozzy:這很明顯。如果你認爲'map'是邪惡的,也許Haskell不適合你? :P – 2012-02-13 15:57:32

+2

@NiklasB。 'import Data.Humor' – drozzy 2012-02-13 16:03:37

6

擴展我上面的評論 - map函數自然產生的原因如下。

在Haskell中,列表內涵是做記號只是語法糖:

[ 2 * x | x <- [1..10] ] 

相當於

do { x <- [1..10]; return (2 * x) } 

反過來,做記號是一元的綁定語法糖 - 上面是等價到

[1..10] >>= \x -> return (2 * x) 

這是可行的,因爲List是一個單子。這使得List成單子的代碼(忽略一些無關痛癢的東西),以

concat (map (\x -> return (2 * x)) [1..10]) 

instance Monad [] where 
    return x = [x] 
    xs >>= f = concat (map f xs) 

所以調用上述>>=等同,如果我們更換調用bind,相當於到

concat (map (\x -> [2 * x]) [1..10]) 

所以我們映射功能\x -> [2 * x]在列表[1..10],然後調用concat的結果。但由於我們的函數只有每建立一個元素列表,我們可以跳過了調用Concat的,並與

map (\x -> 2 * x) [1..10] 

替換代碼所以這是自然的,相對簡單列表內涵可以變成了涉及在映射函數表達式範圍。

+0

所有的列表推導都可以平等地「自然地」轉換爲'map'和'filter'的組合。語法糖的意義在於它更甜。 – 2012-02-13 16:26:11

+0

我同意 - 但我認爲知道在列表理解中沒有「魔法」是有幫助的。它由解釋器/編譯器轉換成簡單的舊Haskell,你可以自己寫。只要知道你可以將你的列表理解轉換成不同的形式,有時候可以幫助你編寫更多可讀的代碼。同樣,知道替代'do'符號可以幫助您將代碼中不純的部分與純粹的部分隔離開來。 – 2012-02-13 16:32:17

+0

我只是不同意你的說法,地圖在這裏更自然。你所有的信息都很紮實,我同意你的推理。 – 2012-02-13 16:42:01

1

map,沒有範圍。

generateFoos n = unfoldr (doit (createFoo . show)) 1 where 
    doit f acc = if acc > n then Nothing else Just (f acc, acc + 1) 

我不保證該代碼的任何特定的質量或性能雖然;)

+0

我喜歡展示。很少使用,但功能強大。 – 2012-02-13 16:32:18

相關問題