簡體   English   中英

Haskell IO 傳遞給另一個函數

[英]Haskell IO Passes to Another Function

這里的這個問題與Haskell Input Return Tuple 有關

我想知道我們如何將來自 monad IO 的輸入傳遞給另一個函數以進行一些計算。

其實我想要的是

-- First Example
test = savefile investinput 
-- Second Example
maxinvest :: a
maxinvest = liftM maximuminvest maxinvestinput

maxinvestinput :: IO()
maxinvestinput = do
    str <- readFile "C:\\Invest.txt"
    let cont = words str
    let mytuple = converttuple cont
    let myint = getint mytuple

    putStrLn ""

-- Convert to Tuple
converttuple :: [String] -> [(String, Integer)]
converttuple [] = []
converttuple (x:y:z) = (x, read y):converttuple z

-- Get Integer
getint :: [(String, Integer)] -> [Integer]
getint [] = []
getint (x:xs) = snd (x) : getint xs

-- Search Maximum Invest
maximuminvest :: (Ord a) => [a] -> a
maximuminvest [] = error "Empty Invest Amount List"
maximuminvest [x] = x
maximuminvest (x:xs)   
     | x > maxTail = x  
     | otherwise = maxTail  
     where maxTail = maximuminvest xs 

在第二個示例中,從文件中讀取 maxinvestinput 並將數據轉換為預期的類型 maximuminvest。 請幫忙。

謝謝。

首先,我認為您在理解 Haskell 時遇到了一些基本問題,所以讓我們一步一步地構建它。 希望你會發現這很有幫助。 其中一些只會到達您擁有的代碼,而另一些不會,但它是我在編寫此代碼時所考慮的慢速版本。 之后,我將嘗試回答您的一個特定問題。


我不太確定你想讓你的程序做什么。 我知道您想要一個程序,它將包含人員及其投資列表的文件讀取為輸入。 但是,我不確定你想用它做什么。 您似乎 (a) 想要一個合理的數據結構 ( [(String,Integer)] ),但是 (b) 只使用整數,所以我假設您也想對字符串做一些事情。 讓我們來看看這個。 首先,您需要一個函數,它可以在給定整數列表的情況下返回最大值。 你稱這個為maximuminvest ,但這個函數比投資更通用,那么為什么不稱它為maximum呢? 事實證明,這個函數已經存在。 你怎么會知道這個? 我推薦Hoogle——它是一個 Haskell 搜索引擎,可以讓你搜索函數名稱和類型。 你想要一個從整數列表到單個整數的函數,所以讓我們 搜索那個 事實證明,第一個結果是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

現在我們有了這些,我們可以為自己提供一個方便的函數, maxInvestment :: [String] -> Integer 唯一缺少的是從元組列表到整數列表的能力。 有幾種方法可以解決這個問題。 一個是你擁有的,盡管這在 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 maxInvestment = maximum . map 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 f . g\\x -> f (gx) (它有類型(b -> c) -> (a -> b) -> a -> c ,經過一些思考,這應該是有意義的。)因此, f . g $ hx f . g $ hxf (g (hx)) ,只是更容易閱讀。

現在,我們能夠擺脫let <-呢? 為此,我們可以使用=<< :: Monad m => (a -> mb) -> ma -> mb運算符。 請注意,它幾乎就像$ ,但是m幾乎可以污染所有內容。 這允許我們獲取一個 monadic 值(這里是readFile "C:\\\\Invest.txt" :: IO String ),將它傳遞給一個將普通值轉換為 monadic 值的函數,並獲得該 monadic 值。 因此,我們有

main :: IO ()
main = print . maxInvestment . words =<< readFile "C:\\Invest.txt"

我希望這應該很清楚,尤其是當您將=<<視為一元$

我不確定testfile發生了什么; 如果您編輯您的問題以反映這一點,我會嘗試更新我的答案。


還有一件事。 你說

我想知道我們如何將來自 monad IO 的輸入傳遞給另一個函數以進行一些計算。

與 Haskell 中的所有內容一樣,這是一個類型問題。 因此,讓我們對這里的類型進行解謎。 你有一些函數f :: a -> b和一些一元值m :: IO a 您想使用f來獲取類型b的值。 這是不可能的,正如我在回答你的另一個問題時所解釋的那樣; 但是,你可以得到類型的東西IO b 因此,您需要一個函數來獲取您的f並為您提供一個 monadic 版本。 換句話說, Monad m => (a -> b) -> (ma -> mb) 如果我們將其插入 Hoogle ,第一個結果是Control.Monad.liftM ,它恰好具有該類型簽名。 因此,你可以把liftM作為一個略有不同的“一元$ ”比=<<f `liftM` m適用f到的純結果m (按照哪個單子您正在使用),並返回一元的結果。 不同之處在於, liftM在左側采用純函數,而=<<采用部分liftM

編寫相同內容的另一種方法是使用do -notation:

do x <- m
   return $ f x

這表示“從m取出x ,對其應用f ,然后將結果提升回 monad。” 這與語句return . f =<< m相同return . f =<< m return . f =<< m ,這又是一次liftM 首先f執行純計算; 它的結果被傳遞到return (通過. ),它將純值提升到 monad 中; 然后將這個部分一元函數通過=<,應用到m

太晚了,所以我不確定這有多大意義。 讓我試着總結一下。 簡而言之,沒有通用的方法可以離開 monad 當您想對 monadic 值執行計算時,您將純值(包括函數)提升到 monad 中,而不是相反; 這可能會違反純度,這將是非常糟糕的™。


我希望這實際上回答了你的問題。 如果沒有,請告訴我,以便我可以嘗試使其更有幫助!

我不確定我是否理解你的問題,但我會盡我所能回答。 如果我理解正確的話,我已經簡化了一些事情以解決問題的“實質”。

maxInvestInput :: IO [Integer]
maxInvestInput = liftM convertToIntegers (readFile "foo")

maximumInvest :: Ord a => [a] -> a
maximumInvest = blah blah blah

main = do
   values <- maxInvestInput
   print $ maximumInvest values

OR

main = liftM maximumInvest maxInvestInput >>= print

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM