Reputation: 4977
There's a generic function LanguagePrimitives.DivideByInt
to divide by int
without losing generic behavior, we can use it like this:
let inline Divideby2 n = LanguagePrimitives.DivideByInt n 2
val inline Divideby2 :
^a -> ^a when ^a : (static member DivideByInt : ^a * int -> ^a)
But there's no function called MultiplyByInt
to perform generic multiplication by int
. Is there anything to perform generic multiplication? Like this:
let inline MultiplyBy2 n = SomeGenericFunctionsModule.MultiplybyInt n 2;
P.S. we can always use some non-standard approach like:
let inline MultiplyByInt n m = seq { for i in 1..m -> n} |> Seq.sum
but I'm interested if it is possible to do in the right way.
Upvotes: 2
Views: 162
Reputation: 4977
I've received an answer from Don Syme (via fsbugs email) when I've asked about missing MutliplyByInt
and limited support of DivideByInt
:
Don's answer:
This operator exists to support “Seq.averageBy” etc. This represents pseudo-precise division of the total by the count. We didn’t extend the mechanism beyond what was needed for that.
So it looks like I've misunderstood the purpose of this mechanism.
Upvotes: 1
Reputation: 26174
I'm afraid there's no built-in function, but I can propose two alternative solutions:
type MulExtension = MulExtension of int with
static member (=>) (x:float , MulExtension y) = x * (float y)
static member (=>) (x:decimal, MulExtension y) = x * (decimal y)
static member (=>) (x:int64 , MulExtension y) = x * (int64 y)
// More overloads
let inline MultiplyByInt x y = x => MulExtension y
But you'll have to specify each type. I would rather use this function:
let inline MultiplyByInt x y =
let fromInt (n:int) : ^a when ^a : (static member (*) : ^a * ^a -> ^a) =
System.Convert.ChangeType(n, typeof<'a>) :?> 'a
x * (fromInt y)
I can't see any difference in performance between both methods.
Upvotes: 5
Reputation: 47904
let inline MultiplyByInt n (x: ^a) =
let zero : ^a = LanguagePrimitives.GenericZero
let one : ^a = LanguagePrimitives.GenericOne
if n = 0 then zero
else
let mutable q, r = System.Math.DivRem(abs n, 2)
let mutable y = x
while q > 0 do
y <- y + y
q <- q / 2
let y = if r = 0 then y else y + x
if n > 0 then y
else y * (zero - one)
Upvotes: 1
Reputation: 25516
I managed to get a solution in O(log(N))
which beats yours, but it still feels very ugly
let inline MulInt (m:^t) (n:int) =
let r : ^t ref = ref LanguagePrimitives.GenericZero
let addv : ^t ref= ref LanguagePrimitives.GenericOne
while ((int) !r) < n do
if int(!r + !addv + !addv) < n then
addv := !addv + !addv
else
r := !r + !addv
addv := LanguagePrimitives.GenericOne
!r * m
Using some of the library only features could make this a little better, but would result in warnings.
Note: This solution assumes that n
is representable in ^t
- i.e.
MulInt 2uy 5000
will loop forever
Upvotes: 2