Фильтровать список списков

#list #haskell #nested-lists

#Список #haskell #вложенные списки

Вопрос:

Я очень новичок в Haskell, только начинаю его изучать. Для начала я использую учебное пособие «Изучите Хаскелл для великого блага!» и увидел пример решения проблемы «3n 1»:

 chain :: (Integral a) => a -> [a]  
chain 1 = [1]  
chain n  
    | even n =  n:chain (n `div` 2)  
    | odd n  =  n:chain (n*3   1)  

numLongChains :: Int  
numLongChains = length (filter isLong (map chain [1..100]))  
    where isLong xs = length xs > 15  
  

итак, numLongChains подсчитывает все цепочки длиной более 15 шагов для всех чисел от 1 до 100.

Теперь я хочу свой собственный:

 numLongChains' :: [Int]  
numLongChains' = filter isLong (map chain [1..100])
    where isLong xs = length xs > 15    
  

итак, теперь я хочу не считать эти цепочки, а возвращать отфильтрованный список с этими цепочками.
Но теперь я получаю ошибку при компиляции:

 Couldn't match expected type `Int' with actual type `[a0]'
Expected type: Int -> Bool
  Actual type: [a0] -> Bool
In the first argument of `filter', namely `isLong'
In the expression: filter isLong (map chain [1 .. 100])
  

В чем может быть проблема?

Ответ №1:

Сигнатура типа numLongChains , вероятно, неверна. В зависимости от того, что вы хотите сделать, требуется одно из следующих действий:

  • Вы просто хотите посчитать эти цепочки, ваша функция numLongChains , очевидно, должна вернуть число, изменить первую строку на length $ filter isLong (map chain [1..100]) и тип на Int
  • Вы хотите вернуть список длин длинных цепочек. В этом случае сигнатура типа в порядке, но вам нужно вернуть длину. Я бы посоветовал вам рассчитать длину перед фильтрацией и отфильтровать ее. Тело функции становится filter (>15) (map (length . chain) [1..100]) .
  • Вы хотите вернуть все цепочки, длина которых превышает 15 символов. Просто измените подпись на [[Int]] (список цепочек (списков) из Int s), и все в порядке.

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

1. Спасибо! На самом деле это моя ошибка, использование [[Int]] решило проблему

Ответ №2:

FUZxxl прав. Вы захотите изменить сигнатуру типа вашей функции на [[Int]] . Поскольку вы фильтруете список списков и выбираете только те, которые являются достаточно длинными, вы вернете списки списков.

Одно замечание о чтении отладчика / ошибок времени компиляции Haskell. Эта ошибка может показаться странной. В нем говорится, что у вас было [a0] -> Bool , но вы ожидали Int -> Bool . Это связано с тем, что проверка типов предполагает, что из подписи вашей функции numLongChains вам понадобится функция фильтра, которая проверяет целые числа и возвращает список допустимых. Единственный способ отфильтровать список и получить [Int] обратно — это иметь функцию, которая принимает Int s и возвращает Bool s (Int -> Bool) . Вместо этого он видит функцию, которая проверяет длину. Длина принимает список, поэтому он предполагает, что вы написали функцию, которая проверяет списки. ([a0] -> Bool) . Иногда средство проверки не так дружелюбно, как хотелось бы, но если вы посмотрите достаточно внимательно, вы увидите, что в 9 случаях из 10 трудно расшифруемая ошибка является результатом таких предположений.

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

1. Эрик, тебе тоже спасибо, но все, что я понимаю, это то, что мне сейчас слишком сложно 🙂 А о вредности неявного преобразования я знал из императивных языков