我今天用Haskell编写了我的第一个程序。它编译并成功运行。而且由于它不是一个典型的"Hello World"程序,它实际上做得远不止于此,所以恭喜我:D
无论如何,我对我的代码和 Haskell 中的语法几乎没有疑问。
问题:
我的程序从标准输入中读取一个整数N
,然后,对于[1,N]
范围内的每个整数i
,它会打印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
您误读了本教程。它会说类型签名应该是
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
的区别?后者可以表示任何大小的整数,前者最终环绕,类似于 C/Java/等中的 int
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中,通常使用像isPrime
这样的名称而不是像is_prime
这样的名称。
第 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))
-
它是
Integral a
,而不是Integer a
。请参阅 http://www.haskell.org/haskellwiki/Converting_numbers。 -
map
和朋友是你在哈斯克尔循环的方式。这就是我重写循环的方式: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))