【问题标题】:How to display function output as a list, [a], instead of a string, show [a], in context of the example code below如何在以下示例代码的上下文中将函数输出显示为列表 [a] 而不是字符串显示 [a]
【发布时间】:2019-04-30 22:19:15
【问题描述】:

我开始学习一些编程,我得到了以下练习:

"创建一个名为 divisors 的函数,它接受一个整数 n > 1 并返回一个数组,其中包含整数的所有除数(除了 1 和数字本身),从小到大。如果数字是素数,则返回字符串'(整数)是素数'。 提示:使用除数 :: (Show a, Integral a) => a -> Either String [a]"

我还不明白如何使用 Either 类型,所以在此期间我决定开始逐步解决这个问题。

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

以下代码有效:

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]”。因此,我从 (then) 语句中删除了 (show):

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

它似乎正在工作!

感谢您的建议。

【问题讨论】:

  • 你已经发现了为什么需要Either。 Haskell 具有强大的静态类型系统,函数需要返回特定类型的值。函数不可能按照您的意愿执行操作并在某些输入上返回字符串并在其他输入上返回整数列表(就像在动态语言中一样)。但这就是Either 的用途。 Either a b 类型的值要么是 Left x,其中 xa 类型的值,要么是 Right y,其中 yb 类型的值。希望您现在可以明白为什么这对您的案例有用。
  • if condition then x else y 要求 xy 属于同一类型。如果不是,例如x :: TypeXy :: TypeY,您可以将两者都转换为Either TypeX TypeY,如下所示:if condition then Left x else Right y。由于现在这两个if 分支具有相同的类型Either TypeX TypeY,它类型检查,并且该类型将是由if 产生的值的类型。
  • 感谢您的回复。我编辑了帖子以反映我尝试使用 Either String [a] 类型的尝试,它似乎有效!
  • 如果您确实想返回以不带LeftRight 的字符串形式打印结果,您可以使用div a = putStrLn . either show show $ divisors a,它应用相同的功能 (show)到Either 的每一侧,并打印出来。

标签: haskell


【解决方案1】:

来自@RobinZigmond:你已经发现了为什么需要Either。 Haskell 具有强大的静态类型系统,函数需要返回特定类型的值。函数不可能按照您的意愿执行操作并在某些输入上返回字符串并在其他输入上返回整数列表(就像在动态语言中一样)。但这就是Either 的用途。 Either a b 类型的值要么是 Left x,其中 xa 类型的值,要么是 Right y,其中 yb 类型的值。希望您现在可以明白为什么这对您的案例有用。

来自@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")

它似乎正在工作!

感谢您的建议!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多