HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Math Equation Solver

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
solvermathequation

Problem

I made this math script, where you enter an equation and it solves it for you. I was wondering what you thought of the code, and what I could have done better or what doesn't follow Ruby best practices. The code is also on Github here if you want to see the readme and how to use define_equation()

```
#!/usr/bin/env ruby

require "readline"
require "cmath"

# PREFIXES = { -15 => "f", -12 => "p", -9 => "n",
# -6 => "µ", -3 => "m", 0 => "",
# 3 => "k", 6 => "M", 9 => "G",
# 12 => "T", 15 => "P", 18 => "E" }

def define_equation(file)
File.foreach(file) { |line|
name, var, equation = line.split(":")
Equations.class_eval %[
def #{name}(#{var})
#{equation}
end
]
}
end

def all_formulas
(Equations.instance_methods(false) - [:method_missing])
.map { |i| i.to_s.gsub(/_/, "-") }
end

class Equations

C = 3e8

def m_with_hz(frequency)
C / frequency
end

alias_method :hz_with_m, :m_with_hz

def qudrtc_frml_with_a_b_c(a, b, c)
positive = (-b + CMath.sqrt(b ** 2 - 4 a c)) / 2 * a
negative = (-b - CMath.sqrt(b ** 2 - 4 a c)) / 2 * a

# possibly add positive and negative .to_r as well as the float
[positive, negative]
end

def pythgrn_thrm_with_a_b(a, b)
a = Math.sqrt(a 2 + b 2)
a = "sqrt(#{(a ** 2).round})" unless a.to_i == a
a
end

def pythgrn_thrm_with_b_c(b, c)
a = Math.sqrt(c 2 - b 2)
a = "sqrt(#{(a ** 2).round})" unless a.to_i == a
a
end

def method_missing(m, *args, &block)
"That equation doesn't exist here. (yet)"
end
end

class Cli

attr_reader :equations

def initialize
@equations = Equations.new
end

def help
["To use an equation, just type something like this: hz-with-m 100",
"Thats 100 meters, converted to hertz",
"Or something like this: pythgrn-

Solution

There is a bug in your quadratic formula. Watch your operator associativity!

I'm not a fan of the method names qudrtc_frml_with_a_b_c and pythgrn_thrm_with_a_b, and I think that the poor naming is a symptom of a poor class design.

First of all, drpng vwls makes your interface hard for others to use. Learn from Ken Thompson's greatest regret.

Next, I would remove "formula" and "theorem" from the method names. The caller generally doesn't care how you solve the quadratic equation — whether you do it by using the quadratic formula, completing the square, etc. Similarly, the fact that there is a Pythagorean Theorem is unimportant; the focus should be on the subject of the Pythagorean Theorem, which is the right triangle.

Third, I question the benefit of adding all of these unrelated equations as methods of one class.

Finally, the suffixes …_with_a_b and …_with_b_c for your Pythagorean solver suggest that your solution is too rigid. It would be nice if the user could enter all but one of the variables as constraints, and ask the program to solve for the remaining unknown.

I suggest the following design. It takes advantage of one of Ruby's strengths, which is the ability to craft a human-friendly domain-specific language.

require 'cmath'

class QuadraticEquation
  attr_writer :a, :b, :c, :x

  def x
    positive = (-@b + CMath.sqrt(@b ** 2 - 4 * @a * @c)) / (2 * @a)
    negative = (-@b - CMath.sqrt(@b ** 2 - 4 * @a * @c)) / (2 * @a)
    [positive, negative]
  end

  def a
    (-@b * @x - @c) / (@x ** 2)
  end

  def b
    (-@a * @x ** 2 - @c) / @x
  end

  def c
    -@a * @x ** 2 - @b * @x
  end
end

class RightTriangle
  attr_writer :a, :b, :c

  def a
    Math.sqrt(@c ** 2 - @b ** 2)
  end

  def b
    Math.sqrt(@c ** 2 - @a ** 2)
  end

  def c
    Math.sqrt(@a ** 2 + @b ** 2)
  end

  alias :hypotenuse  :c
  alias :hypotenuse= :c=
end


Here it is in use:

irb(main):001:0> require 'formula'
=> true
irb(main):002:0> triangle = RightTriangle.new
=> #
irb(main):003:0> triangle.hypotenuse = 5
=> 5
irb(main):004:0> triangle.a = 3
=> 3
irb(main):005:0> triangle.b
=> 4.0

Code Snippets

require 'cmath'

class QuadraticEquation
  attr_writer :a, :b, :c, :x

  def x
    positive = (-@b + CMath.sqrt(@b ** 2 - 4 * @a * @c)) / (2 * @a)
    negative = (-@b - CMath.sqrt(@b ** 2 - 4 * @a * @c)) / (2 * @a)
    [positive, negative]
  end

  def a
    (-@b * @x - @c) / (@x ** 2)
  end

  def b
    (-@a * @x ** 2 - @c) / @x
  end

  def c
    -@a * @x ** 2 - @b * @x
  end
end

class RightTriangle
  attr_writer :a, :b, :c

  def a
    Math.sqrt(@c ** 2 - @b ** 2)
  end

  def b
    Math.sqrt(@c ** 2 - @a ** 2)
  end

  def c
    Math.sqrt(@a ** 2 + @b ** 2)
  end

  alias :hypotenuse  :c
  alias :hypotenuse= :c=
end
irb(main):001:0> require 'formula'
=> true
irb(main):002:0> triangle = RightTriangle.new
=> #<RightTriangle:0x007fe14301f500>
irb(main):003:0> triangle.hypotenuse = 5
=> 5
irb(main):004:0> triangle.a = 3
=> 3
irb(main):005:0> triangle.b
=> 4.0

Context

StackExchange Code Review Q#47749, answer score: 2

Revisions (0)

No revisions yet.