As we all know, I’m a professional dev and I write broken code.
I have found some ways to write less broken code though!
I apologize in advance for a somewhat lengthy post. It is mostly pictures of the same piece of paper and some code snippits, so I hope it isn’t to long of a read.
BLUF: Sketch out a plan with pencil and paper before you start coding!
Lets talk about the following challenge. WARNING! The challenge is somewhat technical, but it is representative of an actual function I needed to write.
You are given an array of integers
offsets broken into
numelements chunks of length
elemsize. We call these chunks
elements. Create a new array
blockoffsets where the integers are permuted so that elements are interlaced by
blocksize. That is to say, the first integer in the first
elements will be next to each other, then the second integer in the first
elements and so on. Pad the final block with copies of data from the last element.
Whew, that sounds messy in words. Lets make a small example.
If I have
offsets = [10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18] numelements = 8 elemsize = 2 numblocks = 2 blocksize = 5
the desired output is
blockoffsets = [10, 11, 12, 13, 14, 11, 12, 13, 14, 15, 15, 16, 17, 17, 17, 16, 17, 18, 18, 18]
It’s still a bit tricky to understand. Here is a diagram:
# In this 1D example, the offsets are given by # # ________________________________________________________________________________________ # | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | # | 10 -- 11 | 11 12 | 12 13 | 13 14 | 14 15 | 15 16 | 16 17 | 17 18 | # # We block elements into groups of 5: # ________________________________________________________________________________________ # | block 0: | block 1: | # | e0 | e1 | e2 | e3 | e4 | e0 | e1 | e2 | # | | | # | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | x -- x | # | 10 -- 11 | 11 12 | 12 13 | 13 14 | 14 15 | 15 16 | 16 17 | 17 18 | # # Intermediate logical representation: # ______________________________________________________________________________ # | block 0: | block 1: | # | node0: node1: | node0: node1: | # | 10-11-12-13-14 11-12-13-14-15 | 15-16-17- *- * 16-17-18- *- * | # | e0 e1 e2 e3 e4 e0 e1 e2 e3 e4 | e0 e1 e2 e3 e4 e0 e1 e2 e3 e4 | # # Permuted and padded output: # ______________________________________________________________________________ # | block 0: | block 1: | # | node0: node1: | node0: node1: | # | 10-11-12-13-14 11-12-13-14-15 | 15-16-17-17-17 16-17-18-18-18 | # | e0 e1 e2 e3 e4 e0 e1 e2 e3 e4 | e0 e1 e2 e3 e4 e0 e1 e2 e3 e4 |
The diagram makes things a little bit clearer. As is sometimes the case, describing the problem and understanding what is going on here is harder than solving the problem.
You are welcome to try this challenge. This isn’t from Free Code Camp. This challenge is from a supercomputer software library that I work on. (The interested can see it here) These sorts of coding challenges that you work on in Free Code Camp pop up in all sorts of useful contexts! In this case, I need to make blocks of size 8 to make full use of AVX 512 vectorization.
This challenge description isn’t great, but it’s what I had to work with when I needed to write this function. I wrote it in C, but I’ll go ahead and write it in Python this time.
The first thing I do is put away my computer and grab a sheet of paper. Seriously! The best coding starts without your computer.
Now I outline a simple example and some context for the problem.
With that out of the way, I can start outlining the inputs and outputs I need for my function.
Next, I write a general flow that my function needs to follow. I am adding some space so that I can put in pseudocode later.
Lastly, I put in some pseudocode.
As you can see, I did some erasing and some notes, but I think I’m finally ready to touch the keyboard! The sooner you leap to your keyboard, the more bugs you will write!
I’m going to start by writing the frame of the function and some comments.
def permute_pad_offsets(offsets, numblocks, numelements, blocksize, elemsize): # Loop over blocks of elements # Loop over elements in block # Loop over element size # Return new array
Whew, I’ve got the second hardest piece done. The hardest piece is debugging, but I’ll be able to debug much more easily now that I know what I’m trying to do!
Now I will start adding in some code. I’m adding spoiler tags in case you want to try this challenge at home!
def permute_pad_offsets(offsets, numblocks, numelements, blocksize, elemsize): # Initialize output - Python needs to know the length of the array blockoffsets = [None]*(numblocks*blocksize*elemsize) # Loop over blocks of elements for b in range(numblocks): # Loop over elements in block for e in range(blocksize): # Loop over element size for i in range(elemsize): blockoffsets[b*blocksize*elemsize + i*blocksize + e] = offsets[min(b*blocksize + e, numelements - 1)*elemsize + i] # Note - the min() adds copies so that we pad out to the vector length # Return new array return blockoffsets
I’ll be the first to admit that I glossed over debugging, unit tests, etc, here. There is a lot of work going from the comments to the final code. But it’s much much easier with a plan!
I hope that this gives you the basic idea of how I write less broken code!
You can see my final working solution in C here.
TLDR: Writing out a plan with pencil and paper will make your code less broken and easier to debug!