#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]
, это может сработать.