Ошибка, пытающаяся назвать putStrLn в функции

Я пытаюсь поместить 'распечатать' вызов функции в функцию Haskell.

(простое сообщение отладки).

Ниже мое закодированное сообщение и сообщение об ошибке из компилятора (GHC 6.10).

Я не вполне понимаю, почему это смешивает putStr звоните и пустой массив.

Пустой массив является возвращаемым значением для того особого случая (печать обмениваются сообщениями, на самом деле просто тупик на данный момент).

Какая-либо идея, почему это, не работает?

Мой код:

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
        if(counter >  quotient)
            then do
                putStrLn ("factorList is  : " ++ show quotient)  (*** Line 10***)
                []
        else if(isAFactor num counter)
            then [counter] ++ [quotient] ++ findFactors (counter + 1) num
        else
            findFactors (counter + 1) num

Ошибка от ghc

    test.hs:10:4:
    Couldn't match expected type `[a] -> [Integer]'
           against inferred type `IO ()'
    In the expression:
        putStrLn ("factorList is  : " ++ show quotient) []
    In the expression:
        do putStrLn ("factorList is  : " ++ show quotient) []
    In the expression:
        if (counter > quotient) then
            do putStrLn ("factorList is  : " ++ show quotient) []
        else
            if (isAFactor num counter) then
                  [counter] ++ [quotient] ++ findFactors (counter + 1) num
            else
                findFactors (counter + 1) num
5
задан Micha Wiedenmann 27 August 2019 в 13:03
поделиться

4 ответа

Важно помнить, что Haskell является чистым функциональным языком. Это означает, что функциям не позволяют иметь побочные эффекты вообще, включая печать сообщений отладки на Ваш экран.

Однако возможно повредить эту чистоту, и это может быть полезно в отладке. Смотрите на Отладку модуля. Трассировка. Там Вы найдете функцию trace :: String -> a -> a. Можно использовать его в коде как это:

import Debug.Trace

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
        if(counter >  quotient)
                then trace ("factorList is: " ++ show quotient) [] 
        else if(isAFactor num counter)
            then [counter] ++ [quotient] ++ findFactors (counter + 1) num
        else
            findFactors (counter + 1) num

Как предложенные комментарии:

Haskell является также ленивым языком. Выражение не оценено, прежде чем результат будет на самом деле необходим. Используя трассировку функция может немного сбивать с толку в ленивой установке, потому что не всегда легко понять, когда сообщение трассировки печатается для экранирования (если она печатается вообще).

Поскольку haskell является совсем другим видом языка, возможно, лучше попытаться разработать программы одинаково другим способом. Попытайтесь рассуждать о своих функциях вместо использования trace и подобные "нечистые" конструкции. Учитесь использовать в своих интересах haskells мощную систему типов и использование (например), QuickCheck для тестирования функции, после того как это передало средство проверки типа.

21
ответ дан 18 December 2019 в 05:20
поделиться

Сообщение Jonas касается Вашего вопроса вполне прилично, таким образом, я дам Вам, идиоматическое переписывает Вашей функции findFactors. Я нашел это полезным для меня, когда я сначала учился.

Таким образом, Вы хотите найти все факторы данного числа n путем рассмотрения каждого числа от 1 до n/2, проверка, чтобы видеть, является ли это фактор n и создание списка тех, которые являются.

Ваша версия (с минимальными модификациями, чтобы заставить это работать):

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
        if(counter >  quotient)
            then []
        else if(isAFactor num counter)
            then [counter] ++ findFactors (counter + 1) num
        else
            findFactors (counter + 1) num

Несколько изменений форматирования для создания этого более читаемым:

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num
  | counter > div num 2 = []
  | otherwise = if num `isAFactor` counter 
                then counter:findFactors (counter+1) num
                else findFactors (counter + 1) num

Это прекрасно, но это - меньше, чем идеал парой способов. Во-первых, это повторно вычисляет частное каждый раз findFactors назван, который является n/2 подразделения (хотя ghc -O2 кажется, понимает это и вычисляет его только однажды). Во-вторых, это является довольно раздражающим для контакта с той переменной счетчика везде. В-третьих, это все еще довольно обязательно.

Другой способ посмотреть на проблему состоял бы в том, чтобы взять список целых чисел от 1 до n/2 и фильтр для просто тех, которые являются факторами n. Это переводит довольно непосредственно в Haskell:

findFactors :: Integer -> [Integer]
findFactors num = filter (isAFactor num) [1..(num `div` 2)]

Могло бы стать неожиданностью находить, что это имеет те же рабочие характеристики как вышеупомянутая версия. Haskell не должен выделять память для всего списка до n/2 сразу, это может просто сгенерировать каждое значение, поскольку это необходимо.

9
ответ дан 18 December 2019 в 05:20
поделиться

Другие сделали хорошее задание объяснения Вашего кода, таким образом позвольте мне помочь Вам декодировать сообщение об ошибке.

Couldn't match expected type `[a] -> [Integer]'
       against inferred type `IO ()'
In the expression:
    putStrLn ("factorList is  : " ++ show quotient) []

Я пропал все другие "В выражении" части; они просто показывают все больше контекста включения.

Все в Haskell - выражение, поэтому все имеет тип. Это включает что-то как "putStrLn". Если Вы вводите ": t putStrLn" в GHCi Вы будете видеть, что он отвечает:

putStrLn :: String -> IO ()

Что означает, что putStrLn является функцией, которая берет строку и возвращает "действие IO", которое в этом случае является действием помещения сообщения на экране. В Вашем коде Вы дали "putStrLn" строку, таким образом, компилятор вывел, что выражение "putStrLn (материал)" имело тип "IO ()". Это - "выведенный тип" часть сообщения ошибки компилятора.

Между тем компилятор также делал вывод типа в другом направлении с внешней стороны в. Среди других вещей это заметило, что это "putStrLn (материал)" выражение, казалось, было применено к пустому списку, который имеет тип"" (т.е. список чего-то, мы не знаем что). Кроме того, результат целого выражения должен иметь тип" [Целое число]". Поэтому выражение "putStrLn (материал)" должно быть функцией для превращения" []" в список целых чисел, тип которых записан"-> [Целое число]". Это - "ожидаемый тип" часть сообщения об ошибке.

В этой точке компилятор пришел к заключению, что это не могло подойти эти два типа, таким образом, это сообщило об ошибке.

"Не мог соответствовать ожидаемому типу, 'Foo' против выведенного типа 'Панель'" является, вероятно, самым общим сообщением об ошибке, которое Вы получаете при попытке скомпилировать Haskell, таким образом, который стоит попытаться считать его и понять его. Посмотрите на выведенный тип и попытку выяснить, какая часть заключенного в кавычки выражения имеет тот тип. Затем попытайтесь выяснить, почему компилятор ожидал что-то еще путем рассмотрения окружающего кода.

8
ответ дан 18 December 2019 в 05:20
поделиться

Обновленный с разъяснениями

Проблема состоит в том, что IO в Haskell одноместен, начало блока do синтаксический сахар для объединения одноместных выражений (иногда названный операторами) с одноместными операторами. В этом случае рассматриваемая монада является монадой IO, как может быть выведен от вызова до putStrLn. [] во второй строке do блок на самом деле не, значение целого действительно блокируется, скорее это интерпретируется как последний аргумент putStrLn; не, что это принимает второй аргумент, но компилятор даже не переходит к сути дела понимания этого, потому что это завершается ранее с ошибкой типа, которую Вы заключили в кавычки. Для создания той строки, команда, которую необходимо было бы поместить, например, возвращается, другая одноместная функция, перед ним (т.е. return []). Я не говорю, тем не менее, что это помогло бы Вам решить свою проблему.

Ошибка типа происходит от того, что одноместные выражения IO всегда имеют тип IO _; в Вашем случае, do блок имеет этот тип также, который является очевидно несовместимым с [Integer], тип Вы указали в подписи.

В целом, потому что Haskell является чистым функциональным языком с одноместным IO, после того как Вы в монаде IO, нет никакого выхода из него, это заразно. т.е. если функция имеет a do блок с операциями IO в нем, его подпись будет обязательно содержать IO _ введите, и так будет подпись всех других функций, вызывающих эту функцию, и т.д. (Другие монады действительно обеспечивают функции "выхода", но монада IO не делает.)

3
ответ дан 18 December 2019 в 05:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: