How can I have Show display the name of a function

2019-07-04 03:06发布

As a simple exercise to get me acquainted with Haskell, after idling around on Youtube and stumbling into the American Countdown game show, I wanted to make a solver for the Numbers game.

You get 6 numbers and need to combine them with (+) (-) (*) (/) in order to get a given result.

enter image description here

What I've got so far is the very brain-dead,

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

...but obviously Show doesn't know what to do with functions.

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

How can I work around this? Do I need to mess with type and data constructors to make my own functions that can print or is there some easier way around it?

标签: haskell
3条回答
▲ chillily
2楼-- · 2019-07-04 03:18

I would generally not advise you to implement a Show instance for functions. It is not the "haskelly" way of doing things, for this reason:

You define a "canonical" way of showing functions. You might want them to be shown as their names now, but what if you decide to do this:

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

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

Should really the output of your program change depending on your internal implementation? That doesn't make much sense. Also, what happens to nameless functions?

Also, in another part of the program, you might want to show your functions as their types instead, etc., and then you'd have conflicting Show instances.

Generally, only implement Show when you know that there should be only one way of showing that thing, and that that way works for all values that need to be shown.


The simplest way of solving this problem would probably be to just store the name of the operation along with the operation. Like so:

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楼-- · 2019-07-04 03:26

Another option:

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

apply :: Operation -> Double -> Double -> Double
apply Add      = (+)
apply Subtract = (-)
apply Multiply = (*)
apply Divide   = (/)
查看更多
来,给爷笑一个
4楼-- · 2019-07-04 03:28

You cannot print the name of a function in Haskell (there may be some crazy tricks with meta programming but essentially you cannot). For your program it is probably easiest to define the list of operations as a list of pairs with a string of the name of each operator e.g.

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

You can get a list of just the operations by doing map fst operations and map snd operations to get their names.

查看更多
登录 后发表回答