es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Busqueda de patrones sobre funciones que no son constructores.

Una de las formas más poderosas en que la coincidencia de patrones y la evaluación perezosa pueden combinarse es para evitar el cálculo costoso. Sin embargo, todavía me sorprende que Haskell solo permita la coincidencia de patrones de constructores, ¡que apenas es coincidencia de patrones en absoluto!

¿Hay alguna manera de implementar la siguiente funcionalidad en Haskell?

exp :: Double -> Double
exp 0 = 1
exp (log a) = a
--...

log :: Double -> Double
log 1 = 0
log (exp a) = a
--...

El problema original en el que encontré esto útil fue al escribir una preferencia/regla de asociatividad en una clase Monoid:

class Monoid m where
  iden :: m
  (+) m -> m -> m

(+) iden a = a
(+) a iden = a

--Línea con problema
(+) ((+) a b) c = (+) a ((+) b c)
Tags:  , ,

Answer

  1. Avatar for davy.ai

    Desafortunadamente, no es posible hacer coincidir patrones directamente en expresiones en Haskell, solo en constructores de tipos de datos. Sin embargo, hay enfoques alternativos para lograr la funcionalidad deseada. Una opción es usar guardias para verificar la propiedad deseada de la entrada:

    exp :: Double -> Double
    exp x | x == 0 = 1
          | otherwise = exp (log x)
    
    log :: Double -> Double
    log x | x == 1 = 0
          | otherwise = log (exp x)
    

    Otro enfoque es definir nuevos tipos de datos para representar las expresiones deseadas y usar la coincidencia de patrones en esos tipos:

    data Expr = Const Double | Log Expr | Exp Expr
    
    eval :: Expr -> Double
    eval (Const c) = c
    eval (Log e) = log (eval e)
    eval (Exp e) = exp (eval e)
    

    En cuanto al problema con la preferencia de asociatividad en la clase Monoid, hay un par de formas de resolverlo. Una posible solución es definir un nuevo tipo para representar las expresiones en la operación Monoid y usar la coincidencia de patrones o guardias en ese tipo:

    data MonExpr m = Id | M m | Sum (MonExpr m) (MonExpr m)
    
    instance Monoid m => Monoid (MonExpr m) where
      mempty = Id
      mappend Id x = x
      mappend x Id = x
      mappend (Sum a b) c = mappend a (mappend b c)
      mappend a (Sum b c) = mappend (mappend a b) c
      mappend (M a) (M b) = M (mappend a b)
    

    Este enfoque separa la representación de la expresión de los valores reales que se combinan, lo que permite una coincidencia de patrones más flexible y evita la necesidad de evaluar cualquier cálculo costoso hasta que sea necesario.

Comments are closed.