首页 文章

`(整数a)=> a - > Bool`和`整数 - > Bool`之间的区别?

提问于
浏览
4

我今天在Haskell写了我的第一个程序 . It compiles and runs successfully . 而且由于它不是典型的"Hello World"程序,它实际上远不止于此,所以请恭喜我:D

无论如何,我对我的代码和Haskell中的语法几乎没有疑问 .

Problem:

我的程序从标准输入读取整数 N ,然后,对于 [1,N] 范围内的每个整数 i ,它打印 i 是否为素数 . 目前它不检查输入错误 . :-)

Solution: (也是疑惑/问题)

为了解决这个问题,我编写了这个函数来测试整数的素数:

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中读到的内容不起作用,并且给出了这个错误(我想这是一个错误,尽管它没有这么说) :

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

哪个不起作用,并给出上面发布的错误(?) .

为什么它不起作用?这一行(不起作用)和行(起作用)有什么区别?


另外,循环 1N 的惯用方法是什么?我对代码中的循环并不完全满意 . 请提出改进建议 . 这是我的代码:

--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

3 回答

  • 5

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

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

    这些是不同的类型:

    • Integer -> Bool

    • 这是一个函数,它取一个 Integer 类型的值,并返回一个 Bool 类型的值 .

    • Integral a => a -> Bool

    • 这是一个函数,它采用 a 类型的值并返回 Bool 类型的值 .

    • 什么是 a ?它可以是实现 Integral 类型类的任何类型的调用者选择,例如 IntegerInt .

    (和 IntInteger 之间的区别?后者可以表示任何大小的整数,前者最终包围,类似于C / Java /等中的 int . )


    循环的惯用方法取决于循环的作用:它可以是 Map ,折叠或过滤器 .

    你在 main 中的循环是一个 Map ,因为你在循环中进行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 这样的名称是常规的 . )

  • 3

    Part 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 类的成员 .

    Part 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))
    
  • 13
    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))
    

相关问题