Три функции в Haskell, чтобы показать отфильтрованные результаты из списка фильмов


В настоящее время я работаю на простое приложение командной строки в Haskell и у меня есть три функции, которые достаточно регулярны.

Три функции:

getAllFilmsByDirector :: [Film] -> String -> String
getAllFilmsByDirector filmsDb directorName = unlines [name ++ ", " ++ director ++ ", " ++ show year ++ ", " ++ show (calculateRatings likes dislikes)|(name, director, year, likes, dislikes) <- filmsDb, director == directorName]

getAllFilmsWithHighRatings :: [Film] -> String
getAllFilmsWithHighRatings filmsDb = unlines [name ++ ", " ++ director ++ ", " ++ show year ++ ", " ++ show (calculateRatings likes dislikes)|(name, director, year, likes, dislikes) <- filmsDb, (calculateRatings likes dislikes) >= 75]

getAllFilmsByDirectorAvgRating :: [Film] -> String -> Float
getAllFilmsByDirectorAvgRating filmsDb directorName = roundTo (calculateAverageRating [calculateRatings likes dislikes|(name, director, year, likes, dislikes) <- filmsDb, director == directorName]) 1

Тип Пленки

type Film = (String, String, Int, [String], [String])

testDatabase :: [Film]
testDatabase = [("Blade Runner", "Ridley Scott", 1982, ["Zoe", "Heidi", "Jo", "Kate", "Emma", "Liz", "Dave"], ["Sam", "Olga", "Tim"]), ("The Fly", "David Cronenberg", 1986, ["Garry", "Dave", "Zoe"], ["Kevin", "Emma", "Heidi", "Jo", "Kate"]), ("Body Of Lies", "Ridley Scott", 2008, ["Garry", "Dave"], ["Bill", "Olga", "Tim", "Zoe", "Paula"]), ("Avatar", "James Cameron", 2009, ["Dave", "Amy", "Liz"], ["Olga", "Tim", "Zoe", "Paula"]), ("Titanic", "James Cameron", 1997, ["Zoe", "Emma", "Paula", "Liz", "Olga", "Dave"], ["Sam", "Wally", "Kate"]), ("The Departed", "Martin Scorsese", 2006, ["Wally", "Liz", "Kevin", "Tim", "Emma"], ["Olga", "Dave", "Kate", "Zoe"]), ("Aliens", "Ridley Scott", 1986, ["Dave", "Garry", "Liz", "Sam", "Wally", "Kate", "Zoe"], ["Tim", "Emma", "Jo", "Olga"]), ("Kingdom Of Heaven", "Ridley Scott", 2005, ["Jo", "Wally", "Emma"], ["Tim", "Garry", "Ian", "Neal"]), ("Alien: Covenant", "Ridley Scott", 2017, ["Kevin", "Tim"], ["Emma", "Jo", "Liz"]),  ("E.T. The Extra-Terrestrial", "Steven Spielberg", 1982, ["Dave", "Amy", "Garry", "Ian", "Neal"], ["Jenny", "Kate", "Emma", "Olga"]), ("Bridge of Spies", "Steven Spielberg", 2015, ["Wally", "Sam", "Dave", "Neal"], ["Bill", "Garry", "Ian", "Kate"]), ("Jaws", "Steven Spielberg", 1975, ["Jenny", "Emma", "Bill", "Neal"], ["Sam", "Ian", "Kate"]), ("The Martian", "Ridley Scott", 2015, ["Wally", "Sam", "Dave", "Jo", "Jenny", "Kate", "Emma", "Olga"], ["Ian", "Neal", "Tim", "Liz"]), ("The BFG", "Steven Spielberg", 2016, ["Sam", "Wally", "Dave", "Jo", "Kate"], ["Neal"]), ("The Shawshank Redemption", "Frank Darabont", 1994, ["Dave", "Amy", "Bill", "Garry", "Ian", "Neal", "Kate", "Jenny", "Zoe", "Heidi"], ["Jo"]), ("Gladiator", "Ridley Scott", 2000, ["Olga", "Neal", "Kate", "Garry"], ["Heidi", "Bill", "Sam", "Zoe"]), ("The Green Mile", "Frank Darabont", 1999, ["Kevin", "Tim", "Emma", "Heidi"], ["Kate", "Jenny", "Zoe"]), ("True Lies", "James Cameron", 1994, ["Sam", "Dave"], ["Emma", "Olga", "Jenny", "Zoe"]), ("Super 8", "J J Abrams", 2011, ["Kevin", "Tim", "Emma", "Olga", "Heidi"], ["Wally", "Dave", "Jenny", "Zoe"]), ("Minority Report", "Steven Spielberg", 2002, ["Kevin", "Kate", "Tim", "Emma", "Jenny", "Zoe"], ["Olga", "Heidi"]), ("War Horse", "Steven Spielberg", 2011, ["Garry", "Bill", "Olga", "Jo", "Wally", "Emma", "Tim", "Kate", "Zoe"], ["Heidi", "Jenny", "Sam"]), ("Silence", "Martin Scorsese", 2016, ["Wally", "Emma", "Tim", "Heidi", "Bill", "Jo"], ["Dave", "Olga"]), ("The Terminal", "Steven Spielberg", 2004, ["Kate", "Dave", "Jo", "Wally", "Emma"], ["Heidi"]), ("Star Wars: The Force Awakens", "J J Abrams", 2015, ["Emma", "Wally", "Zoe", "Kate", "Bill", "Dave", "Liz"], ["Olga", "Jo", "Neal"]), ("Hugo", "Martin Scorsese", 2011, ["Wally", "Sam"], ["Kate", "Bill", "Dave"])]

roundTo

roundTo x n = (fromIntegral (round (x * 10^n))) / 10^n

