2

I have to write two functions converting decimal numers into a (-2)adian number system (similar to binary only with -2) and vice versa. I already have managed to get the decimal -> (-2)adian running. But with (-2)adian -> decimal I have a problem and just don't know where to begin. Hope you can Help me

type NegaBinary = String

-- Function (-2)adisch --> decimal
negbin_dezi :: NegaBinary -> Integer -> Integer
negbin_dezi (xs:x) n 
    | (x == 0) = if ([xs] == "") then 0 else (negbin_dezi [xs] (n+1))
    | (x == 1) = if ([xs] == "") then (-2)**n else (-2)**n + (negbin_dezi [xs] (n+1))

It always throws: "Instances of (Num [Char], Floating Integer) required for definition of negbin_dezi.

Anyone an idea why it wont work? Please please please :)

Jakob Abfalter
  • 4,980
  • 17
  • 54
  • 94
  • The question and the title "go different ways". [Negative base](http://en.wikipedia.org/wiki/Negative_base) is the more common (and searchable) term; negabinary specifically. –  Nov 06 '12 at 20:50
  • 1
    (It would help if you posted the decimal to (-2)adian code too.) – AndrewC Nov 06 '12 at 20:53

3 Answers3

5

You have your list pattern-matching syntax backwards. In _ : _ the first argument is the head of the list (one element), and the second is the tail of the list (another list). e.g. x:xs matched with "abc" gives x = 'a' xs = "bc". So xs:x should be x:xs. The reason for GHC asking for an instance of Num [Char], is the comparison x == 0 (and x == 1). In this, it is trying to match the type of x (String == [Char]) with the type of 0 (Num a => a), and to do this, it requires a Num instance for String.

The fix is: negbin_dezi (x:xs) n

The problem asking for an Floating Integer instance is because (**) has type Floating a => a -> a -> a, where as you want (^) which has type (Num a, Integral b) => a -> b -> a (i.e. it is restricted to integer powers.)

Once you've done this, you'll find that your algorithm doesn't work for a few reasons:

  • The number 0 is different to the character '0', you should be comparing x with the characters '0' and '1' rather than the numbers 0 and 1.
  • xs is already a string, so [xs] is a list containing a string, which isn't what you want. This is fixed by removing the square brackets.
  • Possibly the ordering of the reduction is wrong.

On a different note, the duplicated if statement suggests that there is some optimisations that could happen with your code. Specifically, if you handle the empty string as part of negbin_dezi then you won't have to special case it. You could write it something like

negbin_dezi "" _ = 0
negbin_dezi (x:xs) n 
        | n == '0' = negbin_dezi xs (n+1)
        | n == '1' = (-2)^n + negbin_dezi

(This has the bonus of meaning that the function is "more total", i.e. it is defined on more inputs.)

A few more things:

  • The code is "stringly-typed": your data is being represented as a string, despite having more structure. A list of booleans ([Bool]) would be much better.
  • The algorithm can be adapted to be cleaner. For the following, I'm assuming you are storing it like "01" = -2 "001" = 4, etc. If so, then we know that number = a + (-2) * b + (-2)^2 * c ... = a + (-2) * (b + (-2) * (c + ...)) where a,b,c,... are the digits. Looking at this, we can see the stuff inside the brackets is actually the same as the whole expression, just starting at the second digit. This is easy to express in Haskell (I'm using the list-of-bools idea.):

    negbin [] = 0
    negbin (x:xs) = (if x then 1 else 0) + (-2) * negbin xs
    

    And that's the whole thing. If you aren't storing it in that order, then a call to reverse fixes that! (Being really tricky, one could write

    negbin = foldr (\x n -> (if x then 1 else 0) + (-2)*n) 0
    

    )

huon
  • 94,605
  • 21
  • 231
  • 225
3

Some problems:

  1. x == 0 or x == 1, but x is a Char, so you mean x == '0'.

  2. You write (xs:x). There's no pattern for matching at the end of a list. Perhaps use a helper function that reverses the list first.

  3. [xs] has one element, and will never be "". Use a base case instead.

  4. Pattern matching is more helpful than equality checking.

  5. ** is for floating point powers, ^ is for integer powers

  6. You often use [xs] where you mean xs. You don't need to put square brackets to make a list.

Here's a rewrite that works:

negbin_dezi1 :: NegaBinary -> Integer
negbin_dezi1 xs = negbin (reverse xs) 0

negbin []     _ = 0
negbin (x:xs) n 
    | x == '0' = negbin xs (n+1)
    | x == '1' = (-2)^n + (negbin xs (n+1))

It would be nicer to use pattern matching:

negbin_dezi2 :: NegaBinary -> Integer
negbin_dezi2 xs = negbin (reverse xs) 0 where
  negbin []     _ = 0
  negbin ('0':xs) n =          negbin xs (n+1)
  negbin ('1':xs) n = (-2)^n + negbin xs (n+1)

But maybe it would be nicer to convert '0' to 0 and '1' to 1 and just multiply by that:

val :: Char -> Int
val '0' = 0
val '1' = 1

negbin_dezi3 :: NegaBinary -> Integer
negbin_dezi3 xs = negbin (reverse xs) 0 where
  negbin []     _ = 0
  negbin (x:xs) n = val x * (-2)^n  +  negbin xs (n+1)

I'd not write it that way, though:

A completely different approach is to think about the whole thing at once.

"10010" -rev> [0,1,0,0,1] -means> [  0,      1,      0,      0,      1  ]
                                  [(-2)^0, (-2)^1, (-2)^2, (-2)^3, (-2)^4] 

so let's make both lists

powers = [(-2)^n | n <- [0..]]
coefficients = reverse.map val $ xs

and multiply them

zipWith (*) powers coefficients

then add up, giving:

negbin_dezi4 xs = sum $ zipWith (*) powers coefficients
    where powers = [(-2)^n | n <- [0..]]
      coefficients = reverse.map val $ xs

You could rewrite powers as map ((-2)^) [0..],
or even nicer: powers = 1:map ((-2)*) powers.
(It's nicer because it reuses previous calculations and is pleasantly clean.)

AndrewC
  • 32,300
  • 7
  • 79
  • 115
0

this

convB2D::NegaBinary->Integer
convB2D xs|(length xs)==0 =0
          |b=='0' = convB2D(drop 1 xs)
          |b=='1' = val+convB2D(drop 1 xs)
          |otherwise= error "invalid character " 
               where   b=head xs
                       val=(-2)^((length xs)-1)

worked for me. I on the other hand have problems to convert dec->nbin :D

sherif
  • 2,282
  • 19
  • 21