Как определить лямбда-функцию, которая фильтрует список на основе подтипа типа суммы? - PullRequest
0 голосов
/ 27 февраля 2019

Пример взят из «программирования на Haskell из первых принципов». Цель функции фильтра - избавиться от всех объектов, кроме объектов типа «DbDate».

На GitHub Сомона я нашел способфильтровать типы сумм с пониманием списка и сопоставлением с образцом (1).Теперь я пытаюсь найти способ переопределить этот фильтр с помощью лямбда-функции (2) или обычной функции "case of" of "if then".Я не знаю, как правильно проверять тип аргументов функции, когда я имею дело с пользовательским типом данных.

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

import Data.Time

data DatabaseItem = DbString String
                  | DbNumber Integer
                  | DbDate   UTCTime
                  deriving (Eq, Ord, Show)

--List that needs to be filtered
theDatabase :: [DatabaseItem]
theDatabase =
  [ DbDate (UTCTime (fromGregorian 1911 5 1)
                    (secondsToDiffTime 34123))
  , DbNumber 9001
  , DbString "Hello, world!"
  , DbDate (UTCTime (fromGregorian 1921 5 1)
                    (secondsToDiffTime 34123))
  ]



--1 works fine, found on someone's git hub
filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate dbes = [x | (DbDate x) <- dbes]

--2 Looking for the eqivalents with lambda or "case" or "if then"
--pattern is not satisfactory

filterDbDate :: [DatabaseItem] -> [UTCTime]
filterDbDate dbes = filter (\(DbDate x) -> True) theDatabase

Ответы [ 3 ]

0 голосов
/ 28 февраля 2019

filter имеет тип (a -> Bool) -> [a] -> [a], поэтому он не может изменить тип вашего списка.

Согласно Отчету Haskell 98 (раздел 3.11) понимание спискаиспользуется в коде, который вы нашли на github desugars:

filterDbDate2 :: [DatabaseItem] -> [UTCTime]
filterDbDate2 dbes = let extractTime (DbDate time) = [time]
                         extractTime _             = []
                     in concatMap extractTime theDatabase

Вы можете переписать extractTime, чтобы использовать case ... of:

filterDbDate3 :: [DatabaseItem] -> [UTCTime]
filterDbDate3 dbes = let extractTime item = case item of (DbDate time) -> [time]
                                                         _             -> []
                     in concatMap extractTime theDatabase

И заменить его на лямбду:

filterDbDate4 :: [DatabaseItem] -> [UTCTime]
filterDbDate4 dbes = concatMap (\item -> 
    case item of 
        (DbDate time) -> [time]
        _             -> []) 
    theDatabase

Но imho ваше оригинальное решение с использованием понимания списка выглядит лучше всего:

filterDbDate dbes = [x | (DbDate x) <- dbes]
0 голосов
/ 28 февраля 2019

Как @Niko уже сказал в своем ответе, filter не может изменить тип.Однако есть вариант filter, который может: Data.Maybe.mapMaybe :: (a -> Maybe b) -> [a] -> [b].Идея состоит в том, что если вы хотите сохранить элемент, вы возвращаете Just newvalue из лямбды;в противном случае вы возвращаете Nothing.В этом случае вы могли бы переписать filterDbDate как:

import Data.Maybe

filterDbDate dbes = mapMaybe (\x -> case x of { DBDate d -> Just d; _ -> Nothing }) dbes

Лично я бы сказал, что это второй самый ясный способ написания этой функции (после метода понимания списка).

0 голосов
/ 27 февраля 2019

Вы действительно были на правильном пути, поскольку сопоставление с образцом является простым способом решения этой проблемы, однако вы получите ошибку, поскольку сопоставление с образцом не является исчерпывающим.Также обратите внимание, что если вы используете фильтр, вы все равно получите список [DatabaseItem], поскольку фильтр никогда не меняет тип.Однако вы можете использовать map, чтобы сделать это.Итак:

Случай

Вы можете иметь case .. of внутри вашей лямбда-функции:

filterDbDate' :: [DatabaseItem] -> [UTCTime]
filterDbDate' = map (\(DbDate x) -> x) .filter (\x ->
  case x of
    DbDate x -> True
    _        -> False)

Рекурсия + Сопоставление с образцом

Однако я думаюэто проще сделать с помощью рекурсии:

filterDbDate'' :: [DatabaseItem] -> [UTCTime]
filterDbDate'' [] = []
filterDbDate'' ((DbDate d):ds) = d : filterDbDate ds
filterDbDate'' (_:ds)          =     filterDbDate ds

Best Way

Если честно, когда вам нужно смешать фильтр и карту, и ваши лямбды просты, как эта, перечислитетвой самый чистый путь:

filterDbDate ds = [d | (DbDate d) <- ds]
...