【问题标题】:How can I have Show display the name of a function?如何让 Show 显示函数的名称?
【发布时间】:2012-06-14 01:19:03
【问题描述】:

作为一个让我熟悉 Haskell 的简单练习,在 Youtube 上闲逛并偶然发现美国倒计时游戏节目之后,我想为 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))

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

【问题讨论】:

  • filter (\q -&gt; q /= a &amp;&amp; q /= b &amp;&amp; q /= c &amp;&amp; q /= d)可以写成filter (`notElem` [a,b,c,d])
  • @sdcvvc 这是我以前不知道的反引号的用法。
  • 作为教学材料,您可以查看此视频 [Dr. Graham Hutton - 函数式编程基础 - 倒计时问题] (channel9.msdn.com/shows/Going+Deep/…)
  • @badp:这是一个部分,就像(+3);完整版:filter (\q -&gt; notElem q [a,b,c,d]).

标签: haskell


【解决方案1】:

另一种选择:

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

apply :: Operation -> Double -> Double -> Double
apply Add      = (+)
apply Subtract = (-)
apply Multiply = (*)
apply Divide   = (/)

【讨论】:

    【解决方案2】:

    我通常不建议您为函数实现 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])
    

    【讨论】:

      【解决方案3】:

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

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

      您可以通过执行map fst operationsmap snd operations 来获取它们的名称,从而获得仅包含这些操作的列表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-21
        • 1970-01-01
        • 1970-01-01
        • 2016-03-18
        • 2014-07-09
        • 1970-01-01
        相关资源
        最近更新 更多