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

sib*_*bbl 0 sorting io haskell readfile yesod

我对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
Run Code Online (Sandbox Code Playgroud)

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

提前致谢!

Lev*_*son 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.在这个函数中,你将在Tmonad中运行,IOmonad使用Readermonad功能转换monad.在这种情况下,你可以说lift readFile,而不是得到一个IO String结果,你会得到一个T String结果!不过,这只是一个IO String包装ReaderT类型,所以不要认为我们做了任何棘手的事情,比如逃避IOmonad.这可能有点令人困惑,所以让我们看一个例子:

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
Run Code Online (Sandbox Code Playgroud)

但是,如果我们离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
Run Code Online (Sandbox Code Playgroud)