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)
开发者_StackOverflow社区 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
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.
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
精彩评论