2016-03-01 3 views
1

Привет ребята У меня возникли проблемы с моей, например, чтобы показать дерево в Haskell: вот мой код:Haskell дисплей Дерево без Data.Tree или выводе формулы (шоу)

data Tree a b = Branch b (Tree a b) (Tree a b) 
      | Leaf a 
instance (Show a, Show b) =>Show (Tree a b) where 
show (Leaf x) = " "++show x ++"\n" 
show (Branch val l r) = show val ++ "\n" ++" " ++ show l ++ " " ++ show r 

вот мой тест и вывод (что неверно):

Branch "<" (Branch "<" (Leaf 'a') (Leaf 'c')) (Branch "<" (Leaf 'g') (Branch  "<" (Leaf 'n') (Leaf 'y')))) 
"<" 
    "<" 
    'a' 
    'c' 
    "<" 
    'g' 
    "<" 
    'n' 
    'y' 

правый выход

"<" 
    "<" 
    'a' 
    'c' 
    "<" 
    'g' 
    "<" 
     'n' 
     'y' 

любая помощь будет здорово!

+0

Подождите. Это действительно скомпилировалось? Вам не хватает отступов строк 'show'. – Zeta

+4

Тот факт, что вы явно говорите, что не используете 'Data.Tree', говорит о том, что вы уже знаете о' drawTree'. Я могу понять, почему вы, возможно, захотите не смотреть на источник «drawTree» при создании своего собственного решения: ради радости изобретать идеи самостоятельно. Поэтому я не могу понять, почему вы попросите решение из других источников. Что лучше изучать идеи от какого-то случайного человека на StackOverflow по сравнению с изучением идей от автора пакета 'container'? –

+0

Это не хороший экземпляр «Show». Обычно вы должны копировать результат 'show' и использовать его как исходный код Haskell (возможно, с добавлением аннотаций типа). Это было бы лучше как независимая функция showTree. – dfeuer

ответ

4

Ваша функция отображения не имеет способа узнать, как глубоко отступать каждая строка. Предоставление пространства вашим поддеревьям не будет работать, потому что они обычно имеют более одной строки.

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

indent n x = concat (replicate n " ") ++ show x ++ "\n" 

f n (Leaf x) = indent n x 
f n (Branch val l r) = indent n val ++ f (n+1) l ++ f (n+1) r 

instance (Show a, Show b) => Show (Tree a b) where 
    show tree = f 0 tree 
+0

спасибо, что был прав. Я не мог понять, как принимать отступы как параметр – user3324236

+1

Здесь есть несколько странных вещей. Например, 'concat (replicate n" ")' is 'replicate n '''. – Zeta

+0

Да, но легко изменить на 2-пространственный отступ, или '' --- ["' или что угодно. Во всяком случае, важно то, что он эффективно отвечает на вопрос? Часто легче быть умнее, чем быть ясным ... – comingstorm

2

Рассмотрим следующий пример:

putStrLn ("First line" ++ " " ++ "\n" ++ "Second line") 

Выход

First line 
Second line 

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

1

Примечание: Это сообщение написано грамотным Haskell. Скопируйте его в свой любимый редактор, сохраните его как Tree.lhs или аналогичный и загрузите в GHCi.

Давайте попробуем немного другой подход. Вместо того чтобы показывать дерево в виде одной строки, мы создаем линии нашей строки:

> data Tree a b = Branch b (Tree a b) (Tree a b) | Leaf a 

> instance (Show a, Show b) => Show (Tree a b) where 
> show tree = magic (go tree) 
>  where 
>  go :: (Show a, Show b) => Tree a b -> [String] 

Не беспокоиться еще о magic, это будет иметь тип magic :: [String] -> String. Прежде всего сосредоточимся на go. Если мы хотим показать одну Leaf, мы должны только касаться одного значения:

>  go (Leaf v)  = [show v] 

Теперь о филиалах и их отступа. Мы можем показать значение, как и в случае Leaf:

>  go (Branch v l r) = show v 

, но мы должны убедиться, что фактические ветви имеют правильное количество пробелов впереди. Мы используем функцию (пока не определено) называется indent:

>      : indent (go l ++ go r) 

Теперь у нас есть почти все на месте: если indent делает то, что предполагает его название, он собирается отступа все строки в [String] возвращенного go l ++ go r на одно место. Поэтому ветви дерева всегда будут иметь еще одно пространство спереди, как сам корень.Все, что не хватает теперь indent и magic:

>  indent :: [String] -> [String] 
>  indent = map (' ':) 

Это было довольно легко. Конечно, вы можете обменять (' ':) с чем-то еще.

Это работа magic, чтобы взять все струны и приклеить их вместе. Так как вы хотите, чтобы отступы всего дерева на одном пространстве, давайте использовать indent в последний раз:

>  magic = unlines . indent 

И это все. Вот весь код для лучшего обзора:

instance (Show a, Show b) => Show (Tree a b) where 
    show tree = magic (go tree) 
    where 
     go (Leaf v)  = [show v] 
     go (Branch v l r) = show v : indent (go l ++ go r) 
     indent   = map (' ':) 
     magic    = unlines . indent 

Хорошая часть об этой технологии является то, что мы никогда не должны использовать явные уровни или указать количество пространств где-то. Мы можем просто прогуляться по дереву, создать наш [String] и иметь magic и indent сделать остальное для нас.

+0

Для тех, кто ждет еще одного стихотворения: nah. У меня был один на этой неделе, это было не так хорошо/в результате у меня не было настроения/чтобы написать еще один, который скоро. Следующее стихотворение, вероятно, в следующую среду, в зависимости от вопросов. – Zeta

+0

Недостатком этой техники, которая в настоящее время реализована, является то, что вызовы 'map' складываются. В этом конкретном приложении это просто отлично, но в целом эта модель - это то, что нужно учитывать, чтобы избежать серьезных проблем с производительностью. Я заметил, что реализация 'inits' в' base-4.7.0.1' (по существу такая же, как эталонная реализация в Haskell 98 Report) сделала что-то подобное, что привело меня к выводу, что это было ужасно медленно. Новая реализация довольно сложна для скорости, но есть простой способ сделать это, посчитав. – dfeuer

+0

Вы можете исправить проблему, не считая, я думаю, путем прокрутки строки отступа, добавляя к ней пробелы при рекурсивных вызовах. – dfeuer

0

Тип Tree меня раздражает, потому что это монада, но не может быть Monad. Позвольте мне исправить это для вас, перевернув аргументы типа:

data Tree b a = Branch b (Tree a b) (Tree a b) 
       | Leaf a 

display' :: (Show a, Show b) => String -> Tree b a -> String -> String 
display' prefix (Leaf a) = 
    (prefix ++) . shows a . ('\n' :) 
display' prefix (Branch v l r) = 
    (prefix ++) . shows v . ('\n' :) . display' prefix' l . display' prefix' r 
    where prefix' = ' ' : prefix 

display :: (Show a, Show b) => Tree b a -> String 
display t = display' ' ' t "" 
Смежные вопросы