【问题标题】:Read lines of a file, sort and return middle element读取文件的行,排序并返回中间元素
【发布时间】:2013-08-27 22:42:07
【问题描述】:

我对使用 Haskell 的 IO 有点陌生,虽然我通读了很多,但我的代码仍然无法工作。

我希望应用程序做什么:

  1. 读取文件的所有行(file1.txt、file2.txt、...),其中每行都包含数字(浮点数,如 1.12345)
  2. 对所有这些行进行排序(字符串排序或浮点排序无关紧要,我认为字符串排序更快?)
  3. 获取列表的中间元素并打印出来

这是我目前所拥有的代码。我可以保证在传递 [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 -> Content”函数(我使用 Yesod)调用 getSortedMiddleElement,其中数字通过 URL 传递,中间元素应返回给用户。要从字符串中取出Content,必须是“String”,而不是“IO String”……这怎么能轻松实现?

提前致谢!

【问题讨论】:

  • 一种可能性是将类型更改为Int -&gt; IO String,然后像处理函数一样处理函数,例如readFile 或任何其他 IO 函数。
  • 谢谢,到目前为止听起来不错!如何处理返回 HTTP 响应的getTestR :: Int -&gt; Handler RepPlain? getTestR :: Int -&gt; IO Handler RepPlain 不起作用:(
  • 我希望我能告诉你,但我绝不是 Yesod 方面的专家。 :(
  • 也许Handler monad 已经包含IO monad 了?你可以试试liftIO (getTestR 5)。
  • 一个lift 就足够了,Handler RepPlain 是变压器堆栈HandlerT s IO RepPlain 的同义词(其中s 是基础类型的类型,通常命名为App。这是为了但是,Yesod 1.2,但是由于我不知道您的版本,所以这是我能给出的最佳答案。

标签: sorting haskell io readfile yesod


【解决方案1】:

您的类型签名表明您的函数是纯函数(即,它接受一个 Int 并返回一个字符串)但在内部,您正在执行 IO! Haskell 不会让你编写这样的函数。您从文件中读取的任何内容都将永远停留在 IO monad 中,仅此而已(当然,不安全的函数除外)。

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

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

所以如果你有type T = ReaderT String IO,那么你可能有一个函数foo :: Int -&gt; T String。在此函数中,您将在 T monad 中进行操作,它将 IO monad 转换为 Reader monad 功能。在这种情况下,您可以说lift readFile,而不是获得IO String 结果,您将获得T String 结果!不过,这只是包裹在 ReaderT 类型中的 IO String,所以不要认为我们做了任何棘手的事情,比如转义 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

【讨论】:

  • 非常感谢您的详细解答! LiftIO 正是我所需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 2015-12-27
  • 1970-01-01
相关资源
最近更新 更多