2012-06-30 3 views
3

Я написал свою первую программу в Haskell сегодня. It compiles and runs successfully. И так как это не является типичным «Hello World» программы, она на самом деле делает гораздо больше, чем это, поэтому, пожалуйста, поздравляет меня: DРазница между `(Integer a) => a -> Bool` и` Integer -> Bool`?

Во всяком случае, у меня есть некоторые сомнения по поводу моего кода, и синтаксис в Haskell ,

Проблема:

Моя программа считывает целое число N из стандартного ввода, а затем, для каждого целого i в диапазоне [1,N], он печатает i, является ли простое число или нет. В настоящее время он не проверяет входную ошибку. :-)

Решение: (также сомнения/вопросы)

Чтобы решить эту проблему, я написал эту функцию для проверки простоты целого:

is_prime :: Integer -> Bool 
is_prime n = helper n 2 
     where 
      helper :: Integer -> Integer -> Bool 
      helper n i 
       | n < 2 * i = True 
       | mod n i > 0 = helper n (i+1) 
       | otherwise = False 

Он прекрасно работает. Но я сомневаюсь, что первая строка является результатом многих хитов и испытаний, поскольку то, что я читал в this tutorial, не работает, и дал эту ошибку (I предположим, что это ошибка, хотя она не говорит так):

prime.hs:9:13: 
    Type constructor `Integer' used as a class 
    In the type signature for `is_prime': 
     is_prime :: Integer a => a -> Bool 

Согласно the tutorial (который является хорошо написанный учебник, кстати), первая строка должна быть: (учебник говорит (Integral a) => a -> String, поэтому я думал, (Integer a) => a -> Bool должен работать, а.)

is_prime :: (Integer a) => a -> Bool 

которые делают не работает, и выдает указанную выше ошибку (?).

И почему это не работает? В чем разница между этой линией (которая не работает) и линией (которая работает)?


Кроме того, что идиоматический способ перебрать 1 к N? Я не вполне доволен циклом в моем коде. Пожалуйста, предложите улучшения. Вот мой код:

--read_int function 
read_int :: IO Integer 
read_int = do 
    line <- getLine 
    readIO line 

--is_prime function 
is_prime :: Integer -> Bool 
is_prime n = helper n 2 
     where 
      helper :: Integer -> Integer -> Bool 
      helper n i 
       | n < 2 * i = True 
       | mod n i > 0 = helper n (i+1) 
       | otherwise = False 

main = do 
     n <- read_int 
     dump 1 n 
     where 
      dump i x = do 
       putStrLn (show (i) ++ " is a prime? " ++ show (is_prime i)) 
       if i >= x 
        then putStrLn ("") 
        else do 
        dump (i+1) x 
+8

Вы имеете в виду 'Integral a'? – Heatsink

+0

@Heatsink: Нет. Я даже не знаю, существует ли «Интеграл». Имеет ли это? Предполагается ли это «Интеграл»? – Nawaz

+0

Вероятно, он должен быть «Интегральным». Кроме того, ваши две гиперссылки идут на один и тот же URL-адрес, является ли это намеренным? – Heatsink

ответ

13

Вы неправильно читаете учебник. Было бы сказать, тип подпись должна быть

is_prime :: (Integral a) => a -> Bool 
--  NOT Integer a 

Это разные типы:

  • Integer -> Bool
    • Это функция, которая принимает значение типа Integer и возвращает значение типа Bool ,
  • Integral a => a -> Bool
    • Это функция, которая принимает значение типа a и возвращает значение типа Bool.
    • Что такое a? Это может быть любой тип выбора вызывающего абонента, который реализует класс типа Integral, такой как Integer или Int.

(И разница между Int и Integer? Последний может представлять собой целое число от любой величины, бывшие обтекает в конце концов, подобные int с в C/Java/др.)


Идиоматический путь к циклу зависит от того, что делает ваш цикл: это либо карта, либо складка, либо фильтр.

Ваша петля в main - это карта, и поскольку вы делаете i/o в своем цикле, вам нужно использовать mapM_.

let dump i = putStrLn (show (i) ++ " is a prime? " ++ show (is_prime i)) 
in mapM_ dump [1..n] 

В то же время, ваш цикл в is_prime складка (в частности, all в данном случае):

is_prime :: Integer -> Bool 
is_prime n = all nondivisor [2 .. n `div` 2] 
     where 
      nondivisor :: Integer -> Bool 
      nondivisor i = mod n i > 0 

(А мелочью стиля, это обычная в Haskell, чтобы использовать имена, как isPrime вместо таких как is_prime.)

+0

+1. Отличный ответ. :-) – Nawaz

+1

Nitpick: 'coprime' является вводящим в заблуждение именем. Оказывается, на самом деле он называется только 'i', где результат соответствует имени, но без короткого замыкания' is_prime 18' будет вызывать 'coprime 8', который затем будет оцениваться как' True', хотя 8 и 18 aren Совсем. Назовите это 'nondivisor' или так. –

+0

@ DanielFischer Хорошая точка, исправлена. – dave4420

3
  1. Это Integral a, не Integer a. См. http://www.haskell.org/haskellwiki/Converting_numbers.

  2. map и друзья, как вы петли в Haskell.Это, как я бы переписать цикл:

    main :: IO() 
    main = do 
         n <- read_int 
         mapM_ tell_prime [1..n] 
         where tell_prime i = putStrLn (show i ++ " is a prime? " ++ show (is_prime i)) 
    
+0

+1. Спасибо за короткий ответ. – Nawaz

5

Часть 1: Если вы еще раз посмотрите на учебник, вы заметите, что это на самом деле дает тип подписи в следующих формах:

isPrime :: Integer -> Bool 
-- or 
isPrime :: Integral a => a -> Bool 
isPrime :: (Integral a) => a -> Bool -- equivalent 

Здесь Integer это название конкретного типа (имеет фактическое представление) и Integral это имя класса типов. Тип Integer является членом класса Integral.

Ограничение Integral a означает, что независимо от типа a случается, a должен быть членом Integral класса.

Часть 2: Существует множество способов написать такую ​​функцию. Ваше рекурсивное определение выглядит отлично (хотя вы можете использовать n < i * i вместо n < 2 * i, так как он быстрее).

Если вы изучаете Haskell, вы, вероятно, захотите попробовать написать его, используя функции более высокого порядка или списки. Что-то вроде:

module Main (main) where 
import Control.Monad (forM_) 

isPrime :: Integer -> Bool 
isPrime n = all (\i -> (n `rem` i) /= 0) $ takeWhile (\i -> i^2 <= n) [2..] 

main :: IO() 
main = do n <- readLn 
      forM_ [1..n] $ \i -> 
       putStrLn (show (i) ++ " is a prime? " ++ show (isPrime i)) 
+0

+1. Awesome :-) – Nawaz

+0

Ваш код дал ошибку: http://ideone.com/J2tSu ... В чем проблема? – Nawaz

+1

'import Control.Monad' :) – Vitus

Смежные вопросы