4

作为一个让我熟悉 Haskell 的简单练习,在 Youtube 上闲逛并偶然发现 American Countdown 游戏节目之后,我想为 Numbers 游戏制作一个求解器。

你得到 6 个数字,需要将它们组合起来(+) (-) (*) (/)才能得到给定的结果。

在此处输入图像描述

到目前为止我得到的是非常脑死亡的,

let operands = [75, 2, 6, 3, 8, 7] :: [Double]
let goal = 623 :: Double
let operations = [(+), (-), (*), (/)]

show (head [(a, x, b, y, c, z, d, t, e) |
      a <- operands,
      b <- filter (\ q -> q /= a) operands,
      c <- filter (\ q -> q /= a && q /= b) operands,
      d <- filter (\ q -> q /= a && q /= b && q /= c) operands,
      e <- filter (\ q -> q /= a && q /= b && q /= c && q /= d) operands,
      x <- operations,
      y <- operations,
      z <- operations,
      t <- operations,
      t (z (y (x a b) c) d) e == goal])

...但显然 Show 不知道如何处理函数。

No instance for (Show (Double -> Double -> Double))
  arising from a use of `show'
Possible fix:
  add an instance declaration for (Show (Double -> Double -> Double))

我该如何解决这个问题?我是否需要弄乱类型和数据构造函数来制作我自己的可以打印的函数,还是有一些更简单的方法来解决它?

4

3 回答 3

14

另外一个选项:

data Operation = Add | Subtract | Multiply | Divide deriving (Show)

apply :: Operation -> Double -> Double -> Double
apply Add      = (+)
apply Subtract = (-)
apply Multiply = (*)
apply Divide   = (/)
于 2012-06-09T15:25:53.187 回答
9

我通常不建议您Show为函数实现实例。这不是“草率”的做事方式,原因如下:

您定义了显示函数的“规范”方式。您可能希望他们现在显示为他们的名字,但如果您决定这样做:

add 0 y = y
add x y = add (x - 1) (y + 1)

operations = [..., add, ...]

你的程序的输出真的应该根据你的内部实现而改变吗?这没有多大意义。另外,无名函数会发生什么?

此外,在程序的另一部分,您可能希望将您的函数显示为它们的类型,等等,然后您会有冲突的Show实例。

通常,只有Show在您知道应该只有一种显示该事物的方式时才实施,并且该方式适用于所有需要显示的值。


解决此问题的最简单方法可能是将操作的名称与操作一起存储。像这样:

let operations = [("+", (+)), ("-", (-)), ("*", (*)), ("/", (/))]

-- ...
show (head [(a, xname, b, yname, c, zname, d, tname, e) |
  a <- operands,
  b <- filter (\ q -> q /= a) operands,
  c <- filter (\ q -> q /= a && q /= b) operands,
  d <- filter (\ q -> q /= a && q /= b && q /= c) operands,
  e <- filter (\ q -> q /= a && q /= b && q /= c && q /= d) operands,
  (xname, x) <- operations,
  (yname, y) <- operations,
  (zname, z) <- operations,
  (tname, t) <- operations,
  t (z (y (x a b) c) d) e == goal])
于 2012-06-09T15:21:08.790 回答
1

您不能在 Haskell 中打印函数的名称(元编程可能有一些疯狂的技巧,但实际上您不能)。对于您的程序,将操作列表定义为带有每个操作符名称的字符串的对列表可能是最简单的,例如

let operations = [((+)," + "), ((-), " - ")), ((*), " * ")), ((/), " / ")]

您可以通过做map fst operationsmap snd operations获取它们的名称来获取仅操作的列表。

于 2012-06-09T15:20:47.363 回答