阅读文件的行,排序并返回中间元素

问题描述:

我对Haskell的IO有点新,虽然我经常阅读它,但我的代码仍然无法工作。阅读文件的行,排序并返回中间元素

我希望应用程序做什么:

  1. 读取文件的所有行(FILE1.TXT,FILE2.TXT,...),其中都含有数字在每行(浮像1.12345)
  2. 排序所有这些行(字符串排序或浮动的排序并不重要,我想串排序是更快?)
  3. 获取列表的中间元素,并打印出来

这是我的代码到目前为止。我可以保证函数“middle”在传递[String]时工作正常。

middle :: [a] -> a 
middle xs = (drop ((l - 1) `div ` 2) xs) !! 0 
      where l = length xs 

getSortedMiddleElement :: Int -> String 
getSortedMiddleElement i = do 
    dat <- readFile $ "file" ++ (show i) ++ ".txt" 
    return $ middle $ sort $ lines dat 

我从一个“内部 - >内容”,呼吁getSortedMiddleElement功能(我用耶索德),其中数字正在通过URL传递和中间元素应该返回给用户。为了让内容脱离字符串,它需要是“字符串”,而不是“IO字符串”......如何轻松实现?

在此先感谢!

+2

一种可能性是将类型更改为“Int - > IO String”,然后像处理该函数一样处理,例如, 'readFile'或任何其他IO功能。 – kqr

+0

谢谢,这听起来不错!我该如何处理'getTestR :: Int - > Handler RepPlain',它会返回HTTP响应?'getTestR :: Int - > IO Handler RepPlain'不起作用:( – sibbl

+0

我希望我可以告诉你,但我绝不是Yesod的专家。:( – kqr

您的类型签名表示您的函数是纯的(即它需要一个Int并返回一个String),但在里面,您正在执行IO! Haskell不会让你编写这样的函数。你从文件中读取的任何内容都会永久卡在IO monad中,这就是(当然,除了不安全的函数)。

在这种情况下,结果并不那么糟糕,因为Yesod是一个严重的基于IO的框架。所有的网络流量都停留在IO monad中!

当你在一个monad变压器堆栈中时,你可以在堆栈的每个级别访问monadic计算,但只有其中一个直接。您可以使用lift将计算从堆栈中的单层移动到已转换的monad。如果IO位于堆栈中,无论层数有多少,都可以通过liftIO直接访问其操作。

所以,如果你有type T = ReaderT String IO那么你可能有一个功能foo :: Int -> T String。在此函数中,您将在T monad中操作,它将IO monad与Reader monad功能进行转换。在这种情况下,您可以说lift readFile,而不是获得IO String结果,您将得到T String结果!这只是IO String包装的ReaderT类型,所以不要认为我们做了任何棘手的事情,比如逃脱IO monad。这可能是一个有点混乱,所以让我们来看一个例子:

import Control.Monad.Reader (ReaderT) 
import Control.Monad.Writer (WriterT) 
import Control.Monad.Trans (lift, liftIO) 

type T = ReaderT String IO 
getSortedMiddleElement :: Int -> IO String 

foo :: Int -> T String 
foo n = do 
    str <- lift $ getSortedMiddleElement n --str holds a pure String now 
    lift $ putStrLn str     --get `putStrLn` from IO and pass the String 
    return str        --let's wrap it back in T now 

但是,如果我们不止一层离IO?让我们试试看:

type W = WriterT String T -- WriterT String (ReaderT String IO) 

-- This doesn't work; lift only gives you access to the next layer's actions 
-- but IO is now more than one layer away! 
-- 
--bar n = do 
-- str <- lift $ getSortedMiddleElement n 

-- Instead, we need liftIO, which will access IO across many transformer layers 
bar :: Int -> W String 
bar n = do 
    str <- liftIO $ getSortedMiddleElement n 
    liftIO $ putStrLn str 
    return str 
+0

非常感谢这个详细的答案!'LiftIO'正是我所需要的。 – sibbl