问题
Currently trying to solve 2 main questions in my haskell program.
- display all films that a given user is a fan of
- 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 Film
s of which a given user is a Fan
(not the titles of the Film
s):
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