首先,我認爲你對理解Haskell有一些基本的問題,所以讓我們一步一步地構建。希望你會發現這有幫助。其中一些將會到達你所擁有的代碼,而其中的一些則不會,但是這是我寫這段代碼時會想到的一個緩慢版本。之後,我會試着回答你的一個特定問題。
我不太清楚你想讓你的程序做什麼。據我所知,你需要一個程序來讀取一個包含人員和他們投資清單的文件。不過,我不確定你想用它做什麼。你似乎(a)想要一個明智的數據結構([(String,Integer)]
),但然後(b)只使用整數,所以我會假設你也想對這些字符串做些什麼。我們來看看這個。首先,你需要一個函數,給定一個整數列表,返回最大值。你把這個叫做maximuminvest
,但是這個功能比較一般,只是投資,所以爲什麼不叫它maximum
?事實證明,這個功能已經存在。你怎麼能知道這個?我推薦Hoogle-它是一個Haskell搜索引擎,可以讓你搜索函數名和類型。你需要一個從整數列表到單個整數的函數,所以讓我們search for that。事實證明,第一個結果是maximum
,這是你想要的更一般的版本。但爲了學習的目的,假設你想自己寫;在這種情況下,你的實現就好了。
好的,現在我們可以計算出最大值。但首先,我們需要構建我們的列表。我們將需要[String] -> [(String,Integer)]
類型的函數來將我們的無格式列表轉換爲合理的列表。那麼,要從字符串中獲取整數,我們需要使用read
。長話短說,你目前的實施情況也很好,但我會(a)爲單項目清單添加一個error
案例(或者,如果我感覺不錯,只需要返回一個空列表來忽略最終項目奇數長度列表),和(b)中使用大寫字母的名稱,這樣我就可以分辨的話(而且可能是一個不同的名稱):
tupledInvestors :: [String] -> [(String, Integer)]
tupledInvestors [] = []
tupledInvestors [_] = error "tupledInvestors: Odd-length list"
tupledInvestors (name:amt:rest) = (name, read amt) : tupledInvestors rest
現在TAT我們有了這些,我們可以提供我們自己有一個便利的功能,maxInvestment :: [String] -> Integer
。唯一缺少的是從tupled列表到整數列表的能力。有幾種方法可以解決這個問題。一個是你擁有的,儘管這在Haskell中是不尋常的。第二個將是使用map :: (a -> b) -> [a] -> [b]
。這是一個將函數應用於列表的每個元素的函數。因此,您的getint
相當於更簡單的map snd
。最好的方式可能是使用Data.List.maximumBy :: :: (a -> a -> Ordering) -> [a] -> a
。這就像maximum
,但它允許你使用你自己的比較功能。並且使用Data.Ord.comparing :: Ord a => (b -> a) -> b -> b -> Ordering
,事情變得很好。此功能允許您通過將兩個任意對象轉換爲可比較的東西來比較兩個任意對象。因此,我會寫
maxInvestment :: [String] -> Integer
maxInvestment = maximumBy (comparing snd) . tupledInvestors
雖然你也可以寫maxInvestment = maximum . map snd . tupledInvestors
。
好的,現在進入IO。那麼你的主要功能就是想從特定的文件中讀取數據,計算出最大的投資並將其打印出來。來表示一種方式是作爲一系列的三個不同的步驟:
main :: IO()
main = do dataStr <- readFile "C:\\Invest.txt"
let maxInv = maxInvestment $ words dataStr
print maxInv
(該$
運營商,如果你還沒有看到它,只是功能應用,但更方便的優先級,它的類型是(a -> b) -> a -> b
,這將使意義),但let maxInv
似乎很沒有意義,所以我們可以擺脫的是:
main :: IO()
main = do dataStr <- readFile "C:\\Invest.txt"
print . maxInvestment $ words dataStr
的.
,如果你還沒有看到它,是函數組合; f . g
與\x -> f (g x)
相同。 (它的型號爲(b -> c) -> (a -> b) -> a -> c
,這應該有一些想法是有道理的。)因此,f . g $ h x
與f (g (h x))
相同,只是更容易閱讀。我們能夠擺脫let
。那麼<-
呢?爲此,我們可以使用=<< :: Monad m => (a -> m b) -> m a -> m b
運算符。請注意,它幾乎就像$
,但幾乎所有東西都會被m
污染。這允許我們取一個單值(在這裏,readFile "C:\\Invest.txt" :: IO String
),將它傳遞給一個函數,將一個普通值轉換爲一元值,並獲得該單值。因此,我們有
main :: IO()
main = print . maxInvestment . words =<< readFile "C:\\Invest.txt"
這應該是清楚的,我希望,特別是如果你認爲的=<<
作爲一個單子$
。
我不確定testfile
發生了什麼;如果你編輯你的問題以反映這一點,我會嘗試更新我的答案。
還有一件事。你說
我不知道我們如何可以將monad IO的輸入傳遞給另一個函數,以便做一些計算。
與Haskell中的所有內容一樣,這是類型的問題。所以讓我們通過這裏的類型來解謎。你有一些功能f :: a -> b
和一些monadic值m :: IO a
。您想使用f
來獲得b
類型的值。這是不可能的,正如我在my answer to your other question中解釋的;然而,你可以得到IO b
類型的東西。因此,你需要一個功能,它需要你的f
,並給你一個monadic版本。換句話說,類型爲Monad m => (a -> b) -> (m a -> m b)
。如果我們plug that into Hoogle,第一個結果是Control.Monad.liftM
,它恰好具有該類型簽名。因此,可以治療liftM
作爲略有不同的「一元$
」比=<<
:f \
liftM`米applies
˚Fto the pure result of
米(in accordance with whichever monad you're using) and returns the monadic result. The difference is that
liftM takes a pure function on the left, and
= < <`取的局部一元之一。
另一種方式來寫同樣的事情是do
-notation:
do x <- m
return $ f x
這是說「得到x
出m
,適用f
它,結果擡回到單子。」這與陳述return . f =<< m
相同,這正是liftM
。第一個f
執行純計算;其結果被傳遞到return
(通過.
),將純值提升到monad;然後通過=<,
將該部分一元函數應用於m
。
它已經很晚了,所以我不確定它有多少意義。讓我試着總結一下。總之,沒有通用的方式來留下一個monad。當你想對monadic值進行計算時,你可以將純粹的值(包括函數)提升到monad中,而不是;這可能會違反純度,這將是Very Bad™。
我希望實際上回答你的問題。讓我知道如果沒有,所以我可以嘗試使它更有幫助!
爲什麼功能應用程序有類型簽名 (a - > b) - > a - > b? 爲什麼功能組合具有類型簽名 (b→c) - >(a→b)→a→c? 這是什麼=「:: Monad m =>(a - > m b) - > m a - > m b? 除此之外,我也不明白所有在這裏提到的與monad有關的話題? – peterwkc 2010-06-20 03:33:19
如何改變元組列表中的snd值? [(「rer」,100),(「peter」,23),(「asd」,234)] 100的值需要再加100。謝謝。 – peterwkc 2010-06-20 03:43:00
我需要搜索元組列表並更改第二個值? 請幫忙。 謝謝。 – peterwkc 2010-06-20 03:54:03