将文件读入类型 - Haskell

时间:2012-04-16 13:40:27

标签: file haskell input

现在我有两种类型:

type Rating = (String, Int)

type Film = (String, String, Int, [Rating])

我有一个包含此数据的文件:

"Blade Runner"
"Ridley Scott"
1982
("Amy",5), ("Bill",8), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",7), ("Megan",4)

"The Fly"
"David Cronenberg"
1986
("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",6)

如何查看存储所有条目的文件,如FilmDatabase = [Film]?

3 个答案:

答案 0 :(得分:7)

Haskell提供了一种草绘您的方法的独特方式。从你所知道的开始

module Main where

type Rating = (String, Int)
type Film = (String, String, Int, [Rating])

main :: IO ()
main = do
  films <- readFilms "ratings.dat"
  print films

尝试将此程序加载到ghci中将产生

films.hs:8:12: Not in scope: `readFilms'

它需要知道readFilms是什么,所以添加足够的代码来继续移动。

readFilms = undefined

这是一个应该执行与Film数据相关的功能的函数。重新加载此代码(使用:reload命令或简称:r)以获取

films.hs:9:3:
    Ambiguous type variable `a0' in the constraint:
      (Show a0) arising from the use of `print'
    ...

print的类型是

Prelude> :t print
print :: Show a => a -> IO ()

换句话说,print只接受一个参数,非正式地知道如何展示自己(即将其内容转换为字符串)并创建一个I / O操作,在执行时输出该字符串。您或多或少希望print能够工作:

Prelude> print 3
3
Prelude> print "hi"
"hi"

我们知道我们想要print来自文件的Film数据,但是,尽管很好,ghc无法读懂我们的想法。但在添加类型提示后

readFilms :: FilePath -> Film
readFilms = undefined

我们收到了新的错误。

films.hs:8:12:
    Couldn't match expected type `IO t0'
                with actual type `(String, String, Int, [Rating])'
    Expected type: IO t0
      Actual type: Film
    In the return type of a call of `readFilms'
    In a stmt of a 'do' expression: films <- readFilms "ratings.dat"

错误告诉您编译器对您的故事感到困惑。你说readFilms应该给它一个Film,但是你在main中调用它的方式,计算机应该首先执行一些I / O然后然后回馈Film数据。

在Haskell中,这是 pure 字符串之间的区别,比如"JamieB"和副作用,比如在提示您输入Stack Overflow用户名后从键盘读取输入

现在我们知道我们可以将readFilms描绘为

readFilms :: FilePath -> IO Film
readFilms = undefined

并且代码编译! (但我们还不能运行它。)

要挖掘另一个图层,假装单个电影的名称是ratings.dat中唯一的数据,并在其他地方放置占位符以保持类型检查器的满意度。

readFilms :: FilePath -> IO Film
readFilms path = do
  alldata <- readFile path
  return (alldata, "", 0, [])

此版本编译,您甚至可以通过在ghci提示符下输入main来运行它。

dave4420’s answer中有关于其他功能的很好的提示。可以把上面的方法想象成拼凑拼图,其中各个部分都是功能。为了使您的程序正确,所有类型必须放在一起。如上所述,你可以通过采取小的babysteps来推进你的最终工作计划,如果你的草图中有错误,typechecker会告诉你。

要弄清楚的事情:

  • 如何将整个输入的blob转换为单独的行?
  • 你如何判断你的程序正在检查的行是标题,导演等?
  • 如何将文件中的年份(String)转换为Int以配合Film的定义?
  • 你如何跳过空白或空行?
  • 如何使readFilms累积并返回Film数据列表?

答案 1 :(得分:6)

这是家庭作业吗?

您可能会发现这些功能很有用:

请注意String[Char]相同。

一些线索:

  • dropWhile null将从列表的开头删除空行
  • break null会将列表拆分为非空行的主要行和列表的其余部分

答案 2 :(得分:0)

Haskell有一种很好的方法来使用类型来找到合适的函数。例如:在Gregs回答中,他希望你弄清楚(除其他事项外)如何将电影的年份从String转换为Int。好吧,你需要一个功能。应该是什么类型的功能?它接受一个String并返回一个Int,因此类型应为String -> Int。完成后,转到Hoogle并输入该类型。这将为您提供类似类型的函数列表。你需要的函数实际上有一个稍微不同的类型 - Read a => String -> a - 所以它有点在列表中,但是猜测一个类型然后扫描结果列表通常是一个非常有用的策略。