现在我有两种类型:
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]?
答案 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会告诉你。
要弄清楚的事情:
String
)转换为Int
以配合Film
的定义?readFilms
累积并返回Film
数据列表?答案 1 :(得分:6)
这是家庭作业吗?
您可能会发现这些功能很有用:
readFile :: FilePath -> IO String
lines :: String -> [String]
break :: (a -> Bool) -> [a] -> ([a], [a])
dropWhile :: (a -> Bool) -> [a] -> [a]
null :: [a] -> Bool
read :: Read a => String -> a
请注意String
与[Char]
相同。
一些线索:
dropWhile null
将从列表的开头删除空行break null
会将列表拆分为非空行的主要行和列表的其余部分答案 2 :(得分:0)
Haskell有一种很好的方法来使用类型来找到合适的函数。例如:在Gregs回答中,他希望你弄清楚(除其他事项外)如何将电影的年份从String转换为Int。好吧,你需要一个功能。应该是什么类型的功能?它接受一个String并返回一个Int,因此类型应为String -> Int
。完成后,转到Hoogle并输入该类型。这将为您提供类似类型的函数列表。你需要的函数实际上有一个稍微不同的类型 - Read a => String -> a
- 所以它有点在列表中,但是猜测一个类型然后扫描结果列表通常是一个非常有用的策略。