目前,我做這樣的事情在我的代碼:創建列表的另一種方法比理解更多嗎?
--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]
所有的時間的另一種方式......
目前,我做這樣的事情在我的代碼:創建列表的另一種方法比理解更多嗎?
--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]
所有的時間的另一種方式......
我會說不用擔心。 「創建範圍[1..n]
」在這裏並不是一個明確的步驟;那[1..n]
desugars到enumFromTo 1 n
,它的構造懶洋洋地像其他所有東西一樣。這裏沒有隱藏的成本,需要消除。
此外,'[1..n]'可以參與列表融合,因此列表可能被完全消除。 – 2012-02-13 17:12:28
我喜歡它那樣:
generateFoos n = map (createFoo . show) [1..n]
或者是範圍本身的問題?然後我推薦:
generateFoos n = map (createFoo . show) (enumFromTo 1 n)
擴展我上面的評論 - 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]
替換代碼所以這是自然的,相對簡單列表內涵可以變成了涉及在映射函數表達式範圍。
所有的列表推導都可以平等地「自然地」轉換爲'map'和'filter'的組合。語法糖的意義在於它更甜。 – 2012-02-13 16:26:11
我同意 - 但我認爲知道在列表理解中沒有「魔法」是有幫助的。它由解釋器/編譯器轉換成簡單的舊Haskell,你可以自己寫。只要知道你可以將你的列表理解轉換成不同的形式,有時候可以幫助你編寫更多可讀的代碼。同樣,知道替代'do'符號可以幫助您將代碼中不純的部分與純粹的部分隔離開來。 – 2012-02-13 16:32:17
我只是不同意你的說法,地圖在這裏更自然。你所有的信息都很紮實,我同意你的推理。 – 2012-02-13 16:42:01
否map
,沒有範圍。
generateFoos n = unfoldr (doit (createFoo . show)) 1 where
doit f acc = if acc > n then Nothing else Just (f acc, acc + 1)
我不保證該代碼的任何特定的質量或性能雖然;)
我喜歡展示。很少使用,但功能強大。 – 2012-02-13 16:32:18
有什麼不對的範圍? – hammar 2012-02-13 16:10:24
一點都沒有,但只是檢查是否有其他方式做事。 – drozzy 2012-02-13 16:29:04