Ruby字符串类多项式运算代码调试:修复simplify方法报错
Ruby字符串类模拟多项式运算的调试问题
我正在重写Ruby的String类,让它能像代数表达式一样运算——比如"x" + "1"返回"x + 1"而非"x1","x - 1" * "x + 1"返回"x² - 1"而非报错。基础运算方法已完成,但调试自定义的String::simplify方法时遇到问题:执行puts ("x - 1"*"x + 1").simplify时,在String::minus方法第138行触发错误:comparison of nil with 2 failed。可疑方法为String::simplify、String::simplify2和String::minus。
相关代码如下:
class String @@powers = %w(⁻ ˙ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹) @@store = Hash[(["-","."]+[*"0".."9"]).zip @@powers] @@reply = "Sorry ,super strings can't tolerate your madness" def monomial?;self.terms.size == 1end def polynomial?;!self.monomial?end def * b #multiplication if(self.polynomial? or b.polynomial?) s = [] self.terms.each do |i| b.terms.each do |j| q = i._ j q.end_with?("x¹") ? s << q.chomp("¹") : s << q end end return s.join " + " end self._ b end def _ b #multiplication of two terms @negative = 1 @negative *= -1 if self.include? "-" @negative *= -1 if b.include? "-" self.gsub!("x","x¹") if self.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).empty? b.gsub!("x","x¹") if b.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).empty? self.gsub!("x","1x") if self.scan(/\d+/).empty? b.gsub!("x","1x") if b.scan(/\d+/).empty? m = "#{@negative}".scan(/\D+/).join x = "#{self.scan(/\d+/).join.to_i * b.scan(/\d+/).join.to_i}x#{(self.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).join.unsup.to_i + b.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).join.unsup.to_i).to_s.sup}".gsub(/(\D*)(1x)/,"\1x") if x.scan(/\d+/).empty? x.gsub! "x⁰","1" else x.gsub! "x⁰","" end return "0" if (m+x) == "-0" return m+x end def sup;self.gsub /\S/,@@store;end #from normal to supersrcipt string def unsup #from superscript to normal string self.gsub /[⁻⁰¹²³⁴⁵⁶⁷⁸⁹]/,@@store.invert end def ** n;([self]*n).reduce :*end #exponentiation of expression def degree #degree of polynomial z = self.dup z.gsub!("x","x¹") if z.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).empty? z.terms.map{_1.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).join.unsup.to_i}.max end def a b #addition of two terms self.gsub!("x","1x") if self.scan(/\d+/).empty? b.gsub!("x","1x") if b.scan(/\d+/).empty? if self.degree == b.degree x = "#{self.scan(/\d+/).join.to_i + b.scan(/\d+/).join.to_i}x#{b.degree.to_s.sup}" if x.scan(/\d+/).empty? x.gsub! "x⁰","1" else x.gsub! "x⁰","" end x.chomp!("¹") if x.end_with? "x¹" return x end return (a + " + " + b).gsub "+ -","-" end def terms;self.gsub("-","+ -").split " + "end #to get the terms of an expression in array def simplify #to simplify the given expression return self.simplify2 unless self[1..].include? "-" a = self.terms p = a.reject{_1[0] == "-"}.join(" + ").gsub("+ -","-") n = (a.select{_1[0] == "-"}).map{_1[1..]}.join(" + ").gsub("+ -","-") p.minus n end def simplify2 #to simplify the expression containing only positive sign terms a = self.terms e = a.map &:degree ex = self.ex h = Hash[ex.zip([[]]*a.size)] a.size.times{h[e[_1]] += [_1]} s = [] for i in ex.sort.reverse s << h[i].map{a[_1]}.reduce(:a) end s.join(" + ").gsub "+ -","-" end def add b #addition of two expressions (self + " + " + b).gsub("+ -","-").simplify2 end def describe #describing the type of polynomial "Yep,so #{self} is degree #{self.degree} polynomial" end def coeff #to get the coefficient of a term return @@reply if self.polynomial? self.gsub! "x","1x" if self.scan(/\d+/).empty? self.scan(/-*\d+/).join.to_i end def coeffs #to get the coefficients of the terms in array return @@reply if self.monomial? self.terms.map{_1.gsub! "x","1x" if _1.scan(/\d+/).empty?;_1.scan(/-*\d+/).join.to_i} end def m b #to subtract two terms if self.degree == b.degree c,d = self.coeff - b.coeff,b.degree return "0" if c.zero? if d.zero? return "#{c}x#{b.degree.to_s.sup}".gsub "x⁰","" elsif d == 1 return "#{c}x#{b.degree.to_s.sup}".gsub "x¹","x" end return "#{c}x#{b.degree.to_s.sup}" end (self + " - " + b).gsub "- -","+" end def - b return @@reply if self.polynomial? or b.polynomial? self.m b end def minus b #subtraction of two expressions a,b = self.simplify2,b.simplify2 s = [] e1,e2 = a.terms.size,b.terms.size e3,e4 = a.terms.map(&:degree),b.terms.map(&:degree) m = [a.degree,b.degree].max h1 = Hash[[*m.downto(0)].zip([[]]*(m+1))] h2 = Hash[[*m.downto(0)].zip([[]]*(m+1))] e1.times{h1[e3[_1]] += [_1]} e2.times{h2[e4[_1]] += [_1]} m.downto(0){ if h1[_1].empty? && h2[_1].empty? #shoot the user elsif h1[_1].empty? s << "-#{b.terms[h2[_1][0]]}" elsif h2[_1].empty? s << "#{a.terms[h1[_1][0]]}" else s << a.terms[h1[_1][0]].m(b.terms[h2[_1][0]]) end } s.join(" + ").gsub("+ -","-").gsub(/[+-] 0 /,"").terms.join(" + ").gsub "+ -","- " end def ex;self.terms.map(&:degree).uniq;end def negative?;self[0] == "-"end #checking whether the term is negative or not def positive?;!self.positive?end #checking whether the term is positive or not end class Array def form #defining this method to convert the array returned by terms to reform the expression self.join(" + ").gsub "+ -","-" end end
问题定位与修复方案
1. 修复positive?方法的无限递归
原代码中positive?方法定义为def positive?;!self.positive?end,会导致无限递归调用,引发栈溢出并间接产生nil值。
修复后:
def positive?; !self.negative? end
2. 修复simplify2的哈希初始化错误
原代码中h = Hash[ex.zip([[]]*a.size)]会让所有键指向同一个数组实例,导致分组逻辑完全错误。
修复后:
def simplify2 a = self.terms e = a.map &:degree h = Hash.new { |hash, key| hash[key] = [] } a.each_with_index do |term, idx| h[e[idx]] << term end s = [] h.keys.sort.reverse.each do |deg| s << h[deg].reduce(:a) end s.join(" + ").gsub "+ -","-" end
3. 修复degree方法的常数项处理
原方法无法正确识别常数项的次数(应为0),会导致nil值出现。
修复后:
def degree z = self.dup if z.include?("x") z.gsub!("x","x¹") if z.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).empty? z.terms.map { |term| term.include?("x") ? term.scan(/[⁰¹²³⁴⁵⁶⁷⁸⁹]+/).join.unsup.to_i : 0 }.max else 0 end end
4. 修复minus方法的空数组索引访问
原方法中直接访问空数组的索引0会返回nil,引发数值比较错误。
修复后片段:
m.downto(0) do |deg| if h1[deg].empty? && h2[deg].empty? next elsif h1[deg].empty? s << "-#{b.terms[h2[deg][0]]}" unless h2[deg].empty? elsif h2[deg].empty? s << "#{a.terms[h1[deg][0]]}" unless h1[deg].empty? else s << a.terms[h1[deg][0]].m(b.terms[h2[deg][0]]) end end
内容的提问来源于stack exchange,提问作者Ash_Blanc
相关产品推荐
相关产品推荐

