IsPrefixOf работает неправильно во время сегмента сопоставления с образцом - PullRequest
0 голосов
/ 27 апреля 2019

Я очень новичок в Хаскеле.У меня есть список, который действует как база данных, и я хочу искать заголовки списка с префиксом, который вы можете вставить.Тип данных для заголовков: [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)

Где albumToString - это просто функция, которая аккуратно отображает базу данных.

Я понимаю, что проблема, вероятно, является серьезным упущением.

1 Ответ

1 голос
/ 28 апреля 2019

Я думаю, что у вас, возможно, сложилось впечатление, что тип для строк в 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с префиксом, отличным от одноэлементного списка.(Таким образом, дополнительные элементы вызовут ошибку времени выполнения, а не будут игнорироваться.)

...