8
\$\begingroup\$

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 \3ドルx^3 + x^2\$ instead of more uniform but more verbose \3ドルx^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.

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jun 18, 2014 at 16:21
\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

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].

answered Jun 18, 2014 at 17:12
\$\endgroup\$
2
  • \$\begingroup\$ return instead of print \$\endgroup\$ Commented Apr 26, 2019 at 10:10
  • \$\begingroup\$ @Maarten Fabré indeed I've updated my answer. Thanks \$\endgroup\$ Commented May 6, 2019 at 21:39
4
\$\begingroup\$

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.

answered Jun 19, 2014 at 0:08
\$\endgroup\$
3
  • 1
    \$\begingroup\$ suppsign(poly([1,1,1])) would be better written as poly([1,1,1]).lstrip('+') \$\endgroup\$ Commented Jun 19, 2014 at 4:29
  • 1
    \$\begingroup\$ You don't have to use square brackets inside join, see stackoverflow.com/a/18212201/596361 \$\endgroup\$ Commented Jun 19, 2014 at 6:34
  • \$\begingroup\$ I would also remove :g and change :+g to :+, as these are equivalent, see docs.python.org/2/library/string.html#formatstrings \$\endgroup\$ Commented Jun 19, 2014 at 6:42
2
\$\begingroup\$

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)'
answered Apr 26, 2019 at 10:44
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 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})? \$\endgroup\$ Commented Apr 26, 2019 at 12:57
  • \$\begingroup\$ You are correct. This is a relic of a previous version that had no influence, but is not necessary anymore. \$\endgroup\$ Commented Apr 26, 2019 at 13:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.