gpt4 book ai didi

haskell IO : convert IO String to "Other type"

转载 作者:行者123 更新时间:2023-12-02 15:47:00 24 4
gpt4 key购买 nike

我有一个 Haskell 程序,它将文件作为输入并将其转换为二叉搜索树。

import System.IO    

data Tree a = EmptyBST | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)

ins :: Ord a => a -> (Tree a) -> (Tree a)
ins a EmptyBST = Node a EmptyBST EmptyBST
ins a (Node p left right)
| a < p = Node p (ins a left) right
| a > p = Node p left (ins a right)
| otherwise = Node p left right



lstToTree :: Ord a => [a] -> (Tree a)
lstToTree = foldr ins EmptyBST

fileRead = do file <- readFile "tree.txt"
let a = lstToTree (conv (words file))
return a

conv :: [String] -> [Int]
conv = map read

但是,当我运行以下命令时:
ins 5 fileRead 

我收到以下错误:
<interactive>:2:7:
Couldn't match expected type `Tree a0'
with actual type `IO (Tree Int)'
In the second argument of `ins', namely `fileRead'
In the expression: ins 5 fileRead
In an equation for `it': it = ins 5 fileRead

请问有人可以帮助我吗?

谢谢

最佳答案

如果您提供 fileRead,您将能够立即看到问题。带有类型签名。让我们弄清楚 GHC 将在内部分配给 fileRead 的类型注释。 :

fileRead = do file <- readFile "tree.txt"
let t = lstToTree $ map read $ words file
return t
lstToTree :: Ord a => [a] -> Tree a , 和 read总是返回 Read 的成员类型类。所以 t :: (Read a, Ord a) => Tree a .具体类型取决于文件的内容。
return将它的参数包装在一个单子(monad)中,所以 return t类型为 Ord a, Read a => IO (Tree a) .由于 return tdo 中的最后一条语句 block ,它变成 fileRead 的返回类型, 所以
fileRead :: (Read a, Ord a) => IO (Tree a)

所以 fileReadTree包裹在 IO 中, 并且不能直接传入 ins因为它需要 Tree在其自己的。你不能拿 TreeIO ,但您可以“提升”功能 ins进入 IO单子(monad)。

Control.Monad 导出 liftM :: Monad m => (a -> r) -> (m a -> m r) .它接受一个常规函数,并将其变成一个作用于像 IO 这样的单子(monad)的函数。 .它实际上是 fmap 的同义词(在标准 Prelude 中),因为所有单子(monad)都是仿函数。所以这段代码,大致相当于@us202 的,取 fileRead 的结果。 , 插入 5 , 并返回包装在 IO 中的结果.
liftM (ins 5) fileRead
-- or --
fmap (ins 5) fileRead

我推荐 fmap版本。此代码仅利用了 IO是仿函数,所以使用 liftM向读者暗示你可能也需要它是一个单子(monad)。

“提升”是对包裹在单子(monad)或仿函数中的值使用纯函数的一般技术。如果您不熟悉提升(或者如果您对一般的单子(monad)和仿函数感到困惑),我衷心推荐 Learn You A Haskell 的第11-13 章。 .

PS。注意 fileRead 的最后两行可能应该合并,因为 return并没有真正做任何事情:
fileRead :: (Read a, Ord a) => IO (Tree a)
fileRead = do file <- readFile "tree.txt"
return $ lstToTree $ map read $ words file

或者,由于它是一个足够短的函数,您可以取消 do完全符号并使用 fmap再次:
fileRead :: (Read a, Ord a) => IO (Tree a)
fileRead = fmap (lstToTree . map read . words) (readFile "tree.txt")

根据您的评论进行编辑:

Haskell 特意设计为将执行 IO 的代码与常规代码分开。这有一个很好的哲学原因:大多数 Haskell 函数是“纯的”——也就是说,它们的输出仅取决于输入,就像数学中的函数一样。你可以运行一个纯函数一百万次,你总是会得到相同的结果。我们喜欢纯函数,因为它们不会意外破坏程序的其他部分,它们允许惰性,并且它们允许编译器积极地为您优化代码。

当然,在现实世界中,我们需要一点杂质。 IO 代码如 getLine不可能是纯的(而且不做 IO 的程序是没用的!)。 getLine 的结果取决于用户输入的内容:您可以运行 getLine一百万次,每次都得到不同的字符串。 Haskell 利用类型系统将不纯代码标记为 IO .

这就是问题的关键:如果你对不纯的数据使用纯函数,那么结果仍然是不纯的,因为结果取决于用户做了什么。所以整个计算属于 IO单子(monad)。当您想将纯函数带入 IO 时您必须显式(使用 fmap )或隐式(使用 do 表示法)解除它。

这是 Haskell 中非常常见的模式 - 查看我的 fileRead 版本多于。我用过 fmap对不纯的 IO 进行操作具有纯函数的数据。

关于 haskell IO : convert IO String to "Other type",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15641584/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com