2012-09-27 94 views
5

我有三個函數(getRow,getColumn,getBlock)帶有兩個參數(x和y),每個參數產生一個相同類型的列表。我想寫一個串接其輸出第四功能:如何通過Haskell中的多個參數映射函數列表?

outputList :: Int -> Int -> [Maybe Int] 
outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

功能的工作原理,但有沒有辦法重寫雙圖(有三個'$的),以一個單一的地圖?

回答

22
import Data.Monoid 

outputList :: Int -> Int -> [Maybe Int] 
outputList = mconcat [getRow, getColumn, getBlock] 

您應得的解釋。

首先,我會明確指出所有這些函數都具有相同的類型。

outputList, getRow, getColumn, getBlock :: Int -> Int -> [Maybe Int] 

現在讓我們從您的原始定義開始。

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

這些函數的結果是一個[Maybe Int],任何東西的列表都是monoid。 Monoidally組合列表與鏈接列表相同,因此我們可以用mconcat替換concat

outputList x y = mconcat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

另一件事是一個monoid是一個函數,如果它的結果​​是一個monoid。也就是說,如果b是幺半羣,那麼a -> b也是幺半羣。單調組合函數與調用具有相同參數的函數相同,然後單調組合結果相同。

因此,我們可以再簡化爲

outputList x = mconcat $ map ($ x) [getRow,getColumn,getBlock] 

然後到

outputList = mconcat [getRow,getColumn,getBlock] 

我們就大功告成了!


Typeclassopedia has a section about monoids,儘管在這種情況下,我不知道它增加遠遠超出了documentation for Data.Monoid說。

4

作爲第一步,我們觀察到您的定義

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

可以使用函數組合物操作者(.)而不是函數應用操作者的($)如下改寫。

outputList x y = (concat . map ($ y) . map ($ x)) [getRow,getColumn,getBlock] 

接下來我們注意到,map是另一名fmap在列表和滿足fmap法律,因此,特別是,我們有map (f . g) == map f . map g。我們應用此法來定義使用單個應用程序map的版本。

outputList x y = (concat . map (($ y) . ($ x))) [getRow,getColumn,getBlock] 

作爲最後一步,我們可以通過concatMap取代的concatmap組合物。

outputList x y = concatMap (($ y) . ($ x)) [getRow,getColumn,getBlock] 

最後,在我看來,雖然哈斯克爾程序員往往使用很多花哨的運營商,它不是一種恥辱定義函數通過

outputList x y = concatMap (\f -> f x y) [getRow,getColumn,getBlock] 

,因爲它明確表示,該函數做什麼。然而,使用類型抽象(如其他答案中所示)可能是一件好事,因爲您可能會發現您的問題具有特定的抽象結構並獲得新的見解。

2

我會與@ dave4420的答案,因爲它最簡潔,並表達你的意思。不過,如果你不想靠Data.Monoid,那麼你可以重寫如下

原始代碼:

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

融合兩個地圖:

outputList x y = concat . map (($y) . ($x)) [getRow,getColumn,getBlock] 

更換concat . mapconcatMap

outputList x y = concatMap (($y) . ($x)) [getRow,getColumn,getBlock] 

然後你就完成了。

編輯: aaaa這和@Jan Christiansen的回答完全一樣。好吧!

+0

順便說一下,有沒有任何靜態需要多長時間,直到有關Haskell的問題得到回答?我的印象是,所有哈斯克爾問題中有90%幾乎立即得到解答。雖然這並沒有說明答案的質量,但我認爲它們的質量也相當高。 –

+2

@JanChristiansen:如果你願意,你可以使用堆棧交換數據瀏覽器來回答。我做了一個非常粗略的近似(過濾器明顯異常,忽略自我答案,&​​c。),典型的(即中位數)時間大約是20分鐘,直到第一個答案發布。 –

相關問題