Duck
Duck

Reputation: 35953

Converting a function to deal with Double and Float... using FloatingPoint type?

I have seen a similar question on SO but it does not answer everything I am asking here.

I have a function like this:

  func myFunctionDouble (_ valores:[Double] = []) -> Array<Double> {

    let numeroItens = valores.count
    var c = [Double](repeating: 0,
                     count: numeroItens)

    let factor : Double = Double.pi / Double(numeroItens)

    for i in 0..<numeroItens {
      var sum : Double = 0
      for j in 0..<numeroItens {
        sum = sum + valores[j] * cos ((Double(j) + 0.5) * Double(i) * factor)
      }
      c[i] = sum
    }
    return c;
  }

I want to transform this function to deal with Double and Float.

I thought about replacing Double with FloatingPoint but it will simply not work and give me a bunch of errors.

There is no FloatingPoint.pi or array c cannot be declared like

var c = [FloatingPoint](repeating: 0, count: numeroItens)

is there a way to do that?

and more than that, how do I declare a var or an array to be of FloatingPoint type, I mean, to accept both Float and Double?

Upvotes: 0

Views: 73

Answers (2)

Alexander
Alexander

Reputation: 63252

Added on to @rmaddy's solution, you can drastically simplify your code by using higher order functions to remove all the "noise", things which are necessary for the computation, but aren't actually that specific to your computation.

func myFunction<T: FloatingPoint>(_ valores:[T] = []) -> Array<T>
    where T: ExpressibleByFloatLiteral, T: Cosineable {
    let factor: T = T.pi / T(valores.count)

    return valores.indices.map { i in
        return valores.enumerated()
            .map { (offset, element) in
                element * ((T(offset) + 0.5) * T(i) * factor).cosine()
            }
            .reduce(0, +)
    }
}

From here, you can start labelling the terms better, and providing more context as to what these do.

The term element * ((T(offset) + 0.5) * T(i) * factor).cosine() is faaaaarrrrr too complicated for a single expression. Why 0.5? What's factor? Why are we taking the cosine? It's very non-obvious. Not only will strangers to this code not understand this, but over time, you yourself will forget about this and become a "stranger" to the code.

Upvotes: 2

rmaddy
rmaddy

Reputation: 318794

Following the example set forth in Rob's answer to How to use FloatingPoint generic type for Float/Double, you can do the following:

import Darwin // minimal necessary import
// importing Foundation, AppKit or UIKit also implicitly import Darwin

protocol Cosineable {
    func cosine() -> Self
}

extension Float: Cosineable {
    func cosine() -> Float { return cos(self) }
}

extension Double: Cosineable {
    func cosine() -> Double { return cos(self) }
}

func myFunction<T: FloatingPoint>(_ valores:[T] = []) -> Array<T> where T: ExpressibleByFloatLiteral, T: Cosineable {

    let numeroItens = valores.count
    var c = [T](repeating: 0,
                     count: numeroItens)

    let factor : T = T.pi / T(numeroItens)

    for i in 0..<numeroItens {
        var sum : T = 0
        for j in 0..<numeroItens {
            sum += valores[j] * ((T(j) + 0.5) * T(i) * factor).cosine()
        }
        c[i] = sum
    }
    return c
}

Upvotes: 2

Related Questions