So you have a lot of list manipulation, I believe you can reduce that to work with strings and use
re.sub to fill in the gaps.
Let's start by analyzing the answer:
I see that the result is basically symmetrical vertically and horizontally. Since lines are printed left-to-right, top-to-bottom, we can easily split it into top and bottom halves which are mirrored. This allows us to break apart our approach in only doing half the work, and copying a reflection onto itself.
res is populated with the top half of the answer, we want to extend it with the reversed copy of it. A common shorthand for a reversed list by using extended slices.
But doing this would copy the middle line twice! Since
[::-1] is the same as
[n:-1:-1], removing the last row would result in
[n-1:-1:-1], or simply
Now the top half of the solution can be broken down as repeated numbers, gradually substituting in decreased values for each row. The key word here is substitution. Creating a list and appending both sides is a way to handle this, but requires multiple modifications to it as you presented:
for x in range(1, n + 1):
str_x = str(x)
l = len(res)
res.insert(0, [str_x] * l)
res.append([str_x] * l)
Your approach appends the number to the beginning and end of the string. If you were working with strings, you could essentially create it all in one declaration. We can determine that the number of times
n appears in the first line is
2n (since it's left-right mirrored)
+ 1 to account for the
0 that would be in the center. The starting line is very simple:
res = [str(n) * (n * 2 + 1)]
As mentioned earlier, the subsequent lines build on the previous line by replacing all but the first and last
n-1. I really enjoy utilizing
re.sub, but you can use string concatenation as well.
re, we could build a pattern as
n is the number to be replaced. Once we find it, we need to replace it with
n-1 using the same formula described above. But since we need to leave an
n on each side, we'll have to subtract 2 from this formula to become
2 * n + 1 - 2 or simply
2 * n - 1.
n_minus_1_str = str(x-1) * (x * 2 - 1)
With this, we can replace all the middle
n values with
n_minus_1_str and attach
n on both sides to preserve it.
str_x + n_minus_1_str + str_x
Putting this new line together for
re.sub, we get:
new_line = re.sub(str_x + "+", str_x + (str(x-1) * (x * 2 - 1)) + str_x, res[-1])
You could also implement the above without
re.sub, and use regular string concatenation instead to build the string. The slicing gets a bit tricky, but can be accomplished with:
new_line = res[-1][:n-x+1] + str(x-1) * (x * 2 - 1) + res[-1][x-n-1:]
Finally, we just need a
for loop to run backwards from
n down to
1 (not including
0, since we are adding
n-1 in the loop.
for x in xrange(n, 0, -1):
xrange, since the list generated by
range is unnecessary.
Putting this all together with
'\n'.join(res) we get the following:
from re import sub
res = [str(n) * (n * 2 + 1)] # First line
for x in xrange(n, 0, -1): # Append remaining until 0 is in the center
str_x = str(x)
res.append(sub(str_x + "+", str_x + (str(x-1) * (x * 2 - 1)) + str_x, res[-1]))
res.extend(res[n-1::-1]) # Add the flipped version without the last row
I chose to replace the entire string of
x instead of matching only the middle values to save on regex computation.
Edit: Forgive me, first time posting in Code Review.