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

IsPrefixOf not working correctly during pattern matching segment

我对 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只是一个整齐显示数据库的函数。

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

我想您可能已经形成这样的印象,即 Haskell 中字符串的类型是 [String]。但是,此类型表示 list 个字符串,而不是单个字符串。单个字符串的类型只是 String.

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

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

这意味着每张专辑:

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

有一个 Title,它是一个 list 字符串和一个 Artist,它是一个 list 字符串。我想我可以看出您可能想要多个艺术家(虽然这样类型可能应该以复数形式命名为 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

当调用 as 时,说:

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

创建参数绑定 prefx=["G"]t=["Greatest Hits"]isPrefixOf 调用检查 one-element 列表 ["G"] 是否是 one-element 列表 ["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 不是 one-element 列表的记录,或者如果 searchByPrefix 是用一个one-element 列表以外的前缀。 (因此额外的元素将导致运行时错误,而不是被默默地忽略。)