Возьмем функцию типа (Monad m) => a -> m a
. Например:Составляющие действия монадов со складками
ghci> let f x = Just (x+1)
Я хотел бы иметь возможность применять его любое количество раз. Первое, что я попытался было
ghci> let times n f = foldr (>=>) return $ replicate n f
Проблема заключается в том, что она не будет работать для больших n
:
ghci> 3 `times` f $ 1
Just 4
ghci> 1000000 `times` f $ 1
Just *** Exception: stack overflow
Он не работает и в другую сторону:
ghci> let timesl n f = foldl' (<=<) return $ replicate n f
ghci> 3 `timesl` f $ 1
Just 4
ghci> 1000000 `timesl` f $ 1
Just *** Exception: stack overflow
Фактически, какие работы использует ($!)
оператор строгости
ghci> let timesStrict n f = foldr1 ((>=>) . ($!)) $ replicate n f
ghci> 3 `timesStrict` f $ 1
Just 4
ghci> 10000000 `timesStrict` f $ 1
Just 10000001
Есть ли более приятное или более идиоматическое решение? Или, вероятно, более строгий? Я все еще легко получаю переполнение стека, если f
- это тяжелая функция.
UPD: Я обнаружил, что написание times
в уместной форме не решает проблему составления тяжеловесного монадические действий ни. Это работает для ф х = Just (х + 1), но не в реальном мире:
times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)
Ну, все та же проблема, если вы запускаете больше итераций: GHCI> iterateM_n +1000000 (Just (+1).) 3 \ п Просто *** Исключение: переполнение стека \ п GHCi> iterateM_n»+1000000 (+) 0 (Just. (+1)) 3 \ n Just *** Исключение: переполнение стека \ n – sastanin
@jetxee Обновлено! –
Мне нравится использовать '-XBangPatterns' вместо' seq' :-) Во всяком случае, если 'f' строгий, тогда в моем ответе не нужно' >> =! '. Поскольку кажется, что OP 'f' не является, это может помочь. – ephemient