Я пытаюсь использовать в списке разные типы данных. например:

data Shape = Square Int
            | Circle Int
            | Rectangle Int Int
            | Triangle Int Int Int
                 deriving (Show)


shapes = [Square 5, Circle 2, Rectangle 10 5]
showShapes :: [Shape] -> [Int]
showShapes [] = []
showShapes (s:xs) = getArea (s : xs)

Однако я изо всех сил пытаюсь создать метод getArea, так как мне нужен один для каждого типа. Я не знаю, как это сделать с помощью сопоставления шаблонов параметров. Есть ли способ сделать это или я неправильно решаю эту проблему?

Редактировать

Как бы вы это сделали, используя оператор if и функцию typeOf

Я попытался изменить форму на это:

data Shape = Square Int
            | Rectangle Int Int
            | Triangle Int Int Int
                 deriving (Show, Typeable)

Но я получаю ошибку времени компиляции!

0
Yahya Uddin 11 Ноя 2014 в 04:48

2 ответа

Лучший ответ

В вашем простом случае просто используйте сопоставление с образцом в getArea, но вам придется преобразовать свои значения в двойные, поскольку площадь круга никогда не будет целым числом, если у вас есть целочисленный радиус:

getArea :: Shape -> Double
getArea (Square l)       = fromIntegral $ l * l
getArea (Circle r)       = pi * fromIntegral r ^ 2
getArea (Rectangle l w)  = fromIntegral $ l * w
-- assuming the constructor takes the 3 side lengths
getArea (Triangle a b c) = sqrt $ p * (p - a') * (p - b') * (p - c')
    where
        [a', b', c'] = map fromIntegral [a, b, c]
        p = (a' + b' + c') / 2

Хотя я не знаю, чем вы хотите заниматься в showShapes. Обычно слово show в Haskell означает то же самое, что и toString в других языках, но вы пытаетесь применить getArea внутри него. Несмотря на это, сопоставление с шаблоном для showShapes отключено, вам нужны скобки вокруг s:xs, иначе вы получите синтаксическую ошибку, и вы не сможете добавить число перед списком Shape как с getArea s : xs. Вместо этого вы, возможно, захотите рассчитать площадь для каждой фигуры в списке? Для этого вы можете использовать map:

getAreas :: [Shape] -> [Double]
getAreas shapes = map getArea shapes
4
bheklilr 11 Ноя 2014 в 15:35

Обратите внимание, что в этом случае вам не нужно хранить все цифры в одном типе данных. Вместо этого вы можете использовать экзистенциальную количественную оценку:

{-# LANGUAGE ExistentialQuantification #-}

data Square    = Square Int
data Circle    = Circle Int
data Rectangle = Rectangle Int Int

class HasArea a where
    area :: a -> Double

instance HasArea Square where
    area (Square n) = fromIntegral n * fromIntegral n

instance HasArea Circle where
    area (Circle r) = pi * fromIntegral r ^ 2

instance HasArea Rectangle where
    area (Rectangle n m) = fromIntegral n * fromIntegral m

data Shape = forall s. HasArea s => Shape s

shapes :: [Shape]
shapes = [Shape (Square 5), Shape (Circle 2), Shape (Rectangle 10 5)]

shapeArea :: Shape -> Double
shapeArea (Shape s) = area s

main = print $ map shapeArea shapes

Вы можете прочитать об экзистенциальной количественной оценке здесь: http://en.wikibooks.org/wiki/Haskell/Existenfully_quantified_types

Сама по себе экзистенциальная количественная оценка слабее, чем обобщенные алгебраические типы данных. Вы можете прочитать о них здесь: http://en.wikibooks.org/wiki/Haskell/GADT < / а>

1
user3237465 11 Ноя 2014 в 16:01