Центр тяжести списка точек - PullRequest
3 голосов
/ 11 октября 2019

Новичок в Haskell

Проблема в

-- The centroid of a list of points is a point whose x (and y) coordinates are
-- the means of the x (and y) coordinates of the points in the list.
--
-- You may assume the list contains at least one point.
--
-- > centroid [Pt 1 1, Pt 2 2]
-- Pt 1.5 1.5
-- > centroid [Pt (-1.5) 0, Pt 3 2, Pt 0 1]
-- Pt 0.5 1.0

Попробуйте код, подобный этому

data Point = Pt Double Double deriving (Show, Eq)

centroid :: [Point] -> Point

pointX :: Point -> Double
pointX (Pt x y) = x
pointY :: Point -> Double
pointY (Pt x y) = y

pointsX :: [Point] -> [Double]
pointsX xs = map pointX xs
pointsY :: [Point] -> [Double]
pointsY xs = map pointY xs

average :: [Double] -> Double
average xs = (sum xs) `div` (genericLength xs)

centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))

И я получил

Project1.hs:35:22: error:
    • Couldn't match expected type ‘([Double] -> Double)
                                    -> [Double] -> Point’
                  with actual type ‘Point’
    • The function ‘Pt’ is applied to four arguments,
      but its type ‘Double -> Double -> Point’ has only two
      In the expression:
        (Pt average (pointsX cenpoint) average (pointsY cenpoint))
      In an equation for ‘centroid’:
          centroid cenpoint
            = (Pt average (pointsX cenpoint) average (pointsY cenpoint))
   |
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Project1.hs:35:25: error:
    • Couldn't match expected type ‘Double’
                  with actual type ‘[Double] -> Double’
    • Probable cause: ‘average’ is applied to too few arguments
      In the first argument of ‘Pt’, namely ‘average’
      In the expression:
        (Pt average (pointsX cenpoint) average (pointsY cenpoint))
      In an equation for ‘centroid’:
          centroid cenpoint
            = (Pt average (pointsX cenpoint) average (pointsY cenpoint))
   |
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
   |                         ^^^^^^^

Project1.hs:35:33: error:
    • Couldn't match expected type ‘Double’ with actual type ‘[Double]’
    • In the second argument of ‘Pt’, namely ‘(pointsX cenpoint)’
      In the expression:
        (Pt average (pointsX cenpoint) average (pointsY cenpoint))
      In an equation for ‘centroid’:
          centroid cenpoint
            = (Pt average (pointsX cenpoint) average (pointsY cenpoint))
   |
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
   |

Ответы [ 3 ]

3 голосов
/ 11 октября 2019

Вы справились довольно хорошо - обратите внимание, как все ошибки компиляции указывают на вашу последнюю строку (определение centroid). И все они из-за неправильного брекетинга. Это то, что должно быть вместо этого:

centroid cenpoint = Pt (average (pointsX cenpoint)) (average (pointsY cenpoint))

То есть координата x полученного значения Pt является средним значением координат x соответствующих точек (pointsX cenpointсписок x-координат, поэтому average (pointsX cenpoint) является их средним значением), и аналогично для координаты y.

По сравнению с вашей неверной версией (которую я немного прибрал, не меняя, как она интерпретируется)компилятором):

Pt average (pointsX cenpoint) average (pointsY cenpoint)

Это означает, что вы применяете функцию Pt к 4 аргументам: average, pointsX cenpoint, average снова и pointsY cenpoint. Это просто не работает, потому что функция Pt принимает только 2 аргумента.

3 голосов
/ 11 октября 2019

Основная проблема заключается в том, что вы вызываете функции, как если бы вы делали это на языке, таком как Java, C ++ или Python.

Вам необходимо заключить average в круглые скобки, например:

centroid cenpoint = Pt <b>(</b>average (pointsX cenpoint)<b>)</b> <b>(</b>average (pointsY cenpoint)<b>)</b>

Как говорит @ dfeuer , здесь не нужны внешние скобки.

Более того, вы, возможно, захотите использовать (/) здесь вместо div, так как вы работаетес Double s, тогда как div работает на Integral типах:

average :: [Double] -> Double
average xs = sum xs <b>/</b> genericLength xs

, но, как и @ leftroundabout говорит , мы можем лучше использовать length, а затем использовать fromIntegral:

average :: [Double] -> Double
average xs = sum xs <b>/</b> <b>fromIntegral</b> (length xs)
1 голос
/ 12 октября 2019

Не совсем ответ на вопрос, но - это предпочтительный способ сделать это:

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

import Data.VectorSpace
import GHC.Generics

data Point = Pt Double Double
   deriving (Eq, Show, Generic, AdditiveGroup, VectorSpace)

average :: (VectorSpace v, Fractional (Scalar v)) => [v] -> v
average ps = sumV ps ^/ fromIntegral (length xs)

Тогда вы можете напрямую сделать

> average [Pt (-1.5) 0, Pt 3 2, Pt 0 1]
Pt 0.5 1.0

Т.е. centroid ≡ average.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...