Складывание через Майбс в Хаскелле

В попытке узнать Haskell, я столкнулся с ситуацией, в которой я хочу сделать сгиб над списком, но мой аккумулятор - это Maybe. Функция, которую я складываю, тем не менее берет "извлеченное" значение в "Maybe", и если он терпит неудачу, все они терпят неудачу. У меня есть решение, которое я нахожу kludgy, но, зная как мало Haskell, как и я, я считаю, что должен быть лучший способ. Скажем, у нас есть следующая игрушечная проблема: мы хотим суммировать список, но по какой-то причине четверо - плохо, поэтому, если мы попытаемся суммировать в четыре раза в любое время, мы хотим вернуть Nothing. Мое текущее решение выглядит следующим образом:

import Maybe

explodingFourSum :: [Int] -> Maybe Int
explodingFourSum numberList =
    foldl explodingFourMonAdd (Just 0) numberList
    where explodingFourMonAdd =
        (\x y -> if isNothing x
                    then Nothing
                    else explodingFourAdd (fromJust x) y)

explodingFourAdd :: Int -> Int -> Maybe Int
explodingFourAdd _ 4 = Nothing
explodingFourAdd x y = Just(x + y)

Итак, в принципе, есть ли способ очистить или устранить лямбда в explodingFourMonAdd, используя какую-то разновидность Монады? Или как-то карри в → = оператора так, что сложение ведет себя как список функций, прикованных к → =?

Ответы

Ответ 1

Я думаю, вы можете использовать foldM

explodingFourSum numberList = foldM explodingFourAdd 0 numberList

Это позволяет вам избавиться от лишней лямбды и этого (всего 0) в начале.


BTW, проверьте hoogle, чтобы найти вокруг функции, которые вы действительно не помните имя для.

Ответ 2

Итак, в принципе, есть способ очистить или устранить лямбду в explodingFourMonAdd, используя какую-то разлочку Монады?

Yapp. В Control.Monad есть функция foldM, которая именно здесь вы хотите. Таким образом, вы можете заменить свой вызов на foldl на foldM explodingFourAdd 0 numberList.

Ответ 3

Вы можете использовать тот факт, что Maybe является монадой. Функция sequence :: [m a] -> m [a] имеет следующий эффект, если m - Maybe: если все элементы в списке Just x для некоторого x, результатом будет список всех этих justs. В противном случае результат Nothing.

Итак, вы сначала решаете для всех элементов, является ли это провалом. Например, возьмите свой пример:

foursToNothing :: [Int] -> [Maybe Int]
foursToNothing = map go where
  go 4 = Nothing
  go x = Just x

Затем вы выполните последовательность и fmap сгиб:

explodingFourSum = fmap (foldl' (+) 0) . sequence . foursToNothing

Конечно, вам нужно адаптировать это к вашему конкретному случаю.

Ответ 4

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

import Control.Monad
explodingFourSum xs = guard (all (/=4) xs) >> return (sum xs)

Это весь источник. Это решение красиво во многих отношениях: оно повторно использует много уже написанного кода, и оно прекрасно отражает два важных факта о функции (тогда как другие решения, размещенные здесь, объединяют эти два факта вместе).

Конечно, есть хотя бы одна веская причина не использовать эту реализацию. Другие упомянутые здесь решения проходят через входной список только один раз; это прекрасно взаимодействует с сборщиком мусора, позволяя только небольшим частям списка находиться в памяти в любой момент времени. С другой стороны, это решение дважды проходит xs, что предотвратит сбор сборщика мусора при первом проходе.

Ответ 5

Вы также можете решить свою игрушку следующим образом:

import Data.Traversable

explodingFour 4 = Nothing 
explodingFour x = Just x

explodingFourSum = fmap sum . traverse explodingFour 

Конечно, это работает только потому, что одного значения достаточно, чтобы знать, когда вычисление терпит неудачу. Если условие отказа зависит от обоих значений x и y в explodingFourSum, вам нужно использовать foldM.

BTW: Необычный способ написать explodingFour будет

import Control.Monad

explodingFour x = mfilter (/=4) (Just x)

Этот трюк работает и для explodingFourAdd, но менее читаем:

explodingFourAdd x y = Just (x+) `ap` mfilter (/=4) (Just y)