2015-11-07 4 views
1

У меня есть школьный проект, где мне нужно упростить созданные пользователем математические выражения. Нам также необходимо определить наши собственные основные операторы (:+: - дополнение, :*: - умножение и т. Д.).Реализация пользовательских операторов

Например:

Prelude> Const 9 :+: Const 3 :+: Const 2 
Const 14 
Prelude> Const 14 :*: Var 'x' :+: Const 10 :*: Var 'x'  
Const 24 :*: Var 'x' 

Вот то, что я до сих пор:

infixl 4 :+: 
infixl 5 :*:, :/: 
infixr 6 :^: 

data Expr a = Var Char 
     | Const a 
     | (Expr a) :+: (Expr a) 
     | (Expr a) :*: (Expr a) 
     | (Expr a) :^: (Expr a) 
     | (Expr a) :/: (Expr a) 
     deriving (Show, Eq) 

Я пытался что-то подобное для моего оператора сложения без успеха:

class (Num a) => Expr a where 
    (:+:) :: a -> a -> a 

instance Expr a where 
    x :+: y = x + y 

Все что я ищу, это некоторые рекомендации о том, как это начать. Мне комфортно создавать свои собственные функции и использовать общие функции (map, zip, foldr и т. Д.). Недавно мы начали изучать классы классов, функторы и краткое введение в монады.

+0

'x' и' y' in 'x: +: y' ​​are' Expr a's. Вам действительно нужно реализовать всю упрощающую вещь, например. 'Const a: +: Const b = Const (a + b)' – Ryan

+1

Возможно, вам захочется начать с реализации функции 'оцените :: Fractional a => Expr a -> a'. (Вам понадобится 'Fractional' для деления, все остальное в' Num'.) –

+0

Вам, вероятно, придется реализовать упрощение, используя сопоставление шаблонов 'simp (x: *: (y: /: x ')) | x == x '= y; simp ... 'Упрощение этого выражения« полностью »довольно нетривиально, поэтому я не знаю, какие именно упрощения вы должны выполнять. – user2407038

ответ

3

Вы вводите в заблуждение две очень разные темы (если вы вставили сообщения об ошибках, возможно, они всплыли раньше и получили лучшую обратную связь). Давайте посмотрим на то, что вам было предоставлено - тип данных и классы классов Expr.

классном Путаница

Вы были обеспечены:

data Expr a = Var Char 
     | Const a 
     | (Expr a) :+: (Expr a) 
     | (Expr a) :*: (Expr a) 
     | (Expr a) :^: (Expr a) 
     | (Expr a) :/: (Expr a) 
     deriving (Show, Eq) 

Здесь мы можем увидеть Конструкторы основные арифметические операции. Иными словами, :+: является конструктором типа Expr a -> Expr a -> Expr a и аналогичным образом для других.

Теперь вы идете, чтобы попытаться определить функцию, а именно: :+:

class (Num a) => Expr a where 
    (:+:) :: a -> a -> a 

instance Expr a where 
    x :+: y = x + y 

Есть несколько проблем здесь.

  • Вы, класс Expr, просто называетесь тем же самым, что и вышеуказанный тип, он не имеет отношения к данным, определенным выше.
  • Ваш метод :+: не является именем юридической функции. Функции, в отличие от конструкторов данных, должны быть допустимыми переменными и, следовательно, не могут начинаться с символов верхнего регистра или двоеточия.
  • Экземпляр предназначен для всех типов - a является переменной и может соответствовать любому типу. Поэтому вы еще не сказали, что значения x и y имеют тип Expr a.

