4

我今天用 Haskell 编写了我的第一个程序。它编译并成功运行。而且由于它不是典型的“Hello World”程序,它实际上做的远不止这些,所以请恭喜我:D

无论如何,我对我的代码和 Haskell 中的语法毫无疑问。

问题:

我的程序从标准输入中读取一个整数N,然后,对于irange 中的每个整数[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

它工作得很好。但我怀疑第一行是许多尝试的结果,因为我在本教程中阅读的内容不起作用,并给出了这个错误(我这是一个错误,虽然它没有这么说):

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

根据教程(顺便说一句,这是一个写得很好的教程),第一行应该是:(教程说(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
4

3 回答 3

13

你误读了教程。它会说类型签名应该是

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

这些是不同的类型:

  • Integer -> Bool
    • 这是一个接受类型值Integer并返回类型值的函数Bool
  • Integral a => a -> Bool
    • 这是一个接受类型值a并返回类型值的函数Bool
    • 是什么a?它可以是调用者选择的任何实现Integral类型类的类型,例如Integeror Int

Int(和之间的区别Integer?后者可以表示任意大小的整数,前者最终会环绕,类似于intC/Java/etc 中的 s。)


循环的惯用方式取决于循环的作用:它可以是映射、折叠或过滤器。

您的循环输入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 中习惯使用 likeisPrime而不是 like 的名称is_prime。)

于 2012-06-30T17:01:23.300 回答
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))
于 2012-06-30T17:01:22.463 回答
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))
    
于 2012-06-30T16:59:19.867 回答