Наследование для расширения структуры данных в Haskell

Я всегда думаю о нем как о "передаче копией". Это - копия значения быть им примитивный или ссылочный. Если это - примитив, это - копия битов, которые являются значением и если это - Объект, это - копия ссылки.

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

вывод java PassByCopy:

имя = имя Maxx
= Fido

Примитивные классы обертки и Строки неизменны, таким образом, любой пример с помощью тех типов не будет работать то же другими типами/объектами.

23
задан Daniel Daranas 2 August 2013 в 16:47
поделиться

4 ответа

Вопреки тенденции не поощрять использование классов типов, я бы рекомендовал (по мере того, как вы учитесь), чтобы исследовать как решение без классов типов, так и решение с одним, чтобы почувствовать различные компромиссы различных подходов.

Решение с «единым закрытым типом данных» определенно более «функционально», чем классы типов. Это означает, что ваш список фигур «фиксируется» модулем фигур и не расширяется за счет новых фигур извне. По-прежнему легко добавлять новые функции, работающие с фигурами.

У вас есть небольшое неудобство, если у вас есть функция, которая работает только с одним типом фигуры, потому что вы отказываетесь от статического компилятора, проверяющего правильность переданной фигуры для функции (см. пример Натана). Если у вас есть много этих частичных функций, которые работают только с одним конструктором вашего типа данных, я бы пересмотрел подход.

Для решения класса типов я лично предпочел бы не отражать иерархию классов фигур но создавать классы типов для «предметов с площадью поверхности», «предметов с объемом», «предметов с радиусом», ...

Это позволяет вам писать функции, которые принимают определенные виды форм, например, сферы, только (поскольку каждая форма имеет свой тип), но вы можете '

13
ответ дан 29 November 2019 в 01:49
поделиться

Прямой перевод.

type Vector3D = (Double, Double, Double)

class Shape shape where
    name :: shape -> String
    position :: shape -> Vector3D

data Sphere = Sphere {
    sphereName :: String,
    spherePosition :: Vector3D,
    sphereRadius :: Double
}

data Prism = Prism {
    prismName :: String,
    prismPosition :: Vector3D,
    prismDimensions :: Vector3D
}

instance Shape Sphere where
    name = sphereName
    position = spherePosition

instance Shape Prism where
    name = prismName
    position = prismPosition

Обычно вы этого не делаете; это повторяющиеся и полиморфные списки требуют языковых расширений.

Вместо этого, объединение их в один закрытый тип данных, вероятно, первое решение, к которому вы должны пойти.

type Vector3D = (Double, Double, Double)

data Shape
  = Sphere { name :: String, position :: Vector3D, radius :: Double }
  | Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }

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

class (Shape shape) => Prism shape where
    dimensions :: Vector3D
data RectangularPrism = ...
data TriangularPrism = ...
instance Prism RectangularPrism where ...
instance Prism TriangularPrism where ...

Вы также можете моделировать это, встраивая типы данных.

type Vector3D = (Double, Double, Double)

data Shape = Shape { name :: String, position :: Vector3D }

data Sphere = Sphere { sphereToShape :: Shape, radius :: Double }
newSphere :: Vector3D -> Double -> Shape
newSphere = Sphere . Shape "Sphere"

data Prism = Prism { prismToShape :: Shape, dimensions :: Vector3D }

data RectangularPrism = RectangularPrism { rectangularPrismToPrism :: Prism }
newRectangularPrism :: Vector3D -> Vector3D -> RectangularPrism
newRectangularPrism = (.) RectangularPrism . Prism . Shape "RectangularPrism"

data TriangularPrism = TriangularPrism { triangularPrismToPrism :: Prism }
newTriangularPrism :: Vector3D -> Vector3D -> TriangularPrism
newTriangularPrism = (.) TriangularPrism . Prism . Shape "TriangularPrism"

Но моделирование объектно-ориентированного программирования в Haskell далеко не так удовлетворительно, как на самом деле мышление в стиле Haskell. Что вы пытаетесь сделать?

(Также обратите внимание, что все эти решения разрешают только повышающее качество, понижающее преобразование небезопасно и запрещено.)

23
ответ дан 29 November 2019 в 01:49
поделиться

Like Nathan said, modelling datatypes is completely different in Haskell from C++. You could consider the following approach:

data Shape  = Shape  { name        :: String, position :: Vector3d }
data Sphere = Sphere { sphereShape :: Shape,  radius   :: Float }
data Prism  = Prism  { prismShape  :: Shape,  width    :: Float, height :: Float, depth :: Float }

In other words, model references to super classes as extra fields in your datatype. It easily extends to longer inheritance chains.

Don't use type classes, like ephemient suggests. These are used for overloading functions and that is not the issue here at all: your question concerns the modelling of data, not behaviour.

Hope this helps!

7
ответ дан 29 November 2019 в 01:49
поделиться

Простой перевод выделяет часть, которая меняется, но избегает классов типов:

type Vector3D = (Float,Float,Float)
data Body = Prism Vector3D | Sphere Double
radius (Prism position) = -- code here
radius (Sphere r) = r

затем

data Shape = Shape {
  name :: String,
  position :: Vector3D,
  body :: Body
}

shapeOnly (Shape _ pos _) = -- code here

both = radius . body

sphereOnly (Shape _ _ (Sphere radius)) = -- code here
sphereOnly _ = error "Not a sphere"

Это не действительно простой вопрос. Дизайн структуры данных сильно отличается в C ++ и Haskell, поэтому я уверен, что большинство людей, пришедших из объектно-ориентированного языка, задают одно и то же. К сожалению, лучший способ учиться - это делать; лучше всего пробовать это в каждом конкретном случае, пока вы не узнаете, как все работает в Haskell.

Мой ответ довольно прост, но он не подходит для случая, когда у одного подкласса C ++ есть методы что другие не делают. Это вызывает ошибку времени выполнения и требует дополнительного кода для загрузки. Вы также должны решить, будет ли модуль «подкласса» выдавать ошибку или модуль «суперкласс».

2
ответ дан 29 November 2019 в 01:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: