# String representation of a polynomial

We all know that math notation is idiosyncratic. Canonical representation of math objects often have irregular grammar rules to improve readability. For example we write a polynomial $3x^3 + x^2$ instead of more uniform but more verbose $3x^3 + 1x^2 + 0x^1 + 0x^0$. When a coefficient equals 0, you don't write the term, if the power equals $1$, you simply write $x$, and so on. So I wrote a simple program that outputs a string representation of a polynomial, given a list of coefficients:

def enumerate2(xs, start=0, step=1):
for x in xs:
yield (start, x)
start += step

def poly(xs):
"""Return string representation of a polynomial.

>>> poly([2,1,0])
"2x^2 + x"
"""
res = []
for e, x in enumerate2(xs, len(xs)-1, -1):

variable = 'x'

if x == 1:
coefficient = ''
elif x == -1:
coefficient = '-'
else:
coefficient = str(x)

if e == 1:
power = ''
elif e == 0:
power = ''
variable = ''
else:
power = '^' + str(e)

if x < 0:
coefficient = '(' + coefficient
power = power + ')'

if x != 0:
res.append(coefficient + variable + power)

return ' + '.join(res)


enumerate2 is a custom version of enumerate that supports variable step. The result looks like this:

>>> poly([2,0,3,-4,-3,2,0,1,10])
'2x^8 + 3x^6 + (-4x^5) + (-3x^4) + 2x^3 + x + 10'


How do I make this code more elegant and probably more generic? Oh, and the result is sub-optimal, as negative terms are enclosed in brackets, instead of changing the preceding plus sign to minus.

Your enumerate2 is a nice touch but I am not quite convinced that this is necessary : if you are to play with the length manually, you might as well compute the power from the index manually.

Also, if you were to handle the negative with a minus instead of the plus, you'd be able to get rid of the brackets. On the other hand, you cannot use join anymore which is a bit of a pain because it is a cool and efficient function.

Anyway, here's my try :

def poly(p, var_string='x'):
res = ''
first_pow = len(p) - 1
for i, coef in enumerate(p):
power = first_pow - i

if coef:
if coef < 0:
sign, coef = (' - ' if res else '- '), -coef
elif coef > 0: # must be true
sign = (' + ' if res else '')

str_coef = '' if coef == 1 and power != 0 else str(coef)

if power == 0:
str_power = ''
elif power == 1:
str_power = var_string
else:
str_power = var_string + '^' + str(power)

res += sign + str_coef + str_power
return res


and the corresponding output :

2x^8 + 3x^6 - 4x^5 - 3x^4 + 2x^3 + x + 10


Bug found

As I was looking at my original implementation, I found a bug which happens to be in yours too : try with [1,1,1,1,1].

• return instead of print – Maarten Fabré Apr 26 '19 at 10:10
• @Maarten Fabré indeed I've updated my answer. Thanks – SylvainD May 6 '19 at 21:39

I think there's a simpler way to do this:

fmt = [
[ "", "", "" ],
[ "{c:+g}", "{sign:s}x", "{sign:s}x^{n:g}" ],
[ "{c:+g}", "{c:+g}x", "{c:+g}x^{n:g}" ]
]

def term(c, n):
return fmt[cmp(abs(c),1)+1][cmp(n,1)+1].format(sign="- +"[cmp(c,0)+1], c=c, n=n)

def poly(xs):
return "".join(term(xs[i],len(xs)-i-1) for i in xrange(len(xs)))

def suppsign(s):
return s.lstrip('+')

print suppsign(poly([1,1,1]))


The term function takes a coefficient and power value and uses the characteristics of those two to select the appropriate format string to generate a string representing an individual term.

The poly function uses a list comprehension to efficiently concatenate the string for each term.

The suppsign function simply removes the leading + from the resulting string if desired.

# enumerate2

Here you can use itertools.count or reversed

for e, x in enumerate2(xs, len(xs)-1, -1):


becomes

for e, x in zip(itertools.count(len(xs)-1, -1), xs):


or

for e, x in zip(reversed(range(len(xs)), xs):


# continue

You can skip to the next iteration in the for-loop easier by doing instead of if x != 0: ...:

if x == 0:
continue


at the beginning of the loop

# split functions

def coefficient(x):
"""returns the string representation of x."""
if x == 1:
return ""
if x == -1:
return "-"
return str(x)


# sting multiplication and bool

for the power part, you can use string multiplication and the fact int(True) == 1 and int(False) == 0

result = coefficient(x) + variable + f"^{e}" * (e != 1)


# f-string

Since python 3.6, you can do f"({result})" if x < 0 else result instead of

        coefficient = '(' + coefficient
power = power + ')'


# yield

Instead of keeping a list of results, you can yield the intermediate terms. This

def poly2(xs, variable="x"):
if set(xs) == {0}:
yield "0"
return
for e, x in zip(reversed(range(len(xs))), xs):
if x == 0:
continue
if e == 0:
result = str(x)
else:
result = coefficient(x) + variable + f"^{e}" * (e != 1)
yield f"({result})" if x < 0 else result

 " + ".join(poly2((1,-1,0,)))

'x^2 + (-x)'

• Why (e not in {1, 0})? Is that a legacy of an earlier version where the previous if e == 0 special case didn't exist? Or was the special case supposed to be removed when you added the (e not in {1, 0})? – Peter Taylor Apr 26 '19 at 12:57
• You are correct. This is a relic of a previous version that had no influence, but is not necessary anymore. – Maarten Fabré Apr 26 '19 at 13:45