Неожиданная ошибка в хаскелле, которую я не могу понять

#haskell

Вопрос:

У меня есть такой ввод : статистика [(0,1),(1,3),(1,2)] где я хочу рассчитать некоторую информацию. Хорошо с этим, но когда я использую рекурсию для создания цикла, она отправляет мне ошибку, которую я не знаю, почему . Все,что я хочу, — это (в начале) принимать значения (x, y) и (в конце) сохранять другой список таким, какой он есть . Мой код :

 statistics :: [(Int,Int)]->(Int,Int,Int,Int,Int)
statistics [] = (0,0,0,0,0)
statistics s = help_statistics s (head s) (tail s) 0 0 0 0 0 0
                                                            
help_statistics :: [(Int,Int)]->(Int,Int)->[(Int,Int)]->Int->Int->Int->Int->Int->Int->(Int,Int,Int,Int,Int)
help_statistics s (x,y) taill counter matches total_points goal_for goal_against dif
                                                                            |counter==0 =  help_statistics s (head taill) (tail taill) (counter 1) length(s) total_points goal_for goal_against dif
                                                                            |otherwise  = (matches,total_points,goal_for,goal_against,dif)

 ERROR file:.LAB3.hs:11 - Type error in application
*** Expression     : help_statistics s (head taill) (tail taill) (counter   1) length s total_points goal_for goal_against dif
*** Term           : help_statistics
*** Type           : [(Int,Int)] -> (Int,Int) -> [(Int,Int)] -> Int -> Int -> Int -> Int -> Int -> Int -> (Int,Int,Int,Int,Int)
*** Does not match : a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k
 

Комментарии:

1. (length s) вместо length(s) .

2. @WillemVanOnsem ты потрясающий 😛 спасибо тебе !

3. Вероятно, вы захотите записать большинство этих аргументов в записи, чтобы их было легче читать. Кроме того, шаблон «go» — это хороший трюк, поэтому вам не нужно повторять все аргументы, которые не меняются при рекурсивном вызове

4. @WillemVanOnsem Могу я спросить еще кое о чем ? если я задам в качестве входных данных это [(1,5),(3,5)] , у меня был сбой соответствия шаблону : head [] как я могу это решить ?

5. Ах, да. Вы должны стараться избегать использования функций head и tail как можно больше. Вместо этого используйте сопоставление с шаблоном, например f (x:xs) = something; f [] = something else или f xs = case xs of (x:xs) -> something; [] -> something else (замените точку с запятой на новую строку, если хотите)

Ответ №1:

Позвольте мне просмотреть код и помочь вам с проблемами и сделать его более идиоматичным.

Давайте начнем с устранения проблемы length(s) -> > (length s) и уменьшения отступа, чтобы он лучше вписывался в экран.

 statistics :: [(Int,Int)]->(Int,Int,Int,Int,Int)
statistics [] = (0,0,0,0,0)
statistics s = help_statistics s (head s) (tail s) 0 0 0 0 0 0

help_statistics :: [(Int,Int)]->(Int,Int)->[(Int,Int)]->Int->Int->Int->Int->Int->Int->(Int,Int,Int,Int,Int)
help_statistics s (x,y) taill counter matches total_points goal_for goal_against dif
    |counter==0 =  help_statistics s (head taill) (tail taill) (counter 1) (length s) total_points goal_for goal_against dif
    |otherwise  = (matches,total_points,goal_for,goal_against,dif)
 

Далее мы можем заменить head и tail на соответствие шаблону:

 statistics :: [(Int,Int)]->(Int,Int,Int,Int,Int)
statistics [] = (0,0,0,0,0)
statistics s@(x:xs) = help_statistics s x xs 0 0 0 0 0 0

help_statistics :: [(Int,Int)]->(Int,Int)->[(Int,Int)]->Int->Int->Int->Int->Int->Int->(Int,Int,Int,Int,Int)
help_statistics s (x,y) (hd:tl) counter matches total_points goal_for goal_against dif
    |counter==0 =  help_statistics s hd tl (counter 1) (length s) total_points goal_for goal_against dif
    |otherwise  = (matches,total_points,goal_for,goal_against,dif)
help_statistics s (x,y) [] counter matches total_points goal_for goal_against dif
   = _ -- What should we do with an empty list?
 

Следующий шаг-ввести новый тип данных, чтобы нам не приходилось работать с миллионами аргументов и каждый раз подсчитывать, какой аргумент нам нужен:

 data Statistics = MkStatistics
  { matches      :: Int
  , total_points :: Int
  , goal_for     :: Int
  , goal_against :: Int
  , dif          :: Int
  } deriving Show

emptyStatistics :: Statistics
emptyStatistics = MkStatistics 0 0 0 0 0

statistics :: [(Int,Int)] -> Statistics
statistics [] = emptyStatistics
statistics s@(x:xs) = help_statistics s x xs 0 emptyStatistics

help_statistics :: [(Int,Int)]->(Int,Int)->[(Int,Int)]->Int-> Statistics -> Statistics
help_statistics s (x,y) (hd:tl) counter stats
    |counter==0 = let stats' = stats {matches = length s} -- Update `matches` to a new value
                  in help_statistics s hd tl (counter 1) stats'
    |otherwise  = stats
help_statistics s (x,y) [] counter stats
   = _ -- What should we do with an empty list?
 

Далее мы также можем использовать сопоставление шаблонов counter==0

 data Statistics = MkStatistics
  { matches      :: Int
  , total_points :: Int
  , goal_for     :: Int
  , goal_against :: Int
  , dif          :: Int
  } deriving Show

emptyStatistics :: Statistics
emptyStatistics = MkStatistics 0 0 0 0 0

statistics :: [(Int,Int)] -> Statistics
statistics [] = emptyStatistics
statistics s@(x:xs) = help_statistics s x xs 0 emptyStatistics

help_statistics :: [(Int,Int)]->(Int,Int)->[(Int,Int)]->Int-> Statistics -> Statistics
help_statistics s (x,y) (hd:tl) counter@0 stats
    -- When counter == 0, increase it by 1 (i.e. set it to 1)
    -- Is this really what was intended?
    = help_statistics s hd tl (counter 1) stats'
  where stats' = stats {matches = length s} -- Update `matches` to a new value
help_statistics s (x,y) (hd:tl) counter stats
   = stats -- when counter is not 0, return the stats. Was this reversed?
help_statistics s (x,y) [] counter stats
   = _ -- What should we do with an empty list?
 

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

 
data Statistics = MkStatistics
  { matches      :: Int
  , total_points :: Int
  , goal_for     :: Int
  , goal_against :: Int
  , dif          :: Int
  } deriving Show

emptyStatistics :: Statistics
emptyStatistics = MkStatistics 0 0 0 0 0

statistics :: [(Int,Int)] -> Statistics
statistics s = help_statistics s s 0 emptyStatistics

help_statistics :: [(Int,Int)] -> [(Int,Int)] -> Int -> Statistics -> Statistics
help_statistics s ((x,y):tl) counter@0 stats
    -- When counter == 0, increase it by 1 (i.e. set it to 1)
    -- Is this really what was intended?
    = help_statistics s tl (counter 1) stats'
  where stats' = stats {matches = length s} -- Update `matches` to a new value
help_statistics s (_:_) counter stats
   = stats -- when counter is not 0, return the stats. Was this reversed?
help_statistics s [] counter stats
   = stats -- What should we do with an empty list?