6
\$\begingroup\$

I've posted code/library to GitHub for first time over here.

The code is pretty simple. It generates a BowTie pattern of given size and characters.

I did attempt to implement DP by calculating only half of shape matrix and getting its mirror image from matrix itself.

The code works fine. I do need help with someone analyzing to see if certain logic/lines can be done in more pythonic and efficient way [a.k.a. Code Review].

Any help is appreciated.

Source-Code

from __future__ import print_function

class BowTie(object):
    def __init__(self, size=5, fill_value = '*', empty_value = ' '):
        self.size = size
        self.fill_value = fill_value
        self.empty_value = empty_value

    def  create_shape(self):
        ''' creates shape as per input values and returns list of list of values'''
        star = self.fill_value
        dot = self.empty_value
        m = (self.size * 2) - 1 #Get center row

        #get  top half list
        th = []
        for idx,k in enumerate(xrange(1,self.size+1)): #run through 1 - size
            row = idx + 1
            tmplst = []
            if row % 2 != 0:
                tmplst.append(i for i in xrange(1,row + 1) if i % 2 != 0)
                tmplst.append(i for i in xrange(m, m-row, -1) if i % 2 != 0)
            else:
                tmplst.append(i for i in xrange(1,row + 1) if i % 2 == 0)
                tmplst.append(i for i in xrange(m, m-row, -1) if i % 2 == 0)

            #append each row value to  top half list.
            th.append(sorted(set([j for i in tmplst for j in i])))

        #create mirror-image of top half which is our bottom half 
        th = th + th[len(th) -2::-1]

        def get_list(bound = self.size, alist = []):
            ''' expects upper bound and an integer list
            returns a list of fill_values '''        
            tmp_list = []            
            for i in xrange(1,bound + 1):
                tmp_list.append(star if i in alist else dot)
            return tmp_list

        #create list of * and blanks or as per fill and empty value
        row_values = [get_list(bound = m, alist = i) for i in th]
        return row_values

    def print_shape(self):
        ''' print given list of list of values'''
        for i in self.create_shape():
            print(' '.join(i))
\$\endgroup\$
4
\$\begingroup\$
  • Don't use a class, there's no need.
  • You can remove the tmplst.append if you move the comprehensions into the th.append.

    To do this you can use (i + idx) % 2 == 1 as the check. And tmplst = [xrange(1,row + 1), xrange(m, m-row, -1)].

  • It doesn't make sense to build a set, to then convert it to a list. Sets have \$O(1)\$ lookup where lists have \$O(n)\$.

  • You don't need get_list, just use a comprehension.

This can get something like:

def bow_tie(self, size=5, fill_value='*', empty_value=' '):
    star = fill_value
    dot = empty_value
    center_row = size * 2 - 1
    th = [
        {
            j
            for nums in (
                xrange(1, i + 2),
                xrange(center_row, center_row - i - 1, -1)
            )
            for j in nums
            if (j + i) % 2 == 1
        }
        for i in range(size)
    ]
    th += th[len(th) -2::-1]

    return [
        ' '.join([
            star if i in indexes else dot
            for i in range(1, size * 2)
        ])
        for indexes in th
    ]

However, this isn't that good an approach. Instead you can make a list containing the pattern. And show a 'view'. The view that you want could be:

     *
    **
   ***
  ****
 *****
******

To achieve this, you can make the following list:

'     ******'

With the following moving view:

#     *######
##    **#####
###   ***####
####  ****###
##### *****##
######******#

You can also invert the view to get the other side of the bow tie. And so using the above, we just need to think of what the pattern of the list needs to be for your bowtie.

Since it's pretty much the same as the above, but with a space in-between the dots you could use something like:

'     * * * '

And to create that, just make the first n chars space, with the following n chars flip between space and star:

face = [empty_value] * (size - 1) + [fill_value, empty_value] * (size // 2 + 1)

After this combine the above slider, and chain the top and bottom starts, and you should get:

from itertools import chain

def bow_tie(size=5, fill_value='*', empty_value=' '):
    face = [empty_value] * (size - 1) + [fill_value, empty_value] * (size // 2 + 1)
    starts = chain(range(size), reversed(range(size-1)))
    for i in starts:
        yield ' '.join(face[i:i+size][::-1][:-1] + face[i:i+size])
\$\endgroup\$
  • \$\begingroup\$ +1 for solving this in 7LOC and awesome use of sliding_window. I had to spend some time to understand your approach. I did implement sliding_window differently using islice as that's little easier for me. Pls. take a look at mods. \$\endgroup\$ – Anil_M Oct 30 '17 at 19:22

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.