【问题标题】:Searching a .txt file line by line Haskell逐行搜索 .txt 文件 Haskell
【发布时间】:2018-03-15 20:14:02
【问题描述】:

我是 Haskell 的新手,我创建了一个函数,允许“用户”将新电影添加到数据库(名为 Films.txt 的文本文件)。

main = do
    putStrLn "Insert film title:"
    film <- getLine
    putStrLn ("Who directed " ++ film ++ "?")
    director <- getLine
    putStrLn ("What year was " ++ film ++ " released?")
    year <- getLine
    appendFile "Films.txt" $ (film ++ "\n")
    appendFile "Films.txt" $ (director ++ "\n")
    appendFile "Films.txt" $ (year ++ "\n")
    appendFile "Films.txt" $ (" " ++ "\n")

创建的文本文件示例如下:

Blade Runner
Ridley Scott
1982
The Fly
David Cronenberg
1986
etc...

如果我只想返回某个导演的电影,我将如何逐行搜索此文件。在另一种语言中,我将使用 FOR 循环逐行搜索 .txt 文件,直到一行与搜索词匹配,例如“雷德利·斯科特”。然后我会返回匹配行上方的行,输出它(电影名称)并继续搜索,直到 .txt 文件完成。

但是在 Haskell 中,我无法将这个思考过程转化为代码,主要是因为我找不到逐行搜索文件的方法。

谢谢

【问题讨论】:

    标签: haskell


    【解决方案1】:

    使用您自己的方法,扫描文件以查找名称:

    main = do
        putStrLn "Enter Director's name"
        name <- getLine
        base <- readFile "Films.txt"     -- base is the whole file contents as a single string
        print $moviesBy name $lines base
    
    moviesBy :: String -> [String] -> [[String]]
    moviesBy name (title:director:year:_:others) | director == name = [title, director, year]:moviesBy name others
                                                 | otherwise = moviesBy name others -- a different director, scan the rest of the file
    moviesBy _ _ = [] -- when there's no more records
    

    更详细...

    lines base 将文件内容分成几行(删除换行符),生成字符串列表。

    moviesBy 接受一个字符串作为搜索模式,以及一个作为文件内容的字符串列表。从这个文件的创建方式来看,我们希望在每四行中的每一行 second 处找到导演的姓名。这是作为第一个模式匹配完成的:

    moviesBy name (title:director:year:_:others)
    

    这里,(title:director:year:_:others) 匹配至少四个元素的列表,将它们绑定到相关变量(第四个位置的_ 是一个通配符模式,这里与空白字符串匹配,这对我们来说并不重要) . others,作为: 的最右边的操作数,因此匹配列表的剩余部分(第 4 个尾部,用 Lisp 的说法)。在管道| 之后,我们对匹配添加了一个额外的约束,即四联体中的第二个元素应该等于所关注的导演姓名。如果为真,则在输出中插入[title, director, year] 的列表(作为: 的左操作数在= 的右侧,生产规则的右侧部分)和列表的剩余部分(@ 987654333@) 通过递归调用检查(右操作数 :); otherwise 这个四联体被跳过,只考虑剩下的部分。

    最后一行的匹配处理任何少于四个元素的列表(可能是文件的结尾)。由于我们不太可能在如此短的列表中找到更多导演的电影,因此我们只需返回 []

    例如,如果我们有一个列表

    ["Blade Runner", "Ridley Scott", "1982", " ", "Alien", "Ridley Scott", "1979", " "]
    

    然后寻找我们获得的斯科特的电影:

    1. 首先,列表与(title:director:year:_:others) 匹配。然后绑定变量:title 是“银翼杀手”,director 是“Riddley Scott”,year 是“1982”,others["Alien", "Ridley Scott", "1979", " "]。由于director 等于我们要查找的名称,因此我们采用第一条路径并声明["Blade Runner", "Ridley Scott", "1982"]:moviesBy "Ridley Scott" ["Alien", "Ridley Scott", "1979", " "] 是我们的结果。

    2. 接下来,递归调用构造结果列表的尾部。同样,title 是“Alien”,director“Ridley Scott”,year 是“1979”,others 绑定到一个空列表。因此,第 1 步生成的列表的尾部是 ["Alien", "Ridley Scott", "1979"]:moviesBy "Ridley Scott" []

    3. 最后一次递归调用。我们不能将至少四个元素的模式绑定到空列表,所以我们采用最后一个替代方案,moviesBy _ _,它匹配字符串和列表的任意组合,这里的结果是 [](递归停止)。所以,第 2 步的结果是["Alien", "Ridley Scott", "1979"]:[],换句话说,[["Alien", "Ridley Scott", "1979"]]。并且在 1. 中加上 head,函数的总结果是 [["Blade Runner", "Ridley Scott", "1982"], ["Alien", "Ridley Scott", "1979"]]

    【讨论】:

    • 谢谢你,它正是我需要的。我可以要求您添加更多 cmets 来解释哪些位做什么,以便我下次理解吗?非常感谢
    猜你喜欢
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多