Generic Function with Binary Operations

Generic function with binary operations

The Numeric
protocol requires addition, subtraction and multiplication operators,
but not a division operator.

I am not aware of a protocol which requires a division operator to which both integer and floating point types conform, therefore you have to
implement two overloaded functions:

func normalizeArray<T: FloatingPoint>(a: [T]) -> [T] { ... }

func normalizeArray<T: BinaryInteger>(a: [T]) -> [T] { ... }

Note that your implementation will crash if called with an empty
array, I'll suggest

func normalizeArray<T: BinaryInteger>(a: [T]) -> [T] {
guard let min = a.min(), let max = a.max() else {
return []
}
return a.map({ ($0 - min) / (max - min) })
}

Generic binary operation in a class definition?

You could use a class decorator to mutate your class and add them all in with the help of a factory function:

import operator

def natural_binary_operators(cls):
for name, op in {
'__add__': operator.add,
'__sub__': operator.sub,
'__mul__': operator.mul,
'__truediv__': operator.truediv,
'__floordiv__': operator.floordiv,
'__and__': operator.and_,
'__or__': operator.or_,
'__xor__': operator.xor
}.items():
setattr(cls, name, cls._make_binop(op))

return cls

@natural_binary_operators
class Vector(tuple):
@classmethod
def _make_binop(cls, operator):
def binop(self, other):
try:
return cls([operator(a, x) for a, x in zip(self, other)])
except:
return cls([operator(a, other) for a in self])

return binop

There are a few other ways to do this, but the general idea is still the same.

Generic constraint for bitwise operators

Generics are about allowing any Random class that any Programmer on the planet might throw in for T. However the numeric types are actually a very static list. I would never expect a programmer to make his own numeric type. Stuff with a overloaded Operators including binary ones? Maybe rarely.

So this is very much not a generic case. If you only write code for 2 - maybe 3 - types you should cover just about every generic in existence:

  • the highest range integer you have to expect signed Int64 IIRC
  • the highest range floating point you have to expect. IIRC, Decimal*.
  • optionally BigInteger, for when you have to expect really big numbers. However a short look revealed that none of Math class functions support BigInt values. They keep it to Decimal, Double and many smaler built in Numerics. So this case might have been dropped as to rare and to easy to get wrong.

*Correction: While Decimal has the highest amount of digits of precision and bigest size at 64 bit, Double has the bigger range. By an order of Magnitude, that itself has an order of Magnitude.

Binary comparison operators on generic types

Adding constraint to make sure that the type implements IComparable<T> is a way to go. However, you cannot use the < operator - the interface provides a method CompareTo to do the same thing:

public class MyClass<T> where T : IComparable<T> { 
public T MaxValue {
// Implimentation for MaxValue
}

public T MyMethod(T argument) {
if(argument.CompareTo(this.MaxValue) > 0){
// Then do something
}
}
}

If you needed other numeric operators than just comparison, the situation is more difficult, because you cannot add constraint to support for example + operator and there is no corresponding interface. Some ideas about this can be found here.

Are binary operators / infix functions in R generic? And how to make use of?

Yes, this is possible: use '+.<class name>' <- function().

Examples

'+.product' <- function(a, b) a * b
'+.expo' <- function(a, b) a ^ b

m <- 2; class(m) <- "product"
n <- 3; class(n) <- "product"

r <- 2; class(r) <- "expo"
s <- 3; class(s) <- "expo"

m + n # gives 6
r + s # gives 8

safety notes

The new defined functions will be called if at least one of the arguments is from the corresponding class m + 4 gives you 2 * 4 = 8 and not 2 + 4 = 6. If the classes don't match, you will get an error message (like for r + m). So all in all, be sure that you want to establish a new function behind such basic functions like +.

How to define generic reductions using any binary Operator example OR or AND, custom function?

The helpers sum, product, etc are not actually built-ins. They are just helpers written in the Halide front-end language itself, so you can define more if you like, or make a generic that takes a binary operator. I would start by looking at https://github.com/halide/Halide/blob/master/src/InlineReductions.cpp

The bulk of the magic is just the automatic capturing of free variables, the rest is just like the code you wrote.

Usage of arithmetic operants (+, -, /, *) with generic types

For this you need to create a new protocol that lets Swift know any instance of T can have numeric operators performed on it, for example:

protocol NumericType: Equatable, Comparable {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
func %(lhs: Self, rhs: Self) -> Self
init(_ v: Int)
}

extension Double : NumericType {}
extension Int : NumericType {}

Source: What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift?

Now when you define your MathStatistics class:

class MathStatistics<T: NumericType> {
var numbers = [T]()

func average() -> T? {
if numbers.count == 0 {
return nil
}

let sum = numbers.reduce(T(0)) { $0 + $1 }
return sum / T(numbers.count)
}
}

Now you can use MathsStatistics like so:

let stats = MathStatistics<Int>()
stats.numbers = [1, 3, 5]
println(stats.average()) // Prints: Optional(3)

How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?

Let's break down your example a bit:

fn cube<T: Mul>(x: T) -> T {
let a = x * x;
let b = a * x;
b
}

What are the types of a and b? In this case, the type of a is <T as std::ops::Mul>::Output — sound familiar from the error message? Then, we are trying to multiply that type by x again, but there's no guarantee that Output is able to be multiplied by anything!

Let's do the simplest thing and say that T * T needs to result in a T:

fn cube<T: Mul<Output = T>>(x: T) -> T {
x * x * x
}

Unfortunately, this gives two similar errors:

error[E0382]: use of moved value: `x`
--> src/lib.rs:6:9
|
6 | x * x * x
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because `x` has type `T`, which does not implement the `Copy` trait

Which is because the Mul trait takes arguments by value, so we add the Copy so we can duplicate the values.

I also switched to the where clause as I like it better and it is unwieldy to have that much inline:

fn cube<T>(x: T) -> T
where
T: Mul<Output = T> + Copy
{
x * x * x
}

See also:

  • How do I implement the Add trait for a reference to a struct?
  • How to write a trait bound for adding two references of a generic type?

Generic Constraint for allowing bitwise operations?

There is no appropriate generic type constraint for this situation. You can apply bitwise operations to ints, so convert the enum value to int. T cannot be cast to int directly; however, you can make the detour over object.

public static string ToFlags<T>(this T val)
where T : struct
{
return string.Join(",", Enum.GetValues(typeof(T))
.Cast<T>()
.Select(enumEntry => ((int)(object)enumEntry & (int)(object)val) > 0
? enumEntry.ToString() : "")
.Where(f => !string.IsNullOrWhiteSpace(f)));
}

Note: There is a similar situation with numbers. You can't say where T : number and apply numeric operators on values of type T.

Also you can make val a T and you don't have to pass the type of T as method argument. You are passing it as generic type argument already. This is sufficient. The compiler is also smart enough to infer T, so you don't have to specify it when calling the method.

// Test
Flags item = Flags.COMMAND_ACTION | Flags.COMMAND_MSG;
Console.WriteLine(item.ToFlags());

You can specify struct as generic type constraint, as struct stands for value types. It is not perfect, but better than no constraint at all.



Related Topics



Leave a reply



Submit