调用函数时haskell i / o putstrln错误

时间:2014-03-13 14:18:50

标签: function haskell io demo

我创建了一个电影数据库和与数据库相关的功能。

我现在正在创建一个演示函数,它给出了在ghci中按下数字时执行的特定函数的结果。例如,当键入demo 2时,它会显示数据库中的所有电影。

我已经设法创建了大多数演示功能,但是我遇到了其中3个问题,并且一直显示错误。我已经评论了哪些不起作用,需要帮助了解问题所在。

我已经使用下面的演示功能包含了我创建的所有功能。

import Data.List 
import Text.Printf
import Data.Ord
import Data.Char

type Rating = (String, Int)
type Title = String
type Director = String
type Year = Int
type Film = (Title, Director, Year,[Rating])

testDatabase :: [Film]
testDatabase = [("Blade Runner","Ridley Scott",1982,[("Amy",6), ("Bill",9), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",5), ("Megan",4)]),
                ("The Fly","David Cronenberg",1986,[("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",5)]),
                ("Psycho","Alfred Hitchcock",1960,[("Bill",4), ("Jo",4), ("Garry",8), ("Kevin",7), ("Olga",8), ("Liz",10), ("Ian",9)]),
                ("Body Of Lies","Ridley Scott",2008,[("Sam",3), ("Neal",7), ("Kevin",2), ("Chris",5), ("Olga",6)]),
                ("Avatar","James Cameron",2009,[("Olga",2), ("Wally",8), ("Megan",9), ("Tim",5), ("Zoe",8), ("Emma",3)]),
                ("Titanic","James Cameron",1997,[("Zoe",7), ("Amy",2), ("Emma",5), ("Heidi",3), ("Jo",8), ("Megan",5), ("Olga",7), ("Tim",10)]),
                ("The Departed","Martin Scorsese",2006,[("Heidi",2), ("Jo",8), ("Megan",5), ("Tim",2), ("Fred",5)]),
                ("Aliens","Ridley Scott",1986,[("Fred",8), ("Dave",6), ("Amy",10), ("Bill",7), ("Wally",2), ("Zoe",5)]),
                ("Prometheus","Ridley Scott",2012,[("Garry",3), ("Chris",4), ("Emma",5), ("Bill",1), ("Dave",3)]),
                ("E.T. The Extra-Terrestrial","Steven Spielberg",1982,[("Ian",7), ("Amy",2), ("Emma",7), ("Sam",8), ("Wally",5), ("Zoe",6)]),
                ("The Birds","Alfred Hitchcock",1963,[("Garry",7), ("Kevin",9), ("Olga",4), ("Tim",7), ("Wally",3)]),
                ("Goodfellas","Martin Scorsese",1990,[("Emma",7), ("Sam",9), ("Wally",5), ("Dave",3)]),
                ("The Shawshank Redemption","Frank Darabont",1994,[("Jo",8), ("Sam",10), ("Zoe",3), ("Dave",7), ("Emma",3), ("Garry",10), ("Kevin",7)]),
                ("Gladiator","Ridley Scott",2000,[("Garry",7), ("Ian",4), ("Neal",6), ("Wally",3), ("Emma",4)]),
                ("The Green Mile","Frank Darabont",1999,[("Sam",3), ("Zoe",4), ("Dave",8), ("Wally",5), ("Jo",5)]),
                ("True Lies","James Cameron",1994,[("Dave",3), ("Kevin",4), ("Jo",0)]),
                ("Minority Report","Steven Spielberg",2002,[("Dave",5), ("Garry",6), ("Megan",2), ("Sam",7), ("Wally",8)]),
                ("The Wolf of Wall Street","Martin Scorsese",2013,[("Dave",6), ("Garry",6), ("Megan",0), ("Sam",4)]),
                ("War Horse","Steven Spielberg",2011,[("Dave",6), ("Garry",6), ("Megan",3), ("Sam",7), ("Wally",8), ("Zoe",8)]),
                ("Lincoln","Steven Spielberg",2012,[("Ian",3), ("Sam",7), ("Wally",3), ("Zoe",4), ("Liz",7), ("Megan",4)]),
                ("Vertigo","Alfred Hitchcock",1958,[("Bill",7), ("Emma",5), ("Zoe",9), ("Olga",6), ("Tim",10)]),
                ("The Terminal","Steven Spielberg",2004,[("Olga",3), ("Heidi",8), ("Bill",2), ("Sam",6), ("Garry",8)]),
                ("Jaws","Steven Spielberg",1975,[("Fred",3), ("Garry",0), ("Jo",3), ("Neal",9), ("Emma",7)]),
                ("Hugo","Martin Scorsese",2011,[("Sam",4), ("Wally",3), ("Zoe",4), ("Liz",7)])] 

------------------------------------------------------------
-----------------FUNCTIONAL CODE----------------------------
------------------------------------------------------------                

--when adding need to be addFilm string string int and the list name called testDatabase 
addFilm :: String -> String -> Int -> [Film] -> [Film]
addFilm title director year database = (title, director, year, [])  : database  

--Some functions needed later on:
averageFilmRating :: [(String,Int)] -> Float
averageFilmRating ratings
     = (fromIntegral(sum $ map snd ratings)) / (fromIntegral(length ratings))

--Formats the films for decimal, gives average rating of films instead of all users ratings.
formatFilmOutput :: Film -> String
formatFilmOutput (title, director, year, rating)
     = printf "%s by %s. Year: %d, Average Rating: %.1f" (title) (director) (year) (averageFilmRating rating)    

--Shows all films in the database    
displayAllFilm :: [String]
displayAllFilm = map formatFilmOutput testDatabase


--Shows films by director name
displayByDirector :: String -> [Film]
displayByDirector name
     =  filter(\(_,director,_,_) -> director == name) testDatabase

--Gives the average of directors films  
directorAverage :: String -> Float
directorAverage dir
     = averageFilmRating [rating | (title, director, year, ratings) <- displayByDirector dir, rating <- ratings]     

--These two functions give the films rated of average 6 or over  
filmsRated :: Int -> [Film]
filmsRated rating
         = filter(\(_,_,_,a) -> averageFilmRating a >= fromIntegral rating) testDatabase 

filmsaveragesix = filmsRated 6  

--Shows what films the user has rated.
userRatedFilms :: String -> [Film]
userRatedFilms username
     = filter ((username `elem`) . (\(_,_,_,xs) -> map fst xs)) testDatabase

-- Allows user to rate or re-rate film.
databaseNoFilm:: [Film] -> Title -> [Film]
databaseNoFilm database t = [(title, director, year, ratings) | (title, director, year, ratings) <- database, title /= t]
rateFilm :: [Film] -> Title -> Rating -> [Film]
rateFilm database findtitle (u, r) = databaseNoFilm database findtitle ++ [(title,director,year,(u, r):[(username,rtg) | (username,rtg) <- ratings, username /= u]) | (title, director, year, ratings) <- database, title == findtitle] 

--Displays films by year in descending order of rating
filmsByYear :: Int -> [Film]
filmsByYear year = sortFilms $ filter(\(_,_,yr,_) -> yr >= year) testDatabase

sortFilms :: [Film] -> [Film]
sortFilms = sortBy $ flip $ comparing averageFilmRating'
  where
    averageFilmRating' (_,_,_,rs) = averageFilmRating rs

------------------------------------------------------------
-----------------DEMO FUNCTION------------------------------
------------------------------------------------------------
demo :: Int -> IO ()
demo choice = do
    case choice of
        -- 1 -> do
            -- putStrLn addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase
        2 -> do
            putStrLn (unlines displayAllFilm)
        3 -> do
            putStrLn (unlines (map formatFilmOutput(displayByDirector "James Cameron")))
        4 -> do
            putStrLn (unlines (map formatFilmOutput(filmsaveragesix)))
            -- Get the director average of James Cameron
        -- 5 -> do
            -- putStrLn (directorAverage "James Cameron")
        6 -> do
            putStrLn (unlines (map formatFilmOutput(userRatedFilms "Zoe")))
        -- all films after Zoe rates "Jaws" 8   
        -- 7 -> do
            -- putStrLn rateFilm testDatabase "Jaws" ("Zoe", 8)
        -- 77 all films after Zoe rates "Vertigo" 3
        8 -> do
            putStrLn (unlines (map formatFilmOutput(filmsByYear 2009)))

此处的问题与已注释掉的演示功能有关。取消注释并运行时,演示1的错误与:

有关
Couldn't match type `String -> String -> Int -> [Film] -> [Film]'
                  with `[Char]'
    Expected type: String
      Actual type: String -> String -> Int -> [Film] -> [Film]
    In the first argument of `putStrLn', namely `addFilm'
    In a stmt of a 'do' block:
      putStrLn addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase
    In the expression:
      do { putStrLn
             addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase }

其余评论演示函数

也是类似的错误

1 个答案:

答案 0 :(得分:1)

您遇到的问题与

有关
putStrLn addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase

对于编译器,这看起来就像你试图将5个参数应用于putStrLn,这是一个只需要1个参数的函数,因此编译器错误。如果你要做

putStrLn (addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase)

或等效(和更漂亮)

putStrLn $ addFilm "Gravity" "Alfonso Cuaron" 2013 testDatabase

然后,您将4个参数应用于addFilm,然后将该结果应用于putStrLn。这仍然无法编译,因为addFilm会返回Film的列表,而不是String putStrLn所期望的列表。您可以改为使用print,其定义为

print :: Show a => a -> IO ()
print x = putStrLn $ show x

或者您可以使用formatFilmOutput功能:

putStrLn $ unlines $ map formatFilmOutput $ addFilm "Gravity" ...

unlines将字符串列表转换为由新行连接的单个字符串。

如果您想知道$运算符是什么,它的字面定义为

($) :: (a -> b) -> a -> b
($) = id
infixr 0 $

定义的重要部分是infixr 0 $行。这意味着它与0的固定性相关联,这是最低优先级。函数应用程序的优先级为9,这意味着它总是优先于运算符,所以

add1 x * 2

始终

相同
(add1 x) * 2

$运算符只是括号的替代。您可以将其用作

f $ g $ h $ i $ j $ k $ l $ m x
-- f through m are functions of one argument

这意味着将x应用于m,然后将其应用于l,然后应用于k,然后应用于j,依此类推,或者您可以把它写成

f (g (h (i (j (k (l (m x)))))))

平衡括号是不好玩的。这仅适用于将 last 参数应用于函数,而不是任何中间函数。所以

add = (+)

add $ 1 + 2 $ 3 + 4

将无法正常工作,解析为

add (1 + 2 (3 + 4))
add (3 (7))
add (3 7)

这没有意义。