Экземпляр в терминах show
не достаточно мощный, чтобы избежать избыточных скобок, поскольку он не имеет никакой доступной информации о приоритете. Вместо этого вам нужно будет написать свой экземпляр в терминах showsPrec
, что выглядит следующим образом:
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 7 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 8 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 8 e1 . showString " / " . showsPrec 8 e2
Я выбрал 6 и 7 для ваших уровней приоритета, поскольку именно это Haskell использует для своих собственных +
, *
и div
операторы, но должно быть очевидно, как вы будете выбирать разные.
Что касается ассоциативности, в общем, не существует идеального способа сделать это, но вы можете подделайте его с некоторыми изменениями приоритета в вашем случае, так как математика не имеет операторов с одинаковыми уровнями приоритета с различными ассоциациями. Вот пример того, как это сделать (я добавил Exp
с уровнем приоритета 8, чтобы дать пример правильно ассоциативного способа сделать это тоже):
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
| Exp Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 6 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " / " . showsPrec 8 e2
showsPrec prec (Exp e1 e2) = showParen (prec >= 9) $ showsPrec 9 e1 . showString " ^ " . showsPrec 8 e2
Это все еще не идеально, поскольку он все еще не знает ассоциативное свойство Add
или Mul
, поэтому Mul one (Mul one one)
будет отображаться как 1 * (1 * 1)
вместо 1 * 1 * 1
, но я не думаю, что есть какой-либо возможный способ исправить это, так как деление не разделяет это свойство, но поскольку оно имеет тот же приоритет, что и умножение, вы не можете различить guish их в showsPrec
.
На самом деле, вы можете обмануть немного больше, если заглянуть на следующий уровень вниз и заново связать:
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
| Exp Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 (Add e2 e3)) = showsPrec prec (Add (Add e1 e2) e3)
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 6 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 (Mul e2 e3)) = showsPrec prec (Mul (Mul e1 e2) e3)
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " / " . showsPrec 8 e2
showsPrec prec (Exp e1 e2) = showParen (prec >= 9) $ showsPrec 9 e1 . showString " ^ " . showsPrec 8 e2
Я думаю, что это идеально. Все ваши тесты пройдены сейчас.