Film database in Haskell

情到浓时终转凉″ 提交于 2019-12-24 12:59:30

问题


Currently trying to solve 2 main questions in my haskell program.

  1. display all films that a given user is a fan of
  2. display all the films of a given actor that were released during a particular period (i.e. between a given start year and end year)

This is the sample database I am currently using:

type Title = String
type Cast = String
type Year = Int
type Fans = String

type Film = (Title, [Cast], Year, [Fans])
type Database = [Film]

testDatabase :: Database
testDatabase = [("Casino Royale", ["Daniel Craig", "Eva Green", "Judi Dench"], 2006,    ["Garry", "Dave", "Zoe", "Kevin", "Emma"]),
("Cowboys & Aliens", ["Harrison Ford", "Daniel Craig", "Olivia Wilde"], 2011, ["Bill", "Jo", "Garry", "Kevin", "Olga", "Liz"]),     
    ("Catch Me If You Can", ["Leonardo DiCaprio", "Tom Hanks"], 2002, ["Zoe", "Heidi", "Jo", "Emma", "Liz", "Sam", "Olga", "Kevin", "Tim"])]    

The database is much bigger but for space reasons I omitted some of this.

  • How do I now create the functions required to answer the 2 questions above using this database?

回答1:


Here's a very short answer to your first question. This looks like homework, so you should try to solve the second by yourself!

fan y = map (\(a,_,_,_) -> a) $ filter (\(_,_,_,a) -> elem y a) testDatabase

The important parts are:

elem tests whether y is a member of list a - i.e. whether the film contains the user in its list of fans.

filter takes a predicate and a list, and returns only the items in that list satisfying the predicate.

map takes a function and a list, and applies that function over the list. This is used to extract only the title of the film.

You should be able to use a similar approach to answer the second question.




回答2:


This should work:

type Title = String
type Actor = String
type Cast = [Actor]
type Year = Int
type Fan = String
type Fans = [Fan]
type Period = (Year, Year)
type Film = (Title, Cast, Year, Fans)
type Database = [Film]

testDatabase :: Database
testDatabase = [("Casino Royale", ["Daniel Craig", "Eva Green", "Judi Dench"], 2006, ["Garry", "Dave", "Zoe", "Kevin", "Emma"]),
                ("Cowboys & Aliens", ["Harrison Ford", "Daniel Craig", "Olivia Wilde"], 2011, ["Bill", "Jo", "Garry", "Kevin", "Olga", "Liz"]),     
                ("Catch Me If You Can", ["Leonardo DiCaprio", "Tom Hanks"], 2002, ["Zoe", "Heidi", "Jo", "Emma", "Liz", "Sam", "Olga", "Kevin", "Tim"])]

inCast :: Actor -> Film -> Bool
inCast givenActor (_, cast, _, _) = any (\actor -> actor == givenActor) cast

inPeriod :: Period -> Film -> Bool
inPeriod (periodStart, periodEnd) (_, _, year, _) = periodStart <= year && year <= periodEnd

inCastAndPeriod :: Actor -> Period -> Film -> Bool
inCastAndPeriod actor period film = inCast actor film && inPeriod period film

isFan :: Fan -> Film -> Bool
isFan givenFan (_, _, _, fans) = any (\fan -> fan == givenFan) fans

allFilmsThatAGivenUserIsAFanOf :: Fan -> [Film]
allFilmsThatAGivenUserIsAFanOf givenFan = filter (isFan givenFan) testDatabase

allTheFilmsOfAGivenActorThatWereReleasedDuringAParticularPeriod :: Actor -> Period -> [Film]
allTheFilmsOfAGivenActorThatWereReleasedDuringAParticularPeriod givenActor givenPeriod = filter (inCastAndPeriod givenActor givenPeriod) testDatabase

Good luck!




回答3:


If you declare your Film type as a record, you will get field accessors for free, which will make the filters easier to read (also, isn't Fans a single Fan?):

type Title = String
type Cast = String
type Year = Int
type Fan = String

data Film = Film { filmTitle :: Title
                 , filmCast  :: [Cast]
                 , filmYear  :: Year
                 , filmFans  :: [Fan]
                 }

type Database = [Film]

Your 1st problem states that you want the Films of which a given user is a Fan (not the titles of the Films):

fanOfFilms :: Fan -> Database -> [Film]
fanOfFilms fan = filter (elem fan . filmFans)

Your 2nd problem can be solved in the same manner, but the predicate becomes more complicated:

periodActorOfFilms :: Cast -> Year -> Year -> Database -> [Film]
periodActorOfFilms actor startYear endYear =
    filter $ \film -> and [ actor `elem` filmCast film
                          , startYear <= filmYear film
                          , endYear >= filmYear film
                          ]



回答4:


This solution works if you use the following type declarations. Algebraic type are much better to implement such functions.

-- Types
type Title   = String
type Actor   = String
type Year    = Int
type Fan     = String

-- Film type
data Film    = Film Title [Actor] Year [Fan]
                deriving (Eq,Ord,Show,Read)

-- converts a list of strings to string through recursion and pattern matching.
displayListAsString :: [String] -> String
displayListAsString [] = ""
displayListAsString (x:[]) = x ++ displayListAsString []
displayListAsString (x:xs) = x ++ ", " ++ displayListAsString xs

-- Give all films that a particular user is a fan of.
isFanOfMovies :: Fan -> [Film] -> String
isFanOfMovies fanName [] = "No Database provided."
isFanOfMovies fanName movieDB = moviesAsString $ isFanOf fanName movieDB

-- filters through the database to find the films which a particular user is a fan of.
isFanOf :: Fan -> [Film] -> [Film]
isFanOf fanName = filter (\(Film _ _ _ fans) -> elem fanName fans)

-- displays a movie as well formatted string
movieAsString :: Film -> String
movieAsString (Film title cast year fans) = "\nTitle: " ++ title ++ "\n Cast: " ++ 
(displayListAsString cast) ++ "\n Year: " ++ show year ++ 
"\n Fans: " ++ show (length fans)

-- Gives all films in the database (if database passed through directly)
moviesAsString :: [Film] -> String
moviesAsString movieDB = (unlines.map movieAsString) movieDB

As an extra function, you use the following to first check if the fan exists and then if the value "" is returned, then the system will proceed with retrieving the films. But this is for when you want to implement User Interface.

fanFilms (fanName, database) = do
let fan = fanExists fanName database
    if fan == ""
        then do
            putStrLn "\nDisplaying all the films you're a fan of below:"
            putStrLn $ isFanOfMovies fanName database
            return (fanName, database)
        else do
            putStrLn "No Results Found.\n"
            putStrLn $ fanExists fanName database
            return (fanName, database)

-- This can be used to check if the fan exists.
fanExists :: Fan -> [Film] -> String
fanExists fanName movieDB
    | isFanOf fanName movieDB == []     = "No movies you are fan of."
    | otherwise                         = ""

It's a year late but hopefully still helpful with all you mathfun lot ;)



来源:https://stackoverflow.com/questions/15789061/film-database-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!