3
\$\begingroup\$

I've written a small function for solving simple quadratic equations:

class EquationSolver
  def solve(x, *args)
    args.reverse.map.with_index { |coefficient, index| coefficient * x ** index }.reduce { |result, element| result + element }
  end
end

To calculate f(3) for f(x)=3x3−2x2−x+5, one would write:

puts EquationSolver.new.solve(3, 3, -2, -1, 5)

However, is there a more elegant version of my function, more like reduce.with_index or something similar?

\$\endgroup\$
2
\$\begingroup\$

Some notes:

  • If you use a OOP approach, it seems more logical to use methods.
  • Use arrays to group values that go together.
  • I don't think solve is the correct term, that's when you are finding the roots of a function, here you're just evaluating it at a given point.

I'd write:

class Polynomial
  attr_accessor :coefficients 

  def initialize(coefficients)
    self.coefficients = coefficients.reverse
  end

  def evaluate(x)
    coefficients.map.with_index { |k, power| k * (x**power) }.reduce(0, :+)
  end
end

polynomial = Polynomial.new([3, -2, -1, 5])
puts polynomial.evaluate(3) #=> 65
\$\endgroup\$
  • \$\begingroup\$ Pretty good. Btw, I think you don't need the 0 in reduce(0, :+). \$\endgroup\$ – Alex Popov Nov 21 '13 at 10:38
  • 1
    \$\begingroup\$ @AlexPopov: Well, it's just to consider the case f(x) = 0. \$\endgroup\$ – tokland Nov 21 '13 at 10:46
  • \$\begingroup\$ Good point, didn't think about that. \$\endgroup\$ – Alex Popov Nov 21 '13 at 10:50
  • \$\begingroup\$ Btw, if you have zero = Polynomial.new 0, the code throws undefined method `reverse' for 0:Fixnum (NoMethodError). \$\endgroup\$ – Alex Popov Nov 21 '13 at 10:59
  • 1
    \$\begingroup\$ @Alex: Yes. Personally I don't like this because it makes difficult to add extra arguments. IMHO the coefficients should be grouped, and an array is the most obvious way to do it. \$\endgroup\$ – tokland Nov 21 '13 at 14:40
2
\$\begingroup\$

Yes, there is a more elegant version. Here it is:

class EquationSolver
  def solve(x, *args)
    args.reverse.each_with_index.reduce(0) { |result, (coefficient, index)| result + coefficient * x ** index }
  end
end

It's possible to do this because iterator methods like each_with_index, when called without a block, return an Enumerator object on which you can call all the methods of the Enumerable.

In fact, an even shorter solution is obtained by using a variation of reduce to use a symbolic operator:

class EquationSolver
  def solve(x, *args)
    args.reverse.map.with_index { |coefficient, index| coefficient * x ** index }.inject(:+)
  end
end
\$\endgroup\$
  • \$\begingroup\$ 10x, I had figured out the first version you proposed, but I didn't pass a 0 to reduce as a starting parameter, so it was giving me no implicit conversion of Fixnum into Array \$\endgroup\$ – Alex Popov Nov 21 '13 at 9:32
  • \$\begingroup\$ Yep, it tends to happen. I'd any day prefer my second variation since it makes for an easy reading. \$\endgroup\$ – Chandranshu Nov 21 '13 at 9:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.