calculateRatings

calculateRatings :: [String] -> [String] -> Int
calculateRatings likes dislikes = round(fromIntegral (length likes) / fromIntegral (length likes + length dislikes) * 100)

calculateAvgRating

calculateAverageRating :: [Int] -> Float
calculateAverageRating rating = fromIntegral (round(fromIntegral ((foldr (+) 0 rating)) / fromIntegral (length rating) * 100)) / 100

Я новичок в Haskell и его синтаксис, но я знаю, что я должен, вероятно, сделать использование getAllFilmsByDirector в getAllFilmsByDirectorAvgRating однако, поскольку getAllFilmsByDirectorAvgRating берет Film тип, а не строку, Я не слишком уверен, о лучший способ, чтобы изменить код, чтобы использовать getFilmsByDirector функция.

Я также использую очень похожие списочные включения в каждую из трех и если кто имеет какие-либо идеи, как я могу сделать эти меньше повторов это было бы замечательно.



127
1
задан 19 марта 2018 в 01:03 Источник Поделиться
Комментарии
2 ответа

Не повторяйся

Ваш код страдает от влажного. Ваш Film'ы форматирования функция повторяется. Так что давайте избавиться от этой первой:

prettyFilm :: Film -> String
prettyFilm (name, director, year, likes, dislikes) =
name ++ ", " ++ director ++ ", " ++ show year ++ ", " ++ show rating
where rating = calculateRatings likes dislikes

Сейчас getAllFilmsByDirector и getAllFilmsWithHighRatings немного легче для чтения:

getAllFilmsByDirector :: [Film] -> String -> String
getAllFilmsByDirector filmsDb directorName =
unlines [prettyFilm film | film@(_, director, _, _, _) <- filmsDb
, director == directorName]

getAllFilmsWithHighRatings :: [Film] -> String
getAllFilmsWithHighRatings filmsDb =
unlines [prettyFilm film | film@(_, _, _, likes, dislikes) <- filmsDb
, calculateRatings likes dislikes >= 75]

Однако это все-таки похоже, что это из повторяющегося кода: в итоге обе функции фильтрации filmsDb и затем печатать все фильмы. Если бы у нас было getFilmsBy :: (Film -> Bool) -> [Film] -> [Film] мы могли бы написать:

getAllFilmsByDirector :: [Film] -> String -> String
getAllFilmsByDirector filmsDb directorName = unlines . map prettyFilm . getFilmsBy p $ filmsDb
where
p (_, _, director, _, _) = director == directorName

getAllFilmsWithHighRatings :: [Film] -> String
getAllFilmsWithHighRatings filmsDb = unlines . map prettyFilm . getFilmsBy p $ filmsDb
where
p (_, _, _, likes, dislikes) = calculateRatings likes dislikes >= 75

К счастью, getFilmsBy = filter.

Теперь, когда мы избавились от список осмысленностей, мы видим, что мы можем еще улучшить наши функции, если мы извлекаем unlines . map prettyFilm . filter:

showAllFilmsBy :: (Film -> Bool) -> [Film] -> String
showAllFilmsBy p = unlines . map prettyFilm . filter p

getAllFilmsByDirector :: [Film] -> String -> String
getAllFilmsByDirector filmsDb directorName = showAllFilmsBy p filmsDb
where
p (_, _, director, _, _) = director == directorName

getAllFilmsWithHighRatings :: [Film] -> String
getAllFilmsWithHighRatings filmsDb = showAllFilmsBy p filmsDb
where
p (_, _, _, likes, dislikes) = calculateRatings likes dislikes >= 75

Другие предметы комментарий


  • Некоторые ваши скобки будут лишними, например, в roundTo. Попытаться избавиться от этих.

  • Почти все функции имеют подписи типа (хорошую!), за исключением roundTo (плохо). Попробуйте добавить подписи типа все на высшем уровне Привязок.

  • Если вам действительно нужно использовать Double вместо Float.

  • Постарайтесь оставаться в исходном типе Film как можно дольше. Вы всегда можете пойти с Film для Stringно обратное не верно. Вот почему вы не могли бы повторно использовать ваш код.

3
ответ дан 19 марта 2018 в 07:03 Источник Поделиться

Попробуйте поставить настройки как параметры слева и данные параметры справа, он сочиняет лучше.

genericLength конденсируется много преобразования.

ImplicitParams позволяет мне импровизировать язык запросов, так что мне не нужно распаковать кортеж везде.

Ты уже писал roundTo и тогда вы не использовать его.

{-# LANGUAGE ImplicitParams #-}

getAllFilmsByDirector :: String -> [Film] -> String
getAllFilmsByDirector director = unlines . selectwhere ?pretty (?director == director)

getAllFilmsWithHighRatings :: [Film] -> String
getAllFilmsWithHighRatings = unlines . selectwhere ?pretty (?rating >= 0.75)

getAllFilmsByDirectorAvgRating :: String -> [Film] -> Float
getAllFilmsByDirectorAvgRating director
= roundTo 1 . liftA2 (/) sum genericLength . selectwhere ?rating (?director == director)

-- selectwhere :: Query a -> Query Bool -> [Film] -> [a]
selectwhere select cond = map (exec select) . filter (exec cond) where
-- exec :: Query a -> Film -> a
exec query (name, director, year, likes, dislikes) = let
?rating = roundTo 2 $ genericLength likes / (genericLength likes + genericLength dislikes)
?pretty = name ++ ", " ++ director ++ ", " ++ show year ++ ", " ++ show ?ratings
?director = director
in query

roundTo n x = fromIntegral (round (x * 10^n)) / 10^n

1
ответ дан 19 марта 2018 в 06:03 Источник Поделиться