patternrubyMinor
Representing a long regular expression in a Polynomial class
Viewed 0 times
expressionpolynomialregularlongclassrepresenting
Problem
I wrote a simple
There are a few things that bother me in the
-
Since all but one of the
In that case though the regexp become quite unreadable. Which of the two is better?
-
Regarding the
Polynomial class:class Polynomial
def initialize(coefficients)
@coefficients = coefficients.reverse
end
def to_s
return '0' if @coefficients.all?(&:zero?)
@coefficients.map.with_index do |k, index|
" #{Polynomial.sign(k)} #{k.abs}x^#{index}"
end.reverse.join.strip # removes whitespace at the front
.gsub(/\A\+\s/, '') # removes + if first coefficient is positive
.gsub(/x\^0/, '') # removes x to the power of 0
.gsub(/\s(\+|-)\s0x\^\d/, '') # removes 0 coefficients
.gsub(/\s(\+|-)\s0/, '') # removes 0 at the end of the expression
.gsub(/\^1/, '') # removes power of 1
.gsub(/1x/, 'x')
end
def evaluate(x)
@coefficients.map.with_index { |k, index| k * (x**index) }.reduce(0, :+)
end
private
def self.sign(integer)
integer >= 0 ? '+' : '-'
end
endThere are a few things that bother me in the
to_s method:- Is it bad that I wrote the short comments explaining what each
gsubdoes? Bad in terms of "Would you do this in production code?".
-
Since all but one of the
gsubs do the same, I could replace them with a single gsub, containing a long regexp with lots of ors (|):.gsub(/\A\+\s|x\^0|\s(\+|-)\s0x\^\d|\s(\+|-)\s0|\^1/, '')In that case though the regexp become quite unreadable. Which of the two is better?
-
Regarding the
map.with_index part:- I use
do/end, but than the chaining of methods on theenddoesn't look good.
- If I use curly braces on multiple lines, I break the convention curly braces for single-line blocks,
do/endfor multi-line blocks.
- If I use curly braces with a single-line block, the line will be more than 80 characters long. So which of the three variants would you use?
Solution
Rather than construct the polynomial string incorrectly, then fix it with regex's, it may be easier to contruct it directly. This is one way you could do that.
For this example value of
First save each coeffient's index with its value:
Next, select only pairs for which the value is non-zero or the exponent is zero:
Notice that
I'm not certain if I followed the formatting rules exactly, but if I did not, it should be easy to modify the code to conform.
(Edited to fix boo-boos spotted by @200_success.)
class Polynomial
def initialize(coefficients)
@coefficients = coefficients
end
def to_s
coeffs = @coefficients.each_with_index.select { |c,k| c.nonzero? || k==0 }.reverse
coeffs.each_with_object('') do |(c,k), s|
cneg = (c.to_f 0)
s 0
s 1
end
end
end
coefficients = [2.1, 3, 1.0, 0, -6.1, 1, -5.3, 0.2]
p Polynomial.new(coefficients).to_s
# => 0.2x^7 - 5.3x^6 + x^5 - 6.1x^4 + x^2 + 3x + 2.1For this example value of
coefficients, this is what's happening:First save each coeffient's index with its value:
a = @coefficients.each_with_index
# => [[2.1,0], [3,1], [1.0,2], [0,3], [-6.1,4], [1,5], [-5.3,6], [0.2,7]]Next, select only pairs for which the value is non-zero or the exponent is zero:
b = a.select { |c,k| c.nonzero? || k==0 }
# => [[2.1, 0], [3, 1], [1.0, 2], [-6.1, 4], [1, 5], [-5.3, 6], [0.2, 7]]Notice that
[0,3] has been removed. Now reverse the elements of the array: coeffs = b.reverse
# => [[0.2, 7], [-5.3, 6], [1, 5], [-6.1, 4], [1.0, 2], [3, 1], [2.1, 0]]each_with_object('') creates an empty string, designated s in the block. It is then a simple matter to consider each element of each term in the polynomial to build up the string.I'm not certain if I followed the formatting rules exactly, but if I did not, it should be easy to modify the code to conform.
(Edited to fix boo-boos spotted by @200_success.)
Code Snippets
class Polynomial
def initialize(coefficients)
@coefficients = coefficients
end
def to_s
coeffs = @coefficients.each_with_index.select { |c,k| c.nonzero? || k==0 }.reverse
coeffs.each_with_object('') do |(c,k), s|
cneg = (c.to_f < 0.0)
if [c,k] == coeffs.first
s << '-' if cneg
else
s << (cneg ? ' - ' : ' + ')
end
s << "#{c.abs}" unless (c.abs.to_f == 1.0 && k > 0)
s << "x" if k > 0
s << "^#{k}" if k > 1
end
end
end
coefficients = [2.1, 3, 1.0, 0, -6.1, 1, -5.3, 0.2]
p Polynomial.new(coefficients).to_s
# => 0.2x^7 - 5.3x^6 + x^5 - 6.1x^4 + x^2 + 3x + 2.1a = @coefficients.each_with_index
# => [[2.1,0], [3,1], [1.0,2], [0,3], [-6.1,4], [1,5], [-5.3,6], [0.2,7]]b = a.select { |c,k| c.nonzero? || k==0 }
# => [[2.1, 0], [3, 1], [1.0, 2], [-6.1, 4], [1, 5], [-5.3, 6], [0.2, 7]]coeffs = b.reverse
# => [[0.2, 7], [-5.3, 6], [1, 5], [-6.1, 4], [1.0, 2], [3, 1], [2.1, 0]]Context
StackExchange Code Review Q#37207, answer score: 3
Revisions (0)
No revisions yet.