Question

I came across this code while reading "Programming F# 3.0":

type BitCounter =

    static member CountBits (x : int16) =
        let mutable x' = x
        let mutable numBits = 0
        for i = 0 to 15 do
            numBits <- numBits + int (x' &&& 1s)
        x' <- x' >>> 1
        numBits

    static member CountBits (x : int) =
        let mutable x' = x
        let mutable numBits = 0
        for i = 0 to 31 do
            numBits <- numBits + int (x' &&& 1)
        x' <- x' >>> 1
        numBits

    static member CountBits (x : int64) =
        let mutable x' = x
        let mutable numBits = 0
        for i = 0 to 63 do
            numBits <- numBits + int (x' &&& 1L)
        x' <- x' >>> 1
        numBits

I attempted to shorten this part by making an auxiliary function:

type BitCounter =

    static member CountBitsWithRangedMask x upBound typeConverter =
        seq { for i = 0 to upBound do yield 1 <<< i }
            |> Seq.map typeConverter
            |> Seq.map ((&&&) x)
            |> Seq.filter ((<>) (typeConverter 0))
            |> Seq.length

    static member CountBits (x : int16) =
        BitCounter.CountBitsWithRangedMask x 15 int16

    static member CountBits (x : int) =
        BitCounter.CountBitsWithRangedMask x 31 int

    static member CountBits (x : int64) =
        BitCounter.CountBitsWithRangedMask x 63 int64

But static member CountBits (x : int) caused a compilation error:

error FS0001: This expression was expected to have type
    int16
but here has type
    int

So I'm wondering if I can add some constraint like Integral a in Haskell on CountBitsWithRangedMask's first argument. Or is there any other solution that can simplify the original code?

Was it helpful?

Solution

You can use LanguagPrimitives.GenericOne and LanguagPrimitives.GenericZero combined with inline to acheive this.

let inline CountBitsWithRangedMask x upBound =
    seq { for i = LanguagePrimitives.GenericZero to upBound do yield LanguagePrimitives.GenericOne <<< i }
    |> Seq.map ((&&&) x)
    |> Seq.filter ((<>) (LanguagePrimitives.GenericZero))
    |> Seq.length

let test16 (x:System.Int16) = CountBitsWithRangedMask x 15
let test32 (x:System.Int32) = CountBitsWithRangedMask x 31
let test64 (x:System.Int64) = CountBitsWithRangedMask x 63

Using this we also don't need the typeConverter parameter as the compiler does everything automatically.

This is a reasonably general method for these sorts of problems.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top