У меня такая проблема:
Учитывая тип data T = T (Maybe Int)
, как я могу отфильтровать список, получая значения, отличные от Nothing
?
Вход
a = [T (Just 3), T Nothing, T (Just 4)]
Желаемый результат
b = [T (Just 3), T (Just 4)]
Я пробовал что-то вроде:
b = filter (\x-> x@(Just t)) a
... думая, что я могу фильтровать по совпадению с образцом, но получаю сообщение об ошибке:
Pattern syntax in expression context: x@(Just t)
Did you mean to enable TypeApplications?
Позже я хочу сможет распаковать внутреннее значение (в Just
) и использовать его соответственно.
Я думаю, что здесь мы можем лучше использовать семантику сопоставления с образцом для понимания списка:
result = [ e | e@(T (Just _)) <- a]
Таким образом, здесь мы перечисляем элементы e
в a
, и если сопоставление с образцом с T (Just _)
успешно, мы выводим его в список результатов.
Однако, если вы хотите распаковать значений, инкапсулированных в T (Just x)
, мы, таким образом, можем выполнить сопоставление с образцом и получить обернутый элемент:
result = [ e | T (Just e) <- a]
Таким образом, это будет не только «фильтровать» значения, но и одновременно распаковывать их. Таким образом, T Nothing
игнорируются, сохраняются только упакованные T (Just e)
, а соответствующие e
попадают в список.
Это отвечает на букву вопроса, но я уверен, что [e | T (Just e) <- a]
лучше отвечает духу вопроса.
Да, это именно то, что я хотел позже распаковать внутреннее значение и использовать его соответственно.
@BercoviciAdrian: см. Обновление.
Не могли бы вы рассказать мне, почему я получаю Pattern bindings not allowed in instance declaration
вместо instance Show File where ; (Dfile ls)=Data.List.intercalate ',' [show e|T (Just e) <- ls]
. Может, заслуживает отдельного вопроса, но я не уверен. хотя.
@BercoviciAdrian Вы, наверное, имеете в виду ... where show (Dfile ls)= ...
Да, извините, я забыл это добавить.
Сопоставление с образцом работает только в аргумент функции, но не в теле. Шаблон, который вам нужно сопоставить, - это T
с катаморфизмом, подобным maybe
, для преобразования обернутого значения в логическое.
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (\(T x) -> maybe False (const True) x) a
[T (Just 3),T (Just 4)]
Однако обратите внимание, что maybe False (const True)
уже определен как Data.Maybe.isJust
.
Prelude> import Data.Maybe
Prelude> filter (\(T x) -> isJust x) a
[T (Just 3),T (Just 4)]
Вы можете упростить предикат, если у вас есть функция типа T -> Maybe Int
для компоновки с isJust
. Например:
Prelude> data T = T { getT :: Maybe Int } deriving Show
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (isJust . getT) a
[T {getT = Just 3},T {getT = Just 4}]
Если вы хотите получить список значений Int
в вашем списке T
(набранный таким образом, как [T] -> [Int]
), тогда mapMaybe
из Data.Maybe
уже делает почти то, что вы хотите. Все, что вам нужно, кроме этого, - это функция распаковки типа T -> Maybe Int
.
import Data.Maybe ( mapMaybe )
data T = T (Maybe Int)
deriving (Eq, Show)
unT :: T -> Maybe Int
unT (T x) = x
filterTs = mapMaybe unT
Потом:
λ a = [T (Just 3), T Nothing, T (Just 4)]
a :: [T]
λ filterTs a
[3,4]
it :: [Int]
На мой взгляд, использование этой операции фильтра типа [T] -> [Int]
более полезно, чем возвращение значений T
, содержащих значения, отличные от Nothing
; причина в том, что даже если вы отфильтруете a
до [T (Just 3), T (Just 4)]
, тогда код, который имеет дело с этим более поздним еще, должен соответствовать шаблону на Just
, чтобы получить значения Int
, даже если вы знаете, что Nothing
1 никогда не будет, потому что T
все еще жестко запрограммирован, чтобы содержать Nothing
.
Как правило, если вы выполняете фильтрацию (или по умолчанию, и т. д.), Чтобы гарантировать отсутствие случая, вам следует подумать о преобразовании в тип, который больше не имеет случая. Обычно это упрощает работу с результирующими данными (например, нет необходимости в сопоставлении с шаблоном или fmap
, чтобы попасть внутрь избыточных слоев), а также помогает избежать ошибок.
Также есть catMaybes :: [Maybe a] -> [a]
, который выполняет эту «фильтрацию Nothing
без отображения», но, поскольку вы отображаете для распаковки, конструктор T
лучше подходит.
1 И эта ситуация «Я знаю, что здесь никогда не будет mapMaybe
, поэтому мне не нужно с этим справляться» - очень богатый источник ошибок, которые могут сломаться, когда что-то изменит этот скрытый инвариант в будущем. Так что написание кода, использующего преимущества этого знания о том, что Nothing
«не может» быть, даже не лучшая идея; вы должны еще обрабатывать оба случая!
Спасибо за ваши наблюдения, они очень ценны для меня как новичка. Я хотел знать на практике, что всякий раз, когда у меня есть вложенные maybe
-ы в свои типы, как в примере oyu, поставляемом с T
, я должен создавать методы, соответствующие шаблону -сопоставить и получить значение поля? (в вашем примере unT
)?
вы можете использовать
filter (\x -> case x of (Just _) -> True ; _ -> False) a
или дажеfilter (\case (Just _) -> True ; _ -> False)
(последний с расширением LambdaCase).