2014-03-05 21 views
2

說我有一個整數l = [1,2]迭代印刷在列表中的每個整數

,我想打印到stdout的名單。

print l產生[1,2]

說我想要打印的清單沒有括號

map print l產生

No instance for (Show (IO())) arising from a use of `print' 
Possible fix: add an instance declaration for (Show (IO())) 
In a stmt of an interactive GHCi command: print it 

`:T打印

print :: Show a => a -> IO() 

因此,雖然我認爲這將工作我w提前嘗試:

map putStr $ map show l 

因爲我懷疑從整數到字符串的類型不匹配是怪罪。這產生了與上面相同的錯誤信息。

我意識到我可以做一些事情,比如把列表連接成一個字符串,但是我想盡量避免這種情況。

發生了什麼事?如何在不從List的元素構造字符串的情況下做到這一點?

回答

2

假設您有一個列表xs :: [a]和功能f :: Monad m => a -> m b。您想將函數f應用於xs的每個元素,生成一系列操作,然後對這些操作進行排序。下面是我將如何構建一個函數,將其稱爲mapM,這是做到這一點。在基本情況下,xs = []是空列表,我們只需返回[]。在遞歸情況下,xs的格式爲x : xs。首先,我們要將f應用於x,並採取行動f x :: m b。接下來,我們需要xs遞歸調用mapM。執行第一步的結果是一個值,例如y;執行第二步的結果是一個值列表,例如ys。因此,我們收集yys到一個列表,然後在單子返回他們:

mapM :: Monad m => (a -> m b) -> [a] -> m [b] 
mapM f []  = return [] 
mapM f (x : xs) = f x >>= \y -> mapM f ys >>= \ys -> return (y : ys) 

現在我們可以映射像print的功能,它會返回在IO單子的操作,在值打印的清單: mapM print [1..10]對於從1到10的整數列表完全是這樣做的。然而,存在一個問題:我們並不特別關心收集印刷操作的結果;我們主要關心他們的副作用。我們只需返回(),而不是返回y : ys

mapM_ :: Monad m => (a -> m b) ->[a] -> m() 
mapM_ f []  = return() 
mapM_ f (x : xs) = f x >> mapM_ f xs 

注意mapMmapM_可以在不使用標準庫中的sequencesequence_功能,這樣做正是他們的名字所暗示的明確遞歸定義。如果您查看mapMmapM_的源代碼Control.Monad,您會看到它們以這種方式實現。

+0

這個答案教會了我更多關於Haskell,然後我以前做的所有擺弄。謝謝 –

+0

沒問題 - 我很高興能幫上忙。 –

6

的問題是,

map :: (a -> b) -> [a] -> [b] 

所以我們最終[IO()]。這是一個純粹的價值,一個IO行動清單。它實際上不會打印任何東西。相反,我們希望

mapM_ :: (a -> IO()) -> [a] -> IO() 

的命名約定*M意味着它工作在單子和*_意味着我們扔掉的價值。這與地圖相似,只不過它將每個動作都排序爲>>以返回IO動作。

作爲示例mapM_ print [1..10]將在新行上打印每個元素。

2

Haskell中的所有內容都非常強大,包括執行IO的代碼!

當您編寫print [1, 2]時,這只是putStrLn (show [1, 2])的便利包裝,其中show是一個將(Show'able)對象轉換爲字符串的函數。 print本身不任何東西(副作用意義上的),但它輸出一個IO()行動,這有點像一個迷你unrun「程序」(如果你原諒馬虎的語言),這是't'在創建時運行,但可以傳遞給後續執行。您可以驗證在ghci中

> :t print [1, 2] 
print [1, 2]::IO() 

這只是IO()類型的對象類型....您可以立即拋出這樣的離去,不會發生什麼。更有可能的是,如果你在main中使用這個對象,那麼IO代碼將會運行,副作用和所有的

當您將多個putStrLn(或print)函數映射到列表中,你仍然可以得到一個對象的類型,你可以在ghci中

> :t map print [1, 2] 
map print [1, 2]::[IO()] 

像以前一樣查看,這僅僅是一個對象,你可以繞過,而且它本身不會做任何事情。但與以前不同的是,類型在main中的用法不正確,這需要IO()對象。爲了使用它,你需要將它轉換爲這種類型。

有很多方法可以做這種轉換....我喜歡的一種方法是sequence函數。

sequence $ map print [1, 2] 

這需要IO操作的列表(副作用即 - 迷你「方案」,如果你會原諒馬虎的語言),並作爲IO動作在一起序列。這個代碼現在只能做你想做的事情。


由於jozefg指出,雖然sequence作品,sequence_這裏是一個更好的選擇.... 序列不僅concatinates東西在IO動作,而且還把返回值的列表...由於print的返回值是IO(),所以新的返回值成爲()(IO)的無用列表。 :)

+0

這不是正確的..我想你的意思'順序$地圖print'但爲什麼不' mapM'在這種情況下 – jozefg

+0

缺失的'map'只是一個重新輸入的錯誤,我已經修復了它。除此之外,我不知道爲什麼我會得到downvote,但我在代碼中同時使用'mapM'和'sequence',我認爲'sequence'既是一種有用的教學方法,也是一種教學方法。它有助於將每個IO()操作看作是一段單獨的代碼,它們可以一起排序。儘管有用,但我認爲mapM更像是一種聰明的方式,可以使功能代碼看起來像命令式的代碼。不要誤解我的意思,我不討厭它,我只是認爲先理解順序並不困惑。 – jamshidh

+0

因爲輸入錯誤,因爲'mapM'和'sequence'都生成無用的[[()]]列表。我會說'mapM_'實際上是這裏正確的東西。 – jozefg

0

你可能會寫自己的功能,太:

Prelude> let l = [1,2] 

Prelude> let f [] = return(); f (x:xs) = do print x; f xs 

Prelude> f l 
1 
2 
相關問題