IsPrefixOf在模式匹配段期间无法正常工作

时间:2019-04-27 13:15:56

标签: string haskell prefix

我对Haskell非常陌生。我有一个充当数据库的列表,我想用可以插入的前缀搜索列表的标题。标题的类型数据为[String]

最终设法使代码编译,但仅在插入完整标题的情况下,该功能才有效,而不是搜索前缀。因此,例如,我想搜索[“ G”]并弹出两个结果,但是除非输入完整标题,否则搜索不会有结果。

-- Types
type Title = [String]
type Artist = [String]
type Year = Int
type Sales = Int

-- Define Album type here
type Album  =  (Title, Artist, Year, Sales)

-- Define database type here
type Database = [Album]

然后数据库遵循此模式

[(["Greatest Hits"                 ],        [ "Queen"          ],   1981,    6300000),
(["Gold: Greatest Hits"            ],        [ "ABBA"           ],   1992,   5400000),
...

-

- Detects if the prefix is in the string ahead of it
    searchByPrefix :: [String] -> Album -> Bool
    searchByPrefix prefx (t, a, y, s)
      | isPrefixOf prefx t = True
      | otherwise = False

    -- A function that displays all the albums by a certain artist
    displayAlbumsByPrefix :: [String] -> Database ->  String
    displayAlbumsByPrefix prefx database = albumsToString (filter (searchByPrefix (prefx)) database)

albumsToString只是一个整洁地显示数据库的函数。

我知道这个问题可能是一个巨大的疏忽。

1 个答案:

答案 0 :(得分:1)

我认为您可能已经形成一种印象,即Haskell中字符串的类型为[String]。但是,此类型表示字符串的列表,而不是单个字符串。单个字符串的类型仅为String

因此,您对这些类型的选择有些奇怪:

type Title = [String]
type Artist = [String]

这意味着每张专辑:

type Album  =  (Title, Artist, Year, Sales)

有一个Title是一个字符串的列表和一个Artist是一个字符串的列表。我想我可以看到您可能需要多个歌手(尽管然后该类型应该以复数形式命名为Artists),但我认为专辑标题的单个字符串就足够了。

之所以提出这一点,是因为如果数据库中存在这样的条目,您的函数displayAlbumsByPrefix应该做的模棱两可:

...
(["Dark Side of the Moon", "Gold CD Ultradisc Re-release"], ["Pink Floyd"], 1979, 2520000),
...

是否应将其包含在displayAlbumsByPrefix ["G"]调用生成的列表中?还是只检查Title列表中第一个字符串的前缀?如果数据库中有一个标题为空列表[]的条目怎么办?

无论如何,我们将其搁置一旁,并假设您想坚持使用当前代码,按照惯例,数据库将始终包含仅包含一个字符串["like this"]的列表的标题,并且您希望根据该字符串的前缀。

在这种情况下,您快到了。代码:

searchByPrefix :: [String] -> Album -> Bool
searchByPrefix prefx (t, a, y, s)
  | isPrefixOf prefx t = True
  | otherwise = False

当被称为时,

searchByPrefix ["G"] (["Greatest Hits"],["Queen"],...)

创建参数绑定prefx=["G"]t=["Greatest Hits"]isPrefixOf调用检查单元素列表["G"]是否是单元素列表["Greatest Hits"]的前缀-换句话说,元素"G"是否等于到"Greatest Hits"。显然这就是False,这就是为什么您的代码没有执行您想要的操作。比较以下内容以查看发生了什么:

> isPrefixOf ["G"] ["Goo"]    -- False  -- string "G" is not equal to string "Goo"
> isPrefixOf "G" "Goo"        -- True   -- character 'G' is equal to character 'G'

您可以通过调用列表顶部的isPrefixOf而不是列表本身来解决此问题:

searchByPrefix :: [String] -> Album -> Bool
searchByPrefix prefx (t, a, y, s)
  | isPrefixOf (head prefx) (head t) = True  -- change this line
  | otherwise = False

如果数据库记录中的前缀或标题列表为空,这将失败并出现运行时错误,并且将静默忽略这些列表中第一个元素之后的任何其他元素。

您也可以通过模式匹配来做同样的事情:

searchByPrefix :: [String] -> Album -> Bool
searchByPrefix [prefx] ([t], a, y, s)   -- or change this line
  | isPrefixOf prefx t = True
  | otherwise = False

,如果数据库包含带有Title的记录而不是单元素列表,或者如果调用searchByPrefix的前缀不是a,则此版本将因运行时错误而失败。一元素列表。 (因此,多余的元素将导致运行时错误,而不是被忽略。)