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

时间:2013-08-25 08:50:22

标签: sorting haskell io readfile yesod

我对使用Haskell的IO有点新意,虽然我经常阅读它,但我的代码仍无效。

我希望该应用做什么:

  1. 读取文件的所有行(file1.txt,file2.txt,...),其中每行都包含数字(浮点数如1.12345)
  2. 对所有这些行进行排序(字符串排序或浮点排序无关紧要,我假设字符串排序更快?)
  3. 获取列表的中间元素并将其打印出来
  4. 这是我到目前为止的代码。我可以确保函数“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
    

    我从“Int - &gt; Content”函数调用getSortedMiddleElement(我使用Yesod),其中数字通过URL传递,中间元素应该返回给用户。要从字符串中获取Content,它必须是“String”,而不是“IO String”......如何轻松实现?

    提前致谢!

1 个答案:

答案 0 :(得分:5)

您的类型签名表示您的函数是纯函数(即,它需要一个Int并返回一个String)但在内部,您正在执行IO! Haskell不会让你写这样的函数。你从文件中读到的任何东西都会永远停留在IO monad中,那就是那个(当然,除非是不安全的函数)。

在这种情况下,事实并非如此糟糕,因为Yesod是一个基于IO的重度框架。所有网络流量也都停留在IO monad中!

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

因此,如果你有type T = ReaderT String IO,那么你可能有foo :: Int -> T String函数。在此函数中,您将在T monad中运行,该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