[Haskell-beginners] movie database

Daniel Fischer daniel.is.fischer at web.de
Fri May 7 13:55:35 EDT 2010


On Friday 07 May 2010 18:55:57, Payan Peshraw wrote:
> Hi,
>
>
>
> I have got a database with movies added into it. What Im basically
> coding is:
>
>
>
> - add a new film to the database
> - display all the films
> - display all the films by a given director
> - display a list of user ratings (together with the users' names) for a
> given film - show the average overall rating for a given director
> - allow a given user to rate (or re-rate) a film (note that only the
> latest rating from the user should remain recorded) - display all the
> films from a given year, sorted in descending order of overall rating
>
> Now I want to allow a given user to rate or be able to re-rate a film.
> But the latest rating from the user should remain recorder tho..and also
> I want to display all the films that a given user likes (i.e. the films
> to which she has given a positive-valued rating). I have written the
> code for the user liking the film but it doesn't seem to work and i'm
> not sure what to do with that one eiher.
>
> Below is the coding that I've written so far, but i'm stuck on the last
> bit which I can't seem to code.
>
>
> type Director = String
> type Title = String
> type Year = Int
> type Mark = Int
> type Rating = (String, Int)
>
> type Film = (Title, Director, Year, [Rating])

Consider making Film an ADT,

data Film
    = Film
    { title :: Title
    , director :: Director
    , year :: Year
    , ratings :: [Rating]
    }

, maybe also Rating:

type User = String

data Rating
    = Rating
    { user :: User
    , rating :: Mark
    }

> type Database = [Film]
>
> testDatabase :: Database
> testDatabase = [("Casino Royale", "Martin Campbell", 2006,
> [("Garry",-8),("Dave", 0)]), ("Blade Runner", "Ridley Scott", 1982,
> [("Amy",5)]), ("The Fly", "David Cronenberg", 1986, [("Fred",7)])
>     ]
>
> addFilm :: Database -> Title -> Director -> Year -> Database
> addFilm  database title director year
>          = (title, director, year, []): database
>
> displayFilms :: Database ->[(Title, Director, Year, Mark)]
> displayFilms [] = []
> displayFilms ((i, j, k, l):xs)
>
>                |l == [] = (i, j, k, 0) : displayFilms xs
>                |otherwise = (i, j, k, overallRating l) : displayFilms xs
>
> overallRating :: [Rating] -> Int
> overallRating rating = div (sum [j | (_,j) <- rating]) (length rating)

That gives an error for an empty list.

>
> filmsByDirector :: Database -> Director -> Database
> filmsByDirector [] filmDirector = []
> filmsByDirector((i, j, k, l):xs) filmDirector
>
>                  |j == filmDirector = (i, j, k, l) : filmsByDirector xs
>                  | filmDirector otherwise = filmsByDirector xs
>                  | filmDirector
>
> filmRating :: Database -> Title -> [Rating]
> filmRating [] filmRat = []
> filmRating ((i,j, k, l):xs) filmRat
>
>                 |i == filmRat = (l ++ filmRating xs filmRat)
>                 |otherwise = filmRating xs filmRat
>
> THIS DOESNT SEEM TO WORK?
> {-
> filmsLiked :: Database -> String -> Database
> filmsLiked [] user = []
> filmsLiked ((i,j,k,l):xs) user
>
>                  | == user = i ++ filmsLiked xs user

That should of course be "i : filmsLiked xs user" if the function should 
return the list of titles of the films the user likes,
"(i,j,k,l) : filmsLiked xs user" if it should return a Database (as the 
type signature says in accordance with the behaviour of filmsByDirector).

>                  |otherwise = filmsLiked xs user
>
> person :: [Rating] -> String -> Bool
> person [] userName = False
> person ((n, s):xs) userName
>
>                     |n == userName && s > 0 = True
>                     |otherwise = person xs userName
>
> -}

You have the function which tells you whether a user liked a film (more 
precisely, whether a list of Ratings contains a positive rating from a 
user). The name of that function is very inappropriate, though.

Use that to write

likes :: User -> Film -> Bool
likes user (t,d,y,rs) = ???

And then use that in filmsLiked,

filmsLiked :: Database -> User -> Database
filmsLiked (film : rest) user
    | user `likes` film = ???
    | otherwise = filmsLiked rest user
filmsLiked [] _ = []

>
> averageDirector :: Database -> Director -> Int
> averageDirector [] directorRating = 0
> averageDirector ((i,j,k,l):xs) directorRating
>
>                     |j == directorRating = (overallRating l) +
>                     | (averageDirector xs directorRating) otherwise =
>                     | averageDirector xs directorRating
>
> filmFromYear :: Database -> Year -> Database
> filmFromYear [] filmYear = []
> filmFromYear ((i,j,k,l):xs) filmYear
>
>                     |k == filmYear = (i,j,k,l) : filmFromYear xs
>                     | filmYear otherwise = filmFromYear xs filmYear
>
> rateFilm :: [Rating] -> Rating -> [Rating]
> rateFilm [] rating = rating:[]
> rateFilm ((user, rating):xs) (newUser, newRating)
>
>                     |newUser == user = ((newUser, newRating):xs)
>                     |otherwise = (user, rating) : (rateFilm xs (newUser,
>                     | newRating))
>
> Stuck on this bit..
> {-
> changeFilm :: Database -> Title -> [Rating] -> Database
> changeFilm .........
> -}

What is that supposed to do?

>
>
>
> Any help is appreciated..thank you!

Generally,

- the use of i, j, k, l as parameter names is bad. If you want to use one-
letter parameters, pick the initials of their meaning, (t, d, y, r) is in 
this context much better. (title, director, year, ratings) still better.

- the argument order of your functions is irritating. It would be more 
natural to have the Database as the last argument.

- the code could be much improved by
  * using as-patterns, e.g.
    filmsByDirector (film@(_,d,_,_) : rest) dir
        | d == dir = film : filmsByDirector rest dir
        | otherwise = filmsByDirector rest dir
  * using functions like filter, e.g.
    filmsByDirector db dir = filter (dir `isDirector`) db
        where
         isDirector name (_,d,_,_) = name == d
  * using list-comprehensions, e.g.
    filmsByDirector db dir = [film | film@(_,d,_,_) <- db, dir == d]



More information about the Beginners mailing list