Haskell: использовать карту только для определенного конструктора

#haskell #higher-order-functions

#haskell #функции более высокого порядка

Вопрос:

Я определил следующий тип данных:

 data Item = Item { name :: String, subitems :: [Itemunit] } deriving (Show, Eq)

data Itemunit = Itemunit Item | Dataunit Datau deriving (Show, Eq)

data Datau = Datau { dataName ::String } deriving (Show, Eq)
 

Теперь у меня есть «дерево», определенное этими элементами. Я хочу получить список строк, который содержит, например, имена всех элементов (или всех блоков данных). Моей первой идеей было использовать map, но как я могу фильтровать, чтобы он только рекурсивно вызывал себя, когда Itemunit использовал конструктор Itemunit?

Итак, в основном, что мне нужно, так это:

 getAsStringList = name : map getAsStringList subitems
 

но, очевидно, мне нужно убедиться, что getAsStringList вызывается только в том случае, если подраздел является Itemunit, а не если это Dataunit . Как я мог этого добиться?

Ответ №1:

Вы можете использовать понимание списка для фильтрации по конструкторам данных:

 getAsStringList :: Item -> [String]
getAsStringList (Item nm si) = nm : [ nmi | Itemunit ii <- si, nmi <- getAsStringList ii ] 

Нам нужно использовать nmi <- getAsStringList ii , так как это вернет список String s, и поэтому нам нужно перечислить его.

Вы также можете использовать concatMap :: Foldable t => (a -> [b]) -> t a -> [b] для объединения элементов после сопоставления:

 getAsStringList :: Item -> [String]
getAsStringList (Item nm si) = nm : concatMap getAsStringList [ ii | Itemunit ii <- si ] 

Однако вы используете один и тот же конструктор данных ( Dataunit ) дважды, вам следует использовать другой конструктор данных для одного из типов.

Комментарии:

1. @Richi: ваша функция должна принимать тип в качестве типа ввода. Это может быть Item или Dataunit , или что-то еще, но не два вместе. Вы также можете определить класс типов для однорангового полиморфизма.

2. @Richi: почему бы и нет? concatMap Проблема не в этом, а в функции сопоставления, которую вы используете. Так что, если вы используете другой concatMap , который отображает ... -> [String] , это может сработать.