Pergunta

I'm trying to convert a point in cartesian 3d coordinate system to a spherical 3d system.

This is what I got so far:

radialDistance3D (x,y,z) = sqrt (x*2 + y*y + z*z)

cartesian3DToPolar3D (x,y,z) = (r,alpha, beta)
                                where r     = radialDistance3D (x,y,z) 
                                      alpha = acos(z/r)
                                      beta  = atan2(y,x)

Ghci loads the code but when I try to execute it with

cartesian3DToPolar3D (1.0,2.0,3.0)

I get:

<interactive>:1:0:
    No instance for (RealFloat (t, t))
      arising from a use of `cartesian3DToPolar3D'
                   at <interactive>:1:0-33
    Possible fix: add an instance declaration for (RealFloat (t, t))
    In the expression: cartesian3DToPolar3D (1.0, 2.0, 3.0)
    In the definition of `it':
        it = cartesian3DToPolar3D (1.0, 2.0, 3.0)

Which isn't helpfull. What is going on?

Conversion formulas are from http://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates

Foi útil?

Solução 2

Corrected code:

radialDistance3D (x,y,z) = sqrt (x*x + y*y + z*z)

cartesian3DToPolar3D (x,y,z) = (r,alpha, beta)
                                where r     = radialDistance3D (x,y,z) 
                                      alpha = acos(z/r)
                                      beta  = atan2 y x

There were two mistakes, first was in

radialDistance3D (x,y,z) = sqrt (x*2 + y*y + z*z)` 

which should be

radialDistance3D (x,y,z) = sqrt (x*x + y*y + z*z)

the second was

beta  = atan2(y,x)  

which should be

beta = atan2 x y

Outras dicas

More generally, in Haskell arguments are not normally written in the form "foo (x,y,z)". Instead we write "foo x y z". The former is legal: it wraps up the arguments into a single value (known as a tuple) and passes that, but its unnecessary.

The error message came from the line

beta = atan2 (y, x).

This failed because of the type of the library function atan2, which is roughly

atan2 :: RealFloat a => a -> a -> a

This means that for any type "a" which is an instance of "RealFloat" (i.e. the types "Float" and "Double"), the function "atan2" takes two of them as arguments and returns a new one. The "RealFloat a => ..." bit says that in the rest of the type "a" can be any type which is declared to be an instance of the class RealFloat. "Float" and "Double" are examples of these types. So one potential type for this function is:

atan2 :: Double -> Double -> Double

However what you did was treat it as though it had a different type:

atan2 :: (Double, Double) -> Double

This says that "atan2" takes a single argument which is a tuple containing two values. The type checker tried to see if this whole tuple is an instance of "RealFloat" (i.e. one of the types that we can substitute for "a" in the type of "atan2"), and found that it isn't. So it generated an error message saying so.

What is actually going on in the "atan2 y x" syntax, and in the arrows in the type signature, is revealed when you put the implicit brackets back in. The "->" type operator is right associative, so the type of atan2 is actually:

atan2 :: Double -> (Double -> Double)

(Note: for simplicity I'm leaving out the "RealFloat a" business.) This says that "atan2" takes an argument and returns a new function which expects the second argument.

Now lets put the implicit brackets into the call. Function application is left associative, so the definition of "beta" looks like this;

beta = (atan2 x) y

Following the rule of evaluating brackets from the inside out, this applies the function "atan2" to "x" and gets a new function as a result, which is then applied to "y" giving "beta" as a result. See how the type and the expression mirror each other?

This is not just a theoretical trick: I could write something like

myBeta = atan2 x
...
beta = myBeta y

Or even

betas = map myBeta ys

where "ys" and "betas" are lists of values. Being able to do something like this is one of the great strengths of Haskell.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top