Я думаю, что вы пытаетесь сделать класс для каждого оператора и сделать экземпляр для каждого конструктора (:+:,: *: , etc). This is a broken, or at least overly verbose, solution. The only reason to use classes are if there are multiple type that require instances - you only have the one Expr` типа

Бесклассовой

Там нет необходимости. типа, чтобы решить эту проблему, вместо этого вы можете оценить выражение с помощью традиционной функции, как в удаленном ответе.

Ранее удаленный ответ кажется удаленным с понятием, что и среда привязок для каждого va необходим.Мое чтение проблемы отличается, и я просто оценил бы выражение в простой строке.

evaluate :: Show a => Expr a -> String 
evaluate (Var c) = [c] 
evaluate (a :+: b) = undefined 
evaluate (a :*: b) ... so on and so forth ... 

В вышеприведенном вами шаблон соответствует вашим Expr a для определения операции, то, в зависимости от конкретного случая, вычисляться суб-выражения, как необходимо, прежде чем объединять их в порядке, в зависимости от конструктора (+ с :+: и т.д.).

0

Я был в состоянии реализовать этот проект, я ценю помощь, чтобы начать меня в правильном направлении. Я отправлю свое решение здесь, если кто-то заинтересован. Это может быть не очень, но это работает!

completeCleanUp с помощью cleanUp упростить выражение. plugIn занимает около Char 'x' и заменяет все Var 'x' номером. Наконец evalExpr возвращает решение для Expr.

Обратите внимание, что переменное упрощение не требуется, поэтому Const 9 :*: Var 'x' :+: Const 10 :*: Var 'x' не будет уменьшено.

cleanUp :: (Eq a, Floating a) => Expr a -> Expr a 

cleanUp (Const a) = Const a 
cleanUp (Var a) = Var a 
cleanUp (Const a :*: (Const b :*: c)) = Const (a * b) :*: cleanUp c 
cleanUp (Const a :*: c :*: Const b) = Const (a*b) :*: cleanUp c 
cleanUp (c :*: Const a :*: Const b) = Const (a*b) :*: cleanUp c 
cleanUp (Const a :*: (b :+: c)) = (Const a :*: cleanUp b) :+: (Const a :*: cleanUp c) 
cleanUp (Const 0 :/: a) = Const 0 
cleanUp (a :/: Const 0) = error "Error: Division by 0 not allowed." 
cleanUp (a :/: b) | a == b = Const 1.0 
cleanUp (a :^: Const 1) = cleanUp a 
cleanUp (Const 1 :^: a) = cleanUp a 
cleanUp (a :^: Const 0) = Const 1.0 
cleanUp (Const 0 :^: a) = Const 1.0 
cleanUp ((c :^: Const b) :^: Const a) = cleanUp c :^: Const (a*b) 
cleanUp (Const a :^: Const b) = Const (a ** b) 
cleanUp (Const a :/: Const b) = Const (a/b) 
cleanUp (a :*: Const 1) = cleanUp a 
cleanUp (Const 1 :*: a) = cleanUp a 
cleanUp (a :*: Const 0) = Const 0 
cleanUp (Const 0 :*: a) = Const 0 
cleanUp (Const a :*: Const b) = Const (a * b) 
cleanUp (a :+: Const 0) = cleanUp a 
cleanUp (Const 0 :+: a) = cleanUp a 
cleanUp (Const a :+: Const b) = Const (a + b) 
cleanUp (Var a :^: b) = Var a :^: cleanUp b 
cleanUp (a :^: Var b) = cleanUp a :^: Var b 
cleanUp (Var a :+: b) = Var a :+: cleanUp b 
cleanUp (a :+: Var b) = cleanUp a :+: Var b 
cleanUp (Var a :*: b) = Var a :*: cleanUp b 
cleanUp (a :*: Var b) = cleanUp a :*: Var b 
cleanUp (Var a :/: b) = Var a :/: cleanUp b 
cleanUp (a :/: Var b) = cleanUp a :/: Var b 
cleanUp (a :^: b) = cleanUp a :^: cleanUp b 
cleanUp (a :/: b) = cleanUp a :/: cleanUp b 
cleanUp (a :*: b) = cleanUp a :*: cleanUp b 
cleanUp (a :+: b) = cleanUp a :+: cleanUp b 

completeCleanUp :: (Eq a, Floating a) => Expr a -> Expr a 

completeCleanUp exp 
    | exp == cleanUp exp = exp 
    | otherwise = completeCleanUp (cleanUp exp) 


plugIn :: Char -> a -> Expr a -> Expr a 

plugIn char num (Var c) | char == c = Const num 
plugIn char num (a :^: b) = plugIn char num a :^: plugIn char num b 
plugIn char num (a :/: b) = plugIn char num a :/: plugIn char num b 
plugIn char num (a :*: b) = plugIn char num a :*: plugIn char num b 
plugIn char num (a :+: b) = plugIn char num a :+: plugIn char num b 
plugIn char num a = a 


evalExpr :: (Eq a, Floating a) => Char -> a -> Expr a -> a 

evalExpr char num exp = case (completeCleanUp . plugIn char num $ exp) of 
    (Const x) -> x 
    _ -> error "Cannot further simplify solution."