8
\$\begingroup\$

There is this hackerrank question, where we need a rhombus of letters. As to my opinion it can be viewed as some fractal, or similar logic.

Well I solved the question, but I am obviously disappointed with the code I come up with. Actually when I started writing I knew how I would end-up and I was ready to ask a question here.

I am reluctant to share this code of mine but it is necessary, if I am to improve.

size = int(raw_input())
alphabet = "abcdefghijklmnopqrstuvwxyz"
def draw(size):
    shape = "" # The answer
    tmp_size = size # defining a temporary size variable to help with the solution
    tmp_size -= 1
    while tmp_size+1:
        hyphen_count = (size-tmp_size) *2 - 2 # Needed hyphen count for the left-right of new_line
        hypens = hyphen_count*"-" # Hyphens to be added

        # The new line, the strange ranges I used was to get the correct letters in place. Probably needs simplifying.
        new_line = "-".join([alphabet[abs(num)] for num in range(1-size,tmp_size-size+1) + range(size-tmp_size-1, size)])

        # Draw one line if it is the first line
        if tmp_size == size - 1:
            shape = shape + new_line
        # Draw to both top and bottom if it is not the first line.
        else:
            shape = hypens + new_line + hypens + "\n" + shape + "\n" + hypens + new_line + hypens
        tmp_size -= 1
    return shape
print draw(size)

I added many comments for easier evaluation of this badly-written code. So what I ask is:

  • Dear users with algorithm experience, please help me find a better method if not the code itself. (Maybe a recursion ?)
  • Dear programmers, please help me implement better python.
  • Dear users that know everything, please teach me everything.
\$\endgroup\$
5
\$\begingroup\$

I propose a different method!
If you generate the center, and the size of the shape you can do the entire thing easily with str.format.

>>> '{0:-^16}'.format('yo')
'-------yo-------'

Simple.

The biggest problem is getting the diamond shape. But this can be done with ease.
For the amount of different letters you want per line it goes:

1, 2, 3, 4, 3, 2, 1

Where the character you want goes:

3, 2, 1, 0, 1, 2, 3

If you make a range that goes like the first, you can change it to the second by subtracting from 4 each time.

I'll define this range as:

def piramid_range(size):
    return range(1, size) + range(size, 0, -1)

A quick run of size 4 gives us the correct output. And subtracting from 4 each time gives us the correct output too.

>>> piramid_range(4)
[1, 2, 3, 4, 3, 2, 1]
>>> [4 - i for i in piramid_range(4)]
[3, 2, 1, 0, 1, 2, 3]

and so adding this all together, with two comprehensions makes for a nice small function:

def draw(size):
    fmt = '{{0:-^{size}}}'.format(size=4 * size - 3)
    return '\n'.join(
        fmt.format('-'.join([alphabet[size - s] for s in piramid_range(inner_size)]))
        for inner_size in piramid_range(size)
    )

And finally, as I think your separator character is quite ugly, I think adding the ability to change it would be nice. This is just replacing its two occurrences.

def draw(size, char='-'):
    fmt = '{{0:{char}^{size}}}'.format(char=char, size=4 * size - 3)
    return '\n'.join(
        fmt.format(char.join([alphabet[size - s] for s in piramid_range(inner_size)]))
        for inner_size in piramid_range(size)
    )

print draw(4)
print
print draw(4, char=' ')

Which would have the following output:

------d------
----d-c-d----
--d-c-b-c-d--
d-c-b-a-b-c-d
--d-c-b-c-d--
----d-c-d----
------d------

      d
    d c d
  d c b c d
d c b a b c d
  d c b c d
    d c d
      d
\$\endgroup\$
5
\$\begingroup\$

Joe Wallis gave a much better algorithm, so I'll just give more general notes.

Instead of defining alphabet manually, you could import from the string module.

from string import ascii_lowercase as alphabet

You have a confusing pattern with tmp_size. The fact that you have to define while tmp_size+1 is a red flag. It would be better to write while tmp_size >= 0, which gives the same result. But also you don't need to use while loops to iterate over a set of numbers. for i in range(size - 1, -1, -1) will do what you want.

for i in range(size - 1, -1, -1):

Now there's no need for tmp_size, just refer to i as the current size.

You're recreating the string line twice in this line of code:

shape = hypens + new_line + hypens + "\n" + shape + "\n" + hypens + new_line + hypens

I recommend taking both the hyphen section and the algorithm and moving them to a new function

def get_line(i, size):

This way it's neater and separated out. You can just call it once to get the current line, then add it to shape as necessary.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.