Skip to main content
Tweeted twitter.com/StackCodeReview/status/690100296308133888
edited tags
Link
200_success
  • 145.7k
  • 22
  • 191
  • 481
Source Link
Alexis King
  • 3.2k
  • 2
  • 21
  • 35

Approximating π via Monte Carlo simulation

Inspired by a tweet linked to me by a friend and a Haskell implementation by her for the same problem, I decided to try my hand at approximating the value of π using everything in the Haskell standard library I could find for the job. Here’s what I came up with:

module Pi where

import Data.List (genericLength)
import Control.Arrow (Arrow, (<<<), (***), arr)
import System.Random (newStdGen, randoms)

type Point a = (a, a)

chunk2 :: [a] -> [(a, a)]
chunk2 []      = []
chunk2 [_]     = error "list of uneven length"
chunk2 (x:y:r) = (x, y) : chunk2 r

both :: Arrow arr => arr a b -> arr (a, a) (b, b)
both f = f *** f

unsplit :: Arrow arr => (a -> b -> c) -> arr (a, b) c
unsplit = arr . uncurry

randomFloats :: IO [Float]
randomFloats = randoms <$> newStdGen

randomPoints :: IO [Point Float]
randomPoints = chunk2 <$> randomFloats

isInUnitCircle :: (Floating a, Ord a) => Point a -> Bool
isInUnitCircle (x, y) = x' + y' < 0.25
  where x' = (x - 0.5) ** 2
        y' = (y - 0.5) ** 2

lengthRatio :: (Fractional c) => [b] -> [b] -> c
lengthRatio = curry (unsplit (/) <<< both genericLength)

approximatePi :: [Point Float] -> Float
approximatePi points = circleRatio * 4.0
  where circlePoints = filter isInUnitCircle points
        circleRatio = circlePoints `lengthRatio` points

main :: IO ()
main = do
  putStrLn "How many points do you want to generate to approximate π?"
  numPoints <- read <$> getLine
  points <- take numPoints <$> randomPoints
  print $ approximatePi points

I’m interested in a general review, but I’m especially curious about my use of arrows: is there a better way to write lengthRatio? Are anything like both and unsplit provided anywhere in the standard library? If not, do any packages help?