Skip to content

Instantly share code, notes, and snippets.

@rphly
Created October 27, 2020 12:28
Show Gist options
  • Save rphly/e3f976e0180e10e17c716b90e645c08f to your computer and use it in GitHub Desktop.
Save rphly/e3f976e0180e10e17c716b90e645c08f to your computer and use it in GitHub Desktop.

Some tips, advice, FAQ after Python Session 1

Hi guys, I thought I'd write some answers to common questions from people today during the first session and also give some tips & tricks when solving programming problems. Hopefully this will be useful for you during the exams.

The content is organized as follows. Feel free to read them out of order:

  1. General problem solving strategy
  2. Solving iteration/recursive problems (eg. for/while loops)
  3. Equality (==) vs Same-ness (is)
  4. Nested lists, dictionaries
  5. Range(), indexing and an intuition of step sizes
  6. What next

General problem solving strategy

Whenever we encounter a problem to solve, a good habit to pick up is to learn to break it down. This is true for almost all domains. In Physics, we split forces into horizontal and vertical components. In Math, we can solve the left side of an equation before we solve the right. In programming, the same is true.

When we encounter difficult problems, you should always stay calm and write down the different components that make up the problem:

  • What variables am I provided?
  • What are my inputs? What are their types? (eg. an integer, a list of lists)
  • What is my output? (eg. return a value, print a string)

Following which, it is then useful to ask the following questions:

  • Does order matter? (If yes, usually you will need to pay attention to how a sequence is sorted. Is it consecutively ascending or descending?)
Example problem: 
Sort a list of integers [23415] from small to large
Output: "12345"

Here, the keywords are "sort", "small to large" (ascending) and the input is a list of integers```"
  • Do I need to store data while I modify my variable?
  • What are some variables I need to define?

Then, I think it is useful to scaffold your code into chunks of logic with comments

# 1. do this
# 2. then do this
# 3. finally i have to do this
# 4. return the value

Break complicated problems into smaller chunks that you can tackle.

Create a function that builds a word from the scrambled letters contained in the first list. 
Use the second list to establish each position of the letters in the first list. 
Return a string from the unscrambled letters (that made-up the word).

Examples:
   
-   `word_builder(["g", "e", "o"], [1, 0, 2])` returns `["e", "g", "o"]`
-   `word_builder(["e", "t", "s", "t"], [3, 0, 2, 1])` returns `["t", "e", "s", "t"]`

def word_builder(letters, positions):
    # 0. define empty string 
    # 1. loop through positions
    # 2. get the letter at n index
    # 3. append the letter to a string
    # 4. return string
    
    result = ""
    for n in positions:
        letter = letters[n]
        result += letter
    return result

This is a naive solution to the problem given in Session 1. In programming, we want to practice defensive coding. Which means we want to make sure that we have covered all edge cases and make sure our code handles for inputs that will give the function an error.

For example, to make my solution more defensive, I would check if any index in positions exceeds the length of letters.

def word_builder(letters, positions):
        # 0. define empty string
        # 0.5 check if index in positions exceed the length of letters 
        # 1. loop through positions
        # 2. get the letter at n index
        # 3. append the letter to a string
        # 4. return string
        
        if max(positions) >= len(letters):
            print("Invalid position")
            return
            
        result = ""
        for n in positions:
            letter = letters[n]
            result += letter
        return result

There u go. By breaking down the problem, we are able to better tackle each part and finally solve the whole.

Solving iteration/recursive problems

When solving iterative problems, I like to draw a table with pen and paper so I don't get confused by the values.

sum = 0
for i in range(0, 3):
    sum += i

What is the final value of sum?
variables \ steps 1 2 3
i 0 1 2
sum 0 1 3

As you can see, drawing a table turns a pretty frustrating problem into a trivial one.

sum = 0
for i in range(0, 3):
    for x in range(i):
        sum = sum * x
What is the final value of sum?

Try doing this with and without a table. Tables save lives.

Equality (==) vs Same-ness (is)

One of the most important concepts in programming to understand is the difference between equality and sameness.

Intuitive example: Sally has a pair of perfectly identical twin dogs.

Are they equal? Yes. They are both dogs, both (mostly) identical. Are they the same? No. We recognize them as two distinct dogs. They just have similar qualities.

Similarly, in Python

a = [1,2,3,4]
b = [1,2,3,4]

print(a == b) # true, they have similar qualities. For our intent, they have the same value.
print(a is b) # false, they are different lists!

You can go deeper into why this is so. Basically, it has to do with how python stores these objects in memory (each list has a unique identifier) but this is not required for now. Just know that == and is are not the same!

Nested lists, dictionaries

To tackle this problem, it's always easier to break it down!

nested_list = ['I', 'can', 'be', ['Any', 'DataType', [True]]]

Can you return True?
  1. Don't care about the "inside" lists/dicts/tuples.
  2. Just focus on findind the position of the next list/dict/tuple.
  3. Write it down somewhere. Repeat steps 1-3 until you are done.

Your general strategy should look somewhat like this:

1. [x,x,x, THIS ONE HERE] - nested_list[3]
2. [x, x, THIS ONE HERE] - nested_list[3][2]
3. [THIS ONE HERE] - nested_list[3][2][0]

What is Range() and an intuition for step sizes

range() is a built-in function in Python that basically just creates a sequence of values for you to use.

range(start,stop,step)

start: first number (default 0)
stop: the nth number to stop at (right exclusive)
step: the step size (how much to increment)

range(0,10) # 0,1,2,3,4,5,6,7,8,9
range(10) # 0,1,2,3,4,5,6,7,8,9
range(2,10) # 2,3,4,5,6,7,8,9

for n in range(2,10,2):
    print(n) # 2,4,6,8

Do not get confused about range and indexing. range() is a helper function. Indexing is when you traverse a list/tuple/sequence object. They both use the same syntax of start, stop and step.

Remember that for both range() and indexing, it is left inclusive and right exclusive.

In Math notation, [a, n). Where a is the starting integer, and n is the stopping integer.

Understanding Step Sizes

Imagine your list is now a staircase. If I am a normal person, I will climb the staircase one step at a time. Sometimes, if I'm rushing, I might prefer to climb the stairs two steps at a time. If I'm feeling extra cool, I might climb the stairs k number of steps at a time.

So, when using step sizes in indexing or in range, really we're just talking about how many steps we want to count at a time.

A step size of two means we are counting two at a time.

# step size 2:
for n in range(2,10,2):
    print(n) # 2,4,6,8

By default, step size is 1.

What next

If you need any help/ any questions, please feel free to email me raphael_yee@mymail.sutd.edu.sg

Thanks, Raphael

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment