如何在没有手写递归的情况下摆脱Haskell的纯循环? [英] How do I break out of a pure loop in Haskell without hand-written recursion?

查看:69
本文介绍了如何在没有手写递归的情况下摆脱Haskell的纯循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个函数,该函数将遍历列表,更新一个累加器,直到该累加器达到某个条件或到达列表的末尾.例如,产品功能在累加器达到零时立即停止.

I want to write a function that goes through a list updating an accumulator until that accumulator reaches a certain condition or I get to the end of the list. For example, a product function that stops as soon as its accumulator reaches zero.

我知道如何通过手动编写递归来对其进行编码:

I know how to code it by writing the recursion by hand:

{-# LANGUAGE BangPatterns #-}

prod :: [Integer] -> Integer
prod xs =
    go 1 xs
  where
    go 0   _       = 0
    go !acc []     = acc
    go !acc (x:xs) = go (acc * x) xs

但是有没有办法使用折叠和其他高阶函数对此进行编码?

but is there a way to code this using folds and other higher order functions?

想到的一件事就是定义

mult 0 _ = 0
mult x y = x * y

,然后使用foldl'.但是,这并不会很早就解决,因此会浪费一些性能.

and then using foldl'. However, this doesn't break out early so its a bit wasteful of performance.

我们无法使用foldr,因为它以错误的顺序通过列表,并且其及早爆发"的方式是通过查看列表的元素而不是查看累加器(如果累加器的类型与列表元素的类型不同.

We can't use foldr since it goes through the list in the wrong order and its way of "breaking out early" is by looking at the elements of the list instead of looking at the accumulator (this would have mattered if the accumulator had a different type than the list elements).

推荐答案

一种简单的方法是在允许提前逃脱的monad中进行计算,例如EitherMaybe:

One simple way is to do the computation in a monad that allows to escape early, such as Either or Maybe:

{-# LANGUAGE BangPatterns #-}
import Data.Functor ((<$))
import Data.Maybe (fromMaybe)
import Control.Monad

prod :: [Integer] -> Integer
prod = fromMaybe 0 . prodM

-- The type could be generalized to any MonadPlus Integer
prodM :: [Integer] -> Maybe Integer
prodM = foldM (\ !acc x -> (acc * x) <$ guard (acc /= 0)) 1

在计算的每个步骤中,我们都会检查累加器是否为非零.如果它为零,则guard调用mplus,这将立即退出计算.例如,以下立即退出:

At each step of the computation we check if the accumulator is nonzero. And if it's zero, guard invokes mplus, which exits the computation immediately. For example the following exits immediately:

> prod ([1..5] ++ [0..])
0

这篇关于如何在没有手写递归的情况下摆脱Haskell的纯循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