在以下示例代码的上下文中,如何将函数输出显示为列表[a],而不是字符串,显示[a]

时间:2019-04-30 12:29:40

标签: haskell

我开始学习一些编程,并且进行了以下练习:

“创建一个名为divisors的函数,该函数采用整数n> 1,并返回一个数组,该数组具有从最小到最大的所有整数除数(除了1和数字本身)。如果数字是素数,则返回字符串“(整数)为素数”。 提示:使用除数::(Show a,Integral a)=> a->任一字符串[a]

我还不了解如何使用Either类型,因此在此期间,我决定开始逐步解决问题。

由于练习的一部分需要构造一个函数以区分质数和非质数,因此我决定首先创建一个临时函数:如果数字(a)不是质数,则必须显示其除数的列表[1..a]。如果数字(a)为质数,则必须显示字符串“(a)为质数”。

以下代码有效:

divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
             then show [i | i <- [1..a], mod a i == 0 ]
             else show a ++ " is prime"

此功能的一些输出是:

[1 of 1] Compiling Main             ( program.hs, interpreted )
Ok, one module loaded.
*Main> divisors 1
"1 is prime"
*Main> divisors 2
"2 is prime"
*Main> divisors 3
"3 is prime"
*Main> divisors 4
"[1,2,4]"
*Main> divisors 5
"5 is prime"
*Main> divisors 6
"[1,2,3,6]"
*Main> divisors 7
"7 is prime"

但是,我确实需要将列表显示为列表[1..a],而不是字符串“ [1..a]”。因此,我从(然后)语句中删除了(显示):

divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
             then [i | i <- [1..a], mod a i == 0 ]
             else show a ++ " is prime"

但这会引发错误:

program.hs:11:42: error:
    * No instance for (Integral Char) arising from a use of `mod'
    * In the first argument of `(==)', namely `mod a i'
      In the expression: mod a i == 0
      In a stmt of a list comprehension: mod a i == 0
   |
11 | divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
   |                                          ^^^^^^^
Failed, no modules loaded.

我不完全了解出了什么问题,需要有人解释如何将非素数输出显示为列表,而不是列表的字符串表示形式。

如果有人愿意解释如何重新构造我的函数以正确使用Either类型,我不介意有人要解释如何回答解决方案中使用Either类型的原始练习问题。

编辑: 阅读一些回复后,我尝试了以下操作:

divisors :: (Show a, Integral a) => a -> Either String [a]
divisors a = if length [i | i <- [2..a], mod a i == 0] > 1
             then Right [i | i <- [2..a-1], mod a i == 0]
             else Left (show a ++ " is prime")

它似乎正在工作!

谢谢你的建议。

1 个答案:

答案 0 :(得分:5)

来自@RobinZigmond:您已经发现了为什么需要Either。 Haskell具有强大的静态类型系统,并且函数需要返回特定类型的值。函数不可能按照您想要的方式在某些输入上返回字符串,而在其他输入上返回整数列表(就像在动态语言中那样)。但这就是Either的目的。类型Either a b的值为Left x,其中x是类型a的值,或者Right y,其中y是类型的值b。希望您现在能明白为什么这对您的案件有用。

来自@chi:构造

if condition then x else y

要求xy属于同一类型。如果不是,请说:

x :: TypeX
y :: TypeY

您可以将它们都转换为Either TypeX TypeY,如下所示:

if condition then Left x else Right y

由于现在两个if分支具有相同的类型Either TypeX TypeY,因此它进行类型检查,并且该类型将是if所产生的值的类型。

从原始发帖人@nutbunny:阅读一些回复后,我尝试了以下操作:

divisors :: (Show a, Integral a) => a -> Either String [a]
divisors a = if length [i | i <- [2..a], mod a i == 0] > 1
             then Right [i | i <- [2..a-1], mod a i == 0]
             else Left (show a ++ " is prime")

它似乎正在工作!

谢谢你的建议!