uso opciones multiplos listas hacer funciones ejemplos composicion como ciclos basico arboles oop inheritance haskell

oop - opciones - Herencia para extender una estructura de datos en Haskell



multiplos en haskell (4)

Un programador de C ++ tratando de aprender Haskell aquí. Por favor, disculpe esta pregunta probablemente fácil. Quiero traducir un programa que represente formas en 3D. En C ++ tengo algo como:

class Shape { public: std::string name; Vector3d position; }; class Sphere : public Shape { public: float radius; }; class Prism : public Shape { public: float width, height, depth; };

Estoy tratando de traducir esto a Haskell (¿usando registros?) Para poder tener algunas funciones que saben cómo operar en una Forma (como acceder a su nombre y posición), y otras que solo sepan cómo operar en esferas, como calcular Algo basado en su posición y radio.

En C ++, una función miembro solo podría acceder a estos parámetros, pero me cuesta trabajo descubrir cómo hacer esto en Haskell con registros, clases de tipo o lo que sea.

Gracias.


Como dijo Nathan, los tipos de datos de modelado son completamente diferentes en Haskell de C ++. Podrías considerar el siguiente enfoque:

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 }

En otras palabras, modele las referencias a las súper clases como campos adicionales en su tipo de datos. Se extiende fácilmente a cadenas de herencia más largas.

No use clases tipográficas, como sugiere efímero. Estos se utilizan para funciones de sobrecarga y ese no es el problema aquí: su pregunta se refiere al modelado de datos , no al comportamiento .

¡Espero que esto ayude!


Contrariamente a la tendencia de desalentar el uso de las clases de tipos, recomiendo (a medida que aprendas) explorar tanto una solución sin clases de tipos como una para obtener una idea de las diferentes ventajas y desventajas de los distintos enfoques.

La solución de "tipo de datos cerrado único" es ciertamente más "funcional" que las clases de tipos. Implica que su lista de formas está "fijada" por su módulo de formas y no es extensible con nuevas formas desde el exterior. Todavía es fácil agregar nuevas funciones que operan en las formas.

Aquí tiene un pequeño inconveniente si tiene una función que opera solo en un solo tipo de forma porque abandona el compilador estático y verifique que la forma pasada sea correcta para la función (vea el ejemplo de Nathan). Si tiene muchas de estas funciones parciales que solo funcionan en un constructor de su tipo de datos, reconsideraría el enfoque.

Para una solución de clase de tipos, personalmente prefiero no reflejar la jerarquía de clases de formas sino crear clases de tipos para "cosas con un área de superficie", "cosas con un volumen", "cosas con un radio", ...

Esto le permite escribir funciones que toman tipos particulares de formas, por ejemplo esferas, solo (ya que cada forma es su propio tipo), pero no puede escribir una función que tome "cualquier forma" y luego distinga los distintos tipos de formas concretas. .


La traducción directa.

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

Aunque normalmente no harías esto; Es repetitivo y las listas polimórficas requieren extensiones de lenguaje.

En su lugar, colocarlos en un único tipo de datos cerrado es probablemente la primera solución que debe buscar.

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

Ciertamente, puedes simular múltiples niveles de herencia creando más clases de tipos:

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

También puedes simularlo insertando tipos de datos.

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"

Pero simular OO en Haskell no es tan satisfactorio como pensar de una manera haskelliana. ¿Que estás tratando de hacer?

(También tenga en cuenta que todas estas soluciones solo permiten upcasts, la reducción de emisiones es insegura y no está permitida).


Una simple traducción rompe la parte que varía pero evita las clases de tipos:

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

entonces

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"

Esta no es una pregunta realmente fácil. El diseño de la estructura de datos es muy diferente entre C ++ y Haskell, así que apuesto a que la mayoría de las personas que vienen de un lenguaje OO preguntan lo mismo. Desafortunadamente, la mejor manera de aprender es haciendo; lo mejor que puedes hacer es probarlo caso por caso hasta que aprendas cómo funcionan las cosas en Haskell.

Mi respuesta es bastante simple, pero no trata bien el caso en el que una única subclase de C ++ tiene métodos que los demás no tienen. Lanza un error de tiempo de ejecución y requiere código adicional para arrancar. También debe decidir si el módulo "subclase" decide si lanzar el error o el módulo "superclase".