Преобразование из декартовой 3d в сферическую 3d систему координат в haskell - PullRequest
2 голосов
/ 10 ноября 2010

Я пытаюсь преобразовать точку в декартовой трехмерной системе координат в сферическую трехмерную систему.

Это то, что я получил до сих пор:

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 загружает код, но когда я пытаюсь выполнить его с

cartesian3DToPolar3D (1.0,2.0,3.0)

Я получаю:

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

Что бесполезно. Что происходит?

Формулы преобразования взяты из http://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates

Ответы [ 2 ]

6 голосов
/ 10 ноября 2010

В более общем смысле аргументы в Haskell обычно не записываются в форме "foo (x, y, z)". Вместо этого мы пишем «foo x y z». Первый законен: он объединяет аргументы в одно значение (известное как кортеж) и передает его, но в этом нет необходимости.

Сообщение об ошибке пришло со строки

beta = atan2 (y, x).

Это не удалось из-за типа библиотечной функции atan2, которая примерно равна

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

Это означает, что для любого типа «a», который является экземпляром «RealFloat» (то есть типов «Float» и «Double»), функция «atan2» принимает два из них в качестве аргументов и возвращает новый. Бит «RealFloat a => ...» говорит о том, что в остальной части типа «a» может быть любым типом, который объявлен экземпляром класса RealFloat. «Float» и «Double» являются примерами этих типов. Таким образом, один потенциальный тип для этой функции:

atan2 :: Double -> Double -> Double

Однако то, что вы сделали, относилось к нему как к другому типу:

atan2 :: (Double, Double) -> Double

Это говорит о том, что atan2 принимает единственный аргумент , который является кортежем, содержащим два значения. Средство проверки типов попыталось определить, является ли весь этот кортеж экземпляром "RealFloat" (то есть одним из типов, который мы можем заменить на "a" в типе "atan2"), и обнаружило, что это не так. Таким образом, он выдал сообщение об ошибке, говорящее так.

То, что на самом деле происходит в синтаксисе atan2 yx и в стрелках в сигнатуре типа, раскрывается, когда вы возвращаете неявные скобки. Оператор типа "->" ассоциативно справа, поэтому Атан2 на самом деле:

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

(Примечание: для простоты я опускаю бизнес "RealFloat a".) Это говорит о том, что atan2 принимает аргумент и возвращает новую функцию, которая ожидает второй аргумент.

Теперь давайте поместим неявные скобки в вызов. Приложение функции остается ассоциативным, поэтому определение «бета» выглядит так:

beta = (atan2 x) y

Следуя правилу вычисления скобок изнутри, это применяет функцию «atan2» к «x» и в результате получает новую функцию, которая затем применяется к «y», давая в результате «beta». Видите, как тип и выражение отражают друг друга?

Это не просто теоретический трюк: я мог бы написать что-то вроде

myBeta = atan2 x
...
beta = myBeta y

Или даже

betas = map myBeta ys

где "ys" и "betas" - это списки значений. Возможность делать что-то подобное - одна из сильных сторон Хаскелла.

5 голосов
/ 10 ноября 2010

Исправленный код:

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

Было две ошибки, первая была в

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

, который должен быть

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

второй был

beta  = atan2(y,x)  

, который должен быть

beta = atan2 x y
...