
Hi Markus I haven't tested the efficiency, but an unfold seems natural / idiomatic in this case. The Leibniz formula generates a list in the first step then sums it. Generating a list is often a perfect fit for an unfold. The code below seems quite close to the idea of the Leibniz formula (add 2 to the denominator, flip the sign at each step), sum and multiple by 4.
import Data.List (unfoldr)
leibniz n = (4 *) $ sum $ take n step
-- Note this unfoldr generates an infinite list (both cases produce Just values) ...
step :: [Double] step = unfoldr phi (True,1) where phi (sig,d) | sig == True = Just (1/d, (False,d+2)) | otherwise = Just (negate (1/d), (True,d+2))
Alternatively, an unfoldr with a stop condition might seem more natural, creating a bounded list. However, here it is a bit ugly as there are now 3 elements in the unfold state:
leibniz' n = (4 *) $ sum $ step' n
step' :: Int -> [Double] step' n = unfoldr phi (0,True,1) where phi (i,_,_) | i == n = Nothing phi (i,sig,d) | sig == True = Just (1/d, (i+1,False,d+2)) | otherwise = Just (negate (1/d), (i+1,True,d+2))
Best wishes Stephen