Programming Thinking
3175 segments
This video teaches you programming
thinking.
People say you don't need to write code
anymore. You have AI.
I actually agree with that. But if
somebody say you don't need programming
thinking because of AI, I couldn't
disagree more. To say programming
thinking doesn't matter is like saying
basic knowledge and common sense don't
matter. We still went to school to learn
the fundamental of math, science,
economics because it helps us to think
and ask better question.
In the age of agentic engineering or
vibe coding, programming thinking is a
new common sense that help you to
maximize your use of AI. It's no
surprise to see the value created by
pure vibe coder just plateaus over time.
Whereas it's a people with programming
thinking can harness the true potential
of AI and create impactful project.
Throughout this video, we use a Python
language to learn about programming
thinking.
Starting from variable,
control statement, Python list, for
loop, loop with range, nested loop,
function,
value versus reference, dictionary,
and recursion.
Your attention is all I need. If you
invest your next 60 minutes in the
video, you'll transcend from a pure vibe
coder to the enlightened programming
thinker.
The first time when we saw equal sign,
it probably dates back in grade school.
1 + 1 = 2. The equal sign here feels
like a noun because it is statically
describing a math expression. And this
interpretation is buried deep within our
subconscious. Yet, in programming, the
equal sign means a complete different
thing. It's a verb. It's action which
you perform as a programmer. Using the
equal sign, we store a data into a
variable.
For example, hey, the variable whose
name is A, now you store the number
five. And if we print A, it's going to
print five. And we can use a variable A
as you were directly using a number
itself. So, A + 1 is basically 5 + 1, so
forth, A * 3, A to the power of 2.
We can also store a string to variable.
The variable whose name is B, now stores
a string Bob. If we print B, it prints
Bob. So, hello plus B is hello Bob. And
likewise, my name is plus B, B plus B
plus B.
Question. So, the variable thingy you
said here, what kind of data type? Is it
a number or is it a string?
Well, neither. The best way to think
about the nature of a variable is that
it is something that can store any data
type, but itself is not intrinsically a
data type. So, I know sometimes a
variable really looks like a string, but
it's not because it does not have the
quotes. For example, this is a variable
and the name of this variable is season.
But this season here is a string.
After you store a data to a variable,
which is action we call assignment
statement, you can use a variable later
as you were directly using a data stored
within.
Just knowing this fact, variable is
already a powerful tool. Potato was
using Python to calculate the surface
area and volume of a sphere. But now,
suppose the radius of a sphere is no
longer eight, but seven. He needs to
manually update all occurrences of eight
into the number seven and execute this
code again.
That's really not so efficient, right?
The more clever move here is to use a
variable. So, we first create a variable
on top. The variable's name is R. It
stores the number seven. And in our
formula, rather than multiplying with a
specific radius, we'll multiply that
with variable R, which we know now it is
seven.
And execute again. Great, this works.
From now, potato can just easily modify
the variable on top and you'll just
compute the answers for that particular
radius R.
At this point, some of you are like,
"Come on, potato. Don't be lazy. You are
only manually changing the number three
times. You don't need variables in your
life." But our potato could be more
ambitious. He's also interesting using
Python to compute the surface area and
volume of all the Platonic solid in this
world of the same edge length. They are
tetrahedron,
hexahedron,
octahedron, dodecahedron, icosahedron.
All the different type of hedron have
different formulas for surface area and
volume. But with the use of variable,
one modification here, every line of
computation below changes accordingly.
However, if you do want to replace all
occurrences of edge length manually,
it's very tiring. You get my point.
Let me interject with a few words about
terminology. With such an assignment
statement, variable A stores the number
five. Another 100% equivalent statement
is to say the value of A is five. I just
want to let you know that the two
description here means the exact same
thing. I would also use them
interchangeably.
Cool. Variables can store data. Variable
has value. Why don't we just call them
data container or value with a name?
Because a variable can store different
data throughout the execution of the
program. After your initial assignment
statement, you can change it again and
again. It can store the data of whatever
you want and changes whenever you want.
As a matter of fact, understanding how a
variable changes is a critical part of
programming. Hence, we need to be solid
on this topic.
So, I'm going to ask you to do the
following six quizzes.
I know your reaction. Wait a second. I'm
here to learn about Python variables.
Why are you already testing me?
Although this feel a little unfair at
the moment, I actually think it's a very
good idea to have you using your own
intuition to speculate how variable
behaves in Python and see where your
expectation misalign with outcome. This
is actually much better way of learning
and retain information.
Somewhere along the middle, you might
feel things start to get pretty boring,
but the mastery of variable is very
important. So, please stay with me
through the end.
I'll remind you one important fact.
Python program executes from top to the
bottom line by line.
Let's begin with a warm-up question
first. You can pause the video and
contemplate on what would this Python
program print out at the end.
Okay.
Hopefully, you have got your answer.
As the program executes from the top to
the bottom, the variables A, B, C, D
each stores its respective data.
On line number five, we have a string
concatenation, which begins with
variable A. We know that A is a string
Bob adding with has earned the string of
B * C * D, which is 5 * 8 * 20, 800, and
join again with this week. So, the final
output is Bob has earned $800 this week.
Quiz number one. You can go ahead to
pause and ponder on this Python program.
The answer here is pretty
straightforward. The variable is
initially assigned with a string that
says water
and then reassigned to the result of 2 +
1, which is 3, reassigned again to the
string fish, and then reassigned to the
number 500. But, what's the lesson here?
It is that a Python variable can change
between data types. The variable called
thing is initially storing a string, but
later it stores a number. Although this
may seem somewhat trivial at the moment,
yet in many other programming language
like C or Java, once a variable is
initially created to store number, it
can never be used to store a string
later on. Therefore, Python variable is
very flexible.
Number two.
The variable A stores seven, but in the
second line we create a variable B and
assign variable A to it. This is the
first time that a variable appears on
the right-hand side of the equal sign.
So, what exactly happens here? What does
B store?
The answer is B is going to store
whatever A stores.
A very common misunderstanding is that
people tend to have this mental image,
in which case we believe variable B is
storing the symbol of A, rather than the
value of A. That's incorrect.
When any variable is on the right-hand
side of the equal sign, we just treat it
as its value. Variable B stores the
value of A.
In line number four, variable B is
assigned as A plus 100. Since A is on
the right side of the equal sign, we
treat variable A as if we're treating
its value directly, which at the moment
the value is seven. So, B is a
reassigned as seven plus 100, 107.
In line six, C is assigned with B. So, C
is going to store the value of B.
In line number seven, D is assigned with
C. So, D also store 107.
So, the lesson here is all about
assignment of one variable by another
variable.
Number three.
Line one, variable n stores number 10.
Line two, the variable n appears on both
side of the expression, n equal to n
plus one. To be honest, this is
something strange to even look at. Then,
what's going on here?
Remember our principle that whenever a
variable appears on the right-hand side
of the equal sign, we treat it as if
we're directly treating its value. At
this point, n is 10. So, line number two
is basically saying n equal to 10 plus
one.
Line four, the variable n once again
appears on both sides of the expression.
But, we know we just need to treat it as
its value. At this moment, n is 11. So,
this line is saying n equal to 11 plus
one.
In line number six, n equal to n times
two, which is saying n equals to 12
times two. So, n is 24 now.
Whenever I see an assignment statement
looking like this, my brain
automatically translate this to plus one
to whatever its current value is.
And you might also see another way which
we can write this, and that is n plus
equal one. And those two statement you
see here, they are the exact same thing.
It's just a matter of personal
preference to choose either one of them
to use. So, the lesson of quiz three is
that a variable can update itself based
on its current value.
Number four.
The goal here is to once again test your
understanding of variable assignment. A
common misunderstanding would sound like
this.
In line number two, variable k is
storing the variable f. Therefore, if I
change k to zero, that means f is also
changed to zero.
But, that is incorrect.
I know that you know in line number two,
k equal to f is saying k stores a value
of f, which is 3,000. In line number
three, we change the value of k to zero.
But, that really had no business to do
with the original variable f. So, later
again, if we print F, it's still going
to be 3,000.
Number five.
So, what is the problem with this code
here?
Line number one and two executes
normally.
In line number three, we set area equals
to pi times radius squared.
What is the value of variable pi?
Oh, it's 3.14. It said that on the next
line.
But, remember Python program executes
from top to the bottom line by line. So,
at precisely the timing of line three's
execution, our computer has no idea the
value of pi.
So, there is actually going to be a
error. The lesson here, always define a
variable before using it.
Number six.
The last one we have here is a classic,
swapping two variables value. But, did
this code actually swap though? Nope.
The end goal here, after swapping, we
should see A is 99 and B is one. In line
four, A is assigned with value of B,
which is 99. So, the value of A is 99
now. In the following line, B is
assigned with value of A, which is also
99.
So, in the end, the value of the two
variables did not swap. They just both
became the value of B.
What code can we add so that in the end,
their values actually swap?
Perhaps you remember, after line number
four, when we reassign A with the value
of B,
the original data which A hold, the
number of one, is completely lost in the
environment of this program. Which
means, to fix this problem, we actually
need to create an additional variable
here, a variable called original A to
store A's original value
before A changes.
And later, rather than assigning B with
A, we assign B with a value of original
A.
And indeed, they're swapped. The lesson
here, when we want to swap two variables
in this manner, we need to be cautious
and we need to create an additional
variable for help.
Although I presented them as six small
quizzes, they in fact were six mini
lessons to go over the fundamental of
Python variable and assignment statement
while having you to use your own
intuition to speculate its behavior.
Did I spend too much time on the
details?
Absolutely not. Variable is such an
important concept. Many years down the
road, after you have written 1,000 lines
of code, you will have an unceremonious
realization that
wait, is programming just the art of
creating and updating variables?
And alas, let's finish with two remarks
about variable naming.
When coding, we really have freedom in
naming the variable we create. Yet, the
number one rule to follow is that
variable names should be meaningful.
Remember our warm-up question here? It
would make much more sense if I had to
use a more explicit variable naming.
See how the one down here feels like
this program is telling you exactly what
it is doing and it flows like an article
with high readability.
However, there is a few restriction on
variable naming. One is that we cannot
have any empty space within the
variable's name.
To fix that, we just replace the empty
space with an underscore symbol.
Another one is the first character of a
variable cannot be an integer, but we
could put integer anywhere afterwards.
At the moment, everything doesn't need
to be perfect. We're all beginners here.
If we make a mistake, the clever Python
interpreter is going to tell us where
the mistake happens.
We just need to go and fix it.
Today, we are going to learn about if,
else, and Boolean values. With their
help, our program gains ability to make
decisions, obtaining intelligence in
some way. As human, we really make
decision in a very subjective way, and
often times, we couldn't even precisely
explain our decision. We make decision
based on our mood, how tired we feel,
how hungry we feel, our friend's opinion
on certain matter.
Unlike humans, computer programs make
decision in the most objective way. It
evaluates the current condition. Is it
true?
Or is it false?
And nothing else. We call those two the
Boolean values.
You might wonder, Boolean, what a weird
name. So, they're actually named after a
famous logician, George Boole, who
believes true and false are the only two
values that's sufficient to describe all
the logic in this world.
The Boolean value is a new data type,
which can be stored by a variable.
If I print A, it prints true.
If I print B, it prints false.
Notice, true, false, they're both
capitalize.
Combined with if else, the Boolean
allows our program to make decision. I
think a demo is the best way to explain
this.
The behavior here is pretty
straightforward. Python still executes
from top to bottom.
In line number four, the if statement is
checking whether happy mood is true. And
now, it is.
When the if condition is true, our
program would execute the indented code
block under the if statement,
and will deactivate or completely ignore
the indented code block under else.
>> On the other hand, suppose happy mood is
false.
The if statement is facing a false
condition, and therefore, it would
deactivate the following indented code
block and go ahead to execute the else
part.
In short, using if else, our program can
take different actions based on
different conditions.
In the demo we just saw, there were a
piece of syntax we never seen.
Whenever we use if statement to check
for a condition, this line must
terminate with a colon, and the
following line must be indented.
Together, they're pretty much saying,
"This indented code block only belongs
to the statement here."
The indentation is a must, and there are
two ways you can do it. You can either
indent with one tap
or you can indent with four spaces.
Tap versus space, just who cares?
Surprisingly, many, many people. There
have been a long bloody war among the
programming community fighting over
which method is superior to indent your
code.
Linus Torvalds and Chris Latner are
perhaps two of humanity's most important
software engineer. One uses tap, the
other uses spaces. Personally, I'm
really in no position to imply which one
is better, so you pick your side from
here.
The values true, false, are two
intrinsic Boolean values,
but we can also have expressions that is
equivalent to true
or equivalent to false.
For example, the value of K is 1. K + 1
less than 5. This expression is
obviously correct.
If I print this, it actually prints
true.
If I change expression to K + 1 greater
than 5, then it prints false.
Those two symbols are what we call the
comparison operator because they
literally compare the left and
right-hand side, and the result is
comparison is always a Boolean value.
There are a few more of them.
We use this one to check for less than
or equal to.
This one for greater than or equal to.
The double equal sign to check for
equality.
The exclamation equal to check for
inequality.
1 + 1 is equal to 2. This expression is
true.
1 + 1 is equal to 3. This expression is
false.
The string of three is equal to the
number three.
False. And therefore, it is true to say
they are not equal.
The value of A is a string that says
protein.
If A is equal to the string fat, then we
print they're equal.
Else, we print not equal.
You might wonder, it's pretty weird to
write two equal sign next to each other.
Why can't we just use the one equal sign
to check for equality?
Remember, we have already reserved the
single equal sign exclusively for
variable assignment. Therefore, we must
use something different for actually
checking equality.
In the video game The Legend of Zelda,
the currency is called rupee.
Suppose our main character has
absolutely no money now. So, we set the
variable money to zero.
But, he finds a chest box. If the item
in the box is actually a green rupee,
which has a value of one in this
currency system,
our character has money now, and we
increase his money by one.
But, oftentimes the item is something
else, such as food,
weapon, minerals.
In those cases, the main character need
to sell the item in exchange for rupee.
Perfect. This code uses if else to
express the logic we have.
If I change the item to sword, then our
character has zero money.
Mhm, there is one issue. In this game,
there are also rupees of other colors, a
red rupee worth 20,
or if you get super lucky, there is the
gold rupee, which worth 300.
So, suppose you are the game developer,
how do you change this program to
include more conditions?
One easy way is to use the elif
statement, which stands for else if.
You can think of elif as an additional
checking of condition in between if and
else.
In our case, if the item is not a green
rupee, rather than directly skipping to
the else part, we can use the elif to
check if the item is a red rupee.
And after that, another elif to check if
the item is a gold rupee.
In summary, the elif statement give us
more condition to evaluate in the
process of decision-making.
In this game, we can also eat things to
restore health.
But, whenever we're trying to eat
things, the gaming system is first going
to check if the thing is actually
edible.
So, please take a moment to consider
this code right here.
As ST meat skewer is edible.
An ancient gear is not.
So a simple if else will check for this
logic.
But actually even if our item is edible,
it doesn't mean we can consume it right
now. There is one more condition to be
met, which is our character cannot be
full health.
If the character's maximum health is
four, then his current health must be
less than four in order to eat food.
So how can we encode this logic into our
program?
Our attempt is to add another pair of if
else within the first if condition.
So notice this line is actually indented
twice.
You can think of this logic here is
saying something like this.
If the item is edible, then it still
needs to go through this entire
evaluation procedure again.
If our food is not edible, then it just
skip to the else part.
Wait. Line seven, the condition here is
clearly true. Our current health is 2.5
and the maximum health is four. So why
is line number eight not executing?
Very good question. The key property of
if statement inside another if statement
is that whenever you want to check for
an inner condition, the outer condition
must be true first. If the outer
condition is false, it doesn't matter
how true the inside is because the
program will just never go in there.
So if I change the food back to zesty
meat skewer, then the character can eat
it.
But if I change the current health to
four, then he cannot.
Whenever we have if inside another if,
this is called a nested if statement.
Honestly, when I first learned a Python,
nested structure was pretty hard for me
to comprehend. The only way out is
just doing some coding exercise.
And I'll take a moment here to compare
and contrast between elif versus nested
if.
Initially today, the only thing we know
about is if else.
The use of elif is much like adding a
new path in between them.
When the condition for if is false, elif
give another condition to evaluate
before jumping straight to the path of
else.
The use of nested if is much more like
extending an additional path from the
existing one.
Suppose we want to get here, the only
way is for the outer condition to be
true.
And after that, the inner condition is
also true.
Often times, certain action can only be
taken if two conditions are true at the
same time. In the game, even a simple
action such as shooting an arrow
requires two conditions to be true.
One, we need to have a bow. Two, we need
to have at least one arrow.
This is where logical operators comes in
handy. The logical operator and allows
us to create a Boolean expression that
is only true when both condition on each
side are true.
If either condition is false, then the
overall expression evaluates to false.
This expression would evaluate to false.
Four is equal to four. True.
Zero is greater than zero. False.
True and false would evaluate to false.
Therefore, we can use the and operator
to encode our logic for shooting arrow.
If has bow is true and arrow counts is
greater than zero, then we shoot arrow.
Else, we cannot.
Changing the variable has bow to false,
the program outputs cannot shoot arrow."
If we're really being serious, we should
also decrease the main character's arrow
counts by one because he just shot it.
In contrast to and, there is the or
logical operator. The or creates a
Boolean expression that evaluates to
true as long as either one condition is
true.
One way to compare them is that and is
very strict. Both conditions need to be
true for the expression to be true. Or
is kind of soft. As long as one
condition is true, it is true overall
and it doesn't care which one is true.
When our character explores cold area,
he actually lose health points for being
physically exposing low temperature.
But, as long as he's either wearing warm
clothes
or he ate spicy food. Apparently, in the
game, eating spicy food give you cold
resistance. He no longer takes damage
from the cold weather.
This is a good application for the or
logical operator. If has a warm cloth or
ate spicy food, the character is staying
warm. Else, he's taking damage from the
cold weather.
You might hear the term short-circuiting
when people describe the evaluation for
chain of condition in Python.
It's natural to suspect how does
Python's logical operator and or ever
relate to a physical circuit? Although
the next part of the video does not help
with your understanding of Python, but
is pretty cool. So, I got to show you
their connection.
To begin with, suppose we have a Boolean
value true.
We can represent this as a closed gate
where electrons flows through.
If the Boolean is false, then it will be
seen as an open gate where electrons
doesn't get to flow through.
Now, here is a cool thing. Suppose we
have two Booleans, we AND them together.
This is essentially putting the two
gates in series. The only way the
electrons can flow through is for both
gates to be closed. If either one gate
is open, the electron gets stuck in the
middle, which corresponds to the
expression evaluating to false.
On the other hand, if we OR the two
Booleans, it's like putting the two
gates in parallel. As long as one gate
is closed, doesn't matter which one,
electrons can flow through the end. When
both gates are open, that's when the
expression is false.
When we have a long chain of AND
condition together, it is essentially a
chain of gates in series.
If one gate is open, electrons cannot
flow through.
When we have a long chain of OR
conditions, the gates are parallel
together. As long as one of them is
closed, the electrons will be able to
flow through.
The lesson today was a lot of
information. I'll give a quick summary.
We first learn about the Boolean values,
the if else statement. At this point,
our program already has ability to make
simple decisions.
And then, there were the comparison
operators, logical operators, which
allows us to create Boolean expression
that ultimately evaluates to true or
evaluates to false.
With the help of elif and nested if, our
program can behave properly when there
are multiple conditions or there's
dependency between conditions.
Using all of them combined, we can
pretty much describe any software design
logic, and George Boole will be proud of
you.
Python list is a very powerful data
type. A list is like a chest box of
data. You can store any numbers of data
in there and retrieve them later
whenever you want.
But I know you behind the screen is
clearly someone clever. If I just read
over the documentation word by word,
it's boring. So instead, let's begin by
telling a story about the history of
poker suits using Python list.
Way back in the 14th century, the suits
of the playing cards in Spain were
nothing but familiar. The four suits
were cups, coins, clubs, swords.
The suits, which you and me are familiar
with, hearts and diamond, were not in
there.
Yet, it was true that clubs wasn't
there.
People decided to make things more
interesting by introducing an additional
suit, which is called shields.
And there were five different suits in a
deck.
Many decades later, the Germans decided
to build their own deck by starting from
scratch.
They added the suits hearts, bells,
acorns, and leaves.
And those four suits gained immense
popularity in Europe, but of course, not
as popular as its successor. The French
suits drew inspiration from the German
suits. They kept the hearts, but removed
the bells.
They replaced acorn with clubs and
replaced leaves with spades.
And finally, they added in the diamonds.
Hearts, clubs, spades, diamonds. They
survived all the way to the 21st
century.
All right, that was a lot of
informations. Let's take a moment to
dissect on what just happened. To create
a Python list, we use a square bracket
on a keyboard. If you ever wonder what
those weird-looking brackets are for.
They're used by programmers.
Normally, we create an empty list
without any data in there.
We can also create a list with some
initial data. We will use the comma to
separate them, distinguishing data from
datas.
Sometimes, people use the word element
referring to a particular data in a
list. I'll use those two terms
interchangeably in this video, too.
Here, you definitely notice something
strange. The variable my list does not
store the entire list, which is what
everyone expected. But, rather, it is
storing an arrow which reference to the
actual list. Why? Strange, indeed.
In later chapter called value versus
reference, we'll have a much more
in-depth conversation about this and
understand why it's really important.
For now, it's good to have this mental
image. Variables such as number, string,
boolean, the actual data is stored.
But, a list variable only stores a
reference to the actual list.
After creating list, much of your
programming interaction with Python list
stems from these four primary
operations.
Access,
modify,
add,
remove an element from the list.
Zero and foremost, when we want to
access an element from a list, we must
know its index. For example, suppose I
want to get a first element, assign it
to the variable called item. We use the
name of the list,
square bracket, index zero.
Yep, what you see is correct. To get the
first element, we don't use number one,
we use the number zero. We call this
behavior zero indexing. This is pretty
important to emphasize again. The human
understanding of first actually
translates to index zero in Python.
You cannot imagine the number of human
intelligence hours that had been wasted
on this bug. Somewhere inside a massive
software, a programmer is trying to
access the first element, but the index
one is used instead of zero, causing the
entire program to behave incorrectly.
Now, here's a question for you. Suppose
I want to get a third element of a list,
which index would you use?
Index two.
So, the pattern is whenever you want to
access the nth element, the index used
is n minus one.
The action of modifying a particular
element feels almost identical to
modifying a variable.
We can modify any variable simply by
reassigning it to something else.
In the element case, we just reassign
the list index to something else. The
first element of crypto list is Bitcoin,
and I want to change the first element
to Litecoin.
I just write crypto list index zero
equal the string Litecoin.
A variable can update from its existing
value.
So can an element inside a Python list.
This code here updates the third
element, which is index two, by
multiplying 1.2 with its existing value.
Suppose I want to modify the element
Bitcoin to Litecoin. Does this code
snippet here do the job for me?
It looks like it does, but it doesn't.
The only thing we did here was to change
the variable coin.
The element of index zero was never
changed.
A pretty easy place to make mistake if
we're not careful.
When we want to add a new element to a
list, we use the append method.
Pretty straightforward. The string Kyoto
is now added to the end.
Wait, this is a new syntax we never
seen. What is a dot doing in the middle?
So, this is called a method. In later
chapter, we will formally define what a
method is.
This syntax is pretty much just saying,
after the dot symbol, we're doing some
action, and whatever the action is is
directly related to the item here.
One thing notable to mention, when we
append a variable to a list, we're
appending its value rather than a
symbol. The variable place stores a
string Kyoto. If we append place to
travel list, the string of Kyoto is
added to the end.
However, if this is your mental image,
you'll be incorrect.
So, yeah. If this feels like déjà vu,
it's because we really emphasized this
concept back in chapter two.
When we want to remove element, we use
the pop method.
By default, pop removes the last element
from a list.
We can also give it an index, and that
particular index element will be
removed.
Sometimes we don't know the index of the
element we want to remove. Suppose we
have a list of vegetables. One day, I
find out tomato is not vegetable. Tomato
is actually fruit. We want to remove
fruit from vegetable list, but we don't
know the exact index of tomato. Frankly,
we don't really care about its index, we
just want to remove it.
We use the remove method. We just need
to specify the data we want to remove,
not the index.
Our Python list will find it and remove
it for us.
Even after learning those four primary
operations, you might still feel there's
nothing that special about Python list.
So, it's time to expand our imagination
to something bigger. How about a Python
list that contains every word in Romeo
and Juliet?
So, I first Google Romeo and Juliet txt
and there's a good website hosted by
MIT. And I just copy this entire thing
and paste it on my Google Colab.
Now,
I'm going to use the triple quotation
mark in the very front
and the very end to enclose this entire
chunk of text.
This thing now is actually one valid
Python string. Look at the color. It's
yellow.
Which I can assign this to a variable
called RJ text stands for Romeo and
Juliet.
Triple quotation mark is another valid
way to create a Python string. The
reason we don't use single quotation
mark, which is what we always do, is
because there are new lines within this
text
and there could also be existing
quotation mark in there as well.
Allow me to inject a quick lesson on
string manipulation. Although these new
methods seem somewhat overwhelming, but
in fact, they're just performing very
simple action. The dot lower method
creates a new string after turning every
single character into lowercase.
The dot replace method creates a new
string, in our case, after replacing all
occurrences of comma with an empty
space.
The dot split method, which uses a
Python string to magically create a
Python list. Any empty space in the
string indicates boundary between
elements.
Coming back, we first use the lower
method to create a lowercase string of
Romeo and Juliet.
Then, we replace all all punctuation
mark with empty spaces
And assign this to the variable
RJ_text_no_pmark.
Of course, there are other punctuation
mark besides commas. So, we update the
variable text_no_pmark based on its
existing value, but further replacing
out all the period marks.
And we repeat the same process for the
rest of them.
And then finally, we use the split
method to create a list version of the
string.
If I print RJ_list, what I see is a long
long Python list.
This list contains every single word of
Romeo and Juliet, which is what we want.
Let's ask some basic questions. What's
the first word in there?
What's the second word in there?
What's the 500th word in there?
I can use the l e n len function to
check for how many elements this list
have.
This thing is a number, so I can assign
this to a variable and print it.
Our list has 25,000 strings in there.
Besides the comparison operator, the in
keyword is another tool we have to
create a boolean expression by asking if
a certain data is indeed within the
list.
We can ask, is the string Romeo in
RJ_list?
How about the string Juliet?
False.
Oh, it's because I misspelled Juliet.
Is Shakespeare's iconic thou in there?
Is the word love in there?
False.
How come? I thought this is love story.
It's because I made a mistake. Remember,
everything has been turned into
lowercase, so it's impossible for any
capitalized string to be found within
RJ_list. I need to lowercase love as
well.
Is the string poison in there?
Since the in keyword us a boolean value,
so we can write a if-else logic.
The string robot is not in there.
The string Python is also not in there.
See, we don't even need to read the
entire thing. We can conclude that the
story is not about robot nor Python.
If anyone ask me what's the most
important concept in basic programming,
I would say for loop.
However, for us beginners, the first
time seen this idea, it seems really
abstract and non-intuitive. So, let's
motivate this concept with a real-world
example. Potato is working as a software
engineer for Google now. His job is to
print out every element of a Python list
while saying hi.
And he wrote this program.
Potato's clever solution is to access
the first element and assign it to the
variable user
and print hi user.
After that, the variable user changes to
the second element and print hi user.
And after that, user changes to the
third element and prints hi user again.
And finally, he prints work done, going
home.
As you can see, Potato's overall
strategy is to create just one variable,
user.
And he keeps recycling this variable by
manually changing it to the next index
and then print hi user. His program
executes perfectly. Good job, Potato.
Everything is fun and games until this
list starts to get really long. One
morning, Potato realized user list has
5,000 elements.
Does it imply he's going to write code
like this?
I mean, this works, but our Potato is
definitely exhausted.
Looking at his 6-hours long effort,
Potato realized that his code is
extremely repetitive. He is essentially
just printing hi user over and over
While the variable itself changes to the
next one and to the next one and to the
next one.
He wonders, what if the variable user
can be a little smarter such that it
changes on its own to the next element
one by one.
So that I only need to write hi user
once.
And this is the entire premise of for
loop, which is to automate the
repetitive simple actions finding small
patterns to solve big problems. Now,
potato writes
for user
in user list colon
indentation
print hi user.
With this type of syntax, a for loop
begins.
The loop variable user is just going to
change on its own, hopping to the next
element, changes its value to that,
hopping to the next element and changes
again. Every time after user change the
next element, the entire indented code
block under the for statement will be
executed.
The for loop is going to repeat this
change execute change execute change
execute alternating actions tirelessly
all the way to the end. There is a
sensation that the for loop is running.
No matter how long this Python list is,
whether it's 50 elements, 50,000
elements, or 50 million elements,
potato's program would run properly.
A few syntax to point out. Remember in
the previous chapter, we learned that
the in keyword can check whether data is
indeed inside a list. The same in also
appears when we're writing for loop. I
just want to say those two in means
complete different things under the two
different contexts.
This in is more like asking a question.
Is it in there?
This in is more like a statement saying
that the loop variable user will change
its value one by one to every element
inside the list.
Once again, we see the familiar colon
and indentation syntax, which we had
previously seen with if else statement.
Only the indented code block will be
executed repeatedly when the loop is
running. Things outside a for loop will
not be a part of the loop action.
And this indented code block can
definitely be well more than one line.
Up next, we're going to get ourselves
more familiarized with for loop by
tackling three coding tasks while
highlighting some common mistakes and
misconceptions along the way.
Suppose we have a list of numbers. This
list can be short or very long.
Your goal is to find out what's the
summation of the list or if we literally
add all the numbers together, what would
it be?
If you've never seen a question like
this before, I promise you the answer
will be non-obvious. So, for sure take a
moment to pause and ponder.
So, the central idea is to create a
variable outside before the loop starts
running. We initialize the variable S to
zero.
When the for loop is running, we keep
adding values into the variable S.
Currently, the loop variable number is
100. The code block to execute is S
equals to S plus number, which means we
update S by adding 100 to it. So, S
becomes zero plus 100, which is 100.
After that, the variable number hops to
the next one. Number changes value to
five. The value of S at this point is
100. We increase it by 5. So, S becomes
105. Of course, the variable number hops
onto the next one again and updates S
again.
This process repeats. So, the variable S
is getting bigger and bigger by
accumulating all the index the loop has
passed through.
By the time when the loop terminates,
the variable S has already collected all
the values along the way.
Pretty ingenious, right?
The variable S is a critical data we
want to update in the midst of a for
loop. A very common mistake, which I
definitely made often, is that we put
the variable S inside a for loop rather
than the outside.
Let's see why this is a big problem.
S equal to zero. Since the variable S
does not exist yet, this means we're
going to create the variable S and
initialize it to zero.
S equal to S plus 100, so S becomes 100.
So far, so good. Number hops to the next
element. As you can see, the line S
equal to zero is within the indented
code block. So, it's going to get itself
executed every time.
S is reset to zero.
But, what about the 100 we had seen
before?
It's not accumulating anything.
Even when this loop reaches to the end,
the variable S does not remember
anything it seen before. Obviously, this
is a problem.
So, the lesson here is we have to be
really cautious about what variable to
put inside versus the outside of a for
loop.
All right. Spent a long time on the
previous one.
Task two. Suppose we have a list of
natural numbers.
How can we find the biggest number in
there?
The solution, once again, is that we're
going to create a variable outside the
for loop called the current max. The
idea is to have this current max
variable to store the biggest number the
loop has seen so far. So, it's
initialized to the smallest natural
number.
As the loop starts running, we use a
condition statement to check whether we
should change current max.
For example, at this moment, loop
variable number is 17, and current max
is five. 17 is bigger than five, but
this also means that the current element
is bigger than every single element the
loop has yet seen. So, we must change
the value of the current max to 17.
On the contrary, if the value of number
is less than current max, for example,
number is 12 and current max is 17. That
means there definitely exists an element
somewhere before the current index it is
bigger than 12. So, we don't change
anything and move forward.
As the loop reaches through the end, the
variable current max has been compared
with every single index. Every time when
current max faces a larger number, it
changes to that. So, by the end, current
max certainly stores the biggest number
in this list.
Common question one, do I have to name
the loop variable as number?
Nope. You have complete freedom in
choosing the variable name. You can also
name it current element or just cur
or even c.
As long as you use them consistently,
it's going to work.
Common question two, when the loop
terminates, namely outside the loop's
indentation, can I do something about
the variable c? Like, can I print it?
So, we cannot. The loop variable is kind
of like a ephemeral variable that only
exists when the loop is running.
When the loop terminates, the variable C
just vanishes after having done all its
work. It disappears completely from the
environment of our program. Outside the
for loop, we cannot do anything about
this variable.
Suppose you and many friends are going
on a road trip. You surveyed everyone
for movies to watch together as a group.
You asked them to append their own
favorite movie to the list.
And since certain movies are really
popular, this list has some
redundancies.
As a group leader, you don't want to
bore everyone by asking them to watch
the same movie twice. Your goal is to
create another list in which every
element is unique. Or every movie only
appears in there once.
The idea is similar. We create an empty
list outside. As the for loop starts
running, we conditionally append movies
in there.
Since I never touch on this not logical
operator, let me inject a quick lesson
on this.
Suppose I have a Boolean true.
By putting not in front of it, it
negates to false.
By putting not in front of false, it
negates to true.
The thing on top is a Boolean expression
that evaluates to true.
By putting not in front of it, it
evaluates to false.
As the loop begin, the variable MV
stores a string Endgame.
By our design, unique list cannot have
any redundant element. We only append
new movies in there if it is currently
not in unique list.
At the moment, unique list is empty and
Endgame is not in there. We append.
Hopping to the next one. Spider-Verse
not in unique list. True, we append.
Next one.
Endgame is already in there, we do not
append.
So, if my verbal commentary of four
loops start to get pretty monotonous and
predictable for you,
hey, yeah, you start to really
internalize four loop now.
Looking back at our journey so far, we
attempted to define a word programming.
In chapter zero, we said programming is
creating interesting things. In chapter
two, we said programming is art of
changing variables. Today, the
definition is more refined. Programming
is art of changing variable in the midst
of running four loop with conditional
statement.
The reason why I cherry-picked those
three particular examples, because on
one hand, I think it really drives this
thesis home. On the other hand, there's
some concrete setting for us to engage
with programmatic thinking. Rather than
just using four loop to print out Python
list elements. Four loop is about
finding small pattern to solve big
problem using their few lines of code to
work with a list of data that could be
unimaginably long.
In many future coding situation, I
promise you that you can find archetype
of thinking stemming from these three
examples.
Next chapter, we'll learn about a more
generalized technique of looping, and
we'll see many beautiful connections
between coding and mathematics. See you
there.
In the previous chapter, we learned that
whenever there's a Python list, we can
use a four loop. The four loop is a
great method for automating repetitive
and simple actions.
The loop variable keeps hopping onto the
next element, changes value to that, and
the indented code block executes. Those
action repeats. There is a sensation
that the four loop is running.
As programmer, we call this running a
for loop over a list or simply looping
over list.
But a more professional way to call this
is iterating over a list. As vocab,
iterate means to do again, do again, do
again.
Yet, Python list is not the only thing
that can be iterated over. In fact, it
belongs to a larger abstract class of
objects called iterable.
I know this entire playlist is aimed for
beginners, so we're not going to the
details about what exactly this word
mean. So, for now, if I tell you
something is an iterable, that means it
can be a target of for loop. And the
range object is an iterable.
By calling the range function with a
start and end index, we get a range
object.
But what exactly is this range object?
You can basically think about it as a
sequence of numbers that can be loop
over.
When the for loop starts, the loop
variable I iteratively binds to all
integer from 0 to 9.
As you see, when we loop over the range
object, it logically just feels like
we're looping over a Python list, which
contains the sequential integers.
Notice range 0 10 does not give you the
number 10. It only gives you all the
number from 0 to 9.
This is because Python's end index is
always non-inclusive. If we want the
number 10 in there, we have to change
this to range 0 11.
So, what exactly can we do about this
range object? A sequence of numbers
doesn't seem any useful at all.
We can use the range to indirectly loop
over another Python list.
For the range object, the start index is
0 and the end index equals to the length
of the list.
As the for loop begins, the loop
variable I is integer,
but it conveniently also represent the
index of my list. Then, we can access
the corresponding element using this
index
and store it into variable var
and print it.
Since we're iterating over all the
indices, we ensure that every element is
visited. What you see now is another
perfect valid way of looping over a
list.
So, let's take a moment to compare and
contrast between the two strategy of
looping.
In the direct method on left, the loop
variable var directly binds to the
element, and we can use it immediately.
On the right, the indirect method, where
looping over a sequence of numbers,
the loop variable I only binds to an
integer, and then we use the integer as
the index to access the corresponding
element.
So, this one here is a two-step process.
Potato's like, "The indirect method is
so inefficient. Why would anyone ever
want to loop over a bunch of indices?
Just do it directly."
Thank you, potato. That's indeed a very
good question. It turns out looping over
indices has the advantage in certain
cases.
Consider this scenario. We have a list
of numbers, which stores the hourly
salary of six different employees,
and we want to increase everyone's money
by $5.
Potato's like, "Easy. Gotcha." He writes
a Python code for salary in salary list,
salary equal salary plus five.
As the loop begin,
you already see the problem.
The only thing that this code is doing
is just increment the loop variable
salary by five.
But the actual element in salary list is
never changed.
Therefore, this code is incorrect.
So, to fix that, we must use the
indirect method, loop through the
indices of the list.
As the for loop begin, the loop variable
I iteratively binds to all the index of
salary list, and we increase the element
sitting at index I by five.
Here, we clearly see the data inside the
Python list has been changed.
So, the lesson is if we want to modify
the actual element inside the list, we
need to modify them through index.
Therefore, we must loop over the range
object.
Hey, isn't it bit verbose to write code
like this?
Yep, I should use the more compact
syntax of updating variable, writing
plus equal five instead.
Another application of range object is
that sometimes we want our program to
make decision based on the index of the
element rather than the value of the
element. Still using the salary list as
an example, but this time we want to
increase the salary of a few workers by
$10 because they're just so amazing.
Namely, there is another list which
stores the indices of those candidates
who we deem as amazing. For everyone
else, we still increase their money by
five.
Our program logic here is very
straightforward. We iterated over all
the indices with the help of if else, we
differentially increase the salary by
checking whether if the candidate is
amazing.
Loop variable I binds to zero and the
index zero is not inside amazing list.
So, our program jumps to the else part,
increase the salary by five.
I then binds to one. The index one
inside amazing list, true.
So, we increase the element by 10.
The same logic repeats all the way till
the end.
In general, as a Python programmer, it
is important to be absolutely fluent
with both technique. So, depending on
different coding task, we can always
pick the most optimal strategy to loop
over list.
Yet, sometimes our main focus does not
have to be a list, but rather a sequence
of number themselves. In particular,
when we're doing computation that
relates to math.
Have you ever seen a funny-looking thing
like this? An exclamation mark next to a
number.
It's called the factorial of that
number.
The factorial appears often in
probability, number theory, calculus,
and many other fields of mathematics,
playing important role in mysteriously
elegant equations.
Yet, its definition is very simple. The
factorial of a number n is defined as
the product of all the numbers from n to
one.
For example, 5! is 5 * 4 * 3 * 2 * 1.
If someone throws me a pen and paper and
ask me to calculate this expression,
this is what I would do.
Firstly, I know that multiplication is
commutative, so I can rearrange the
expression by reversing the numbers.
Then, I'll iteratively multiply the two
front numbers together. 1 * 2 is 2, 2 *
3 is 6, 6 * 4 is 24, 24 * 5 is 120. So,
5! is 120.
But, the process certainly gave me some
inspiration.
Those sequential numbers just look like
a ranged object.
The action of repeated multiplication
also rings a bell with the spirit of
loop.
So, let's write a program that can
compute the factorial of any positive
integer.
We initialize the variable P as 1
outside the loop. When the loop is
running, we iteratively multiply the
variable I into P.
The variable P basically serves as a
collector, storing the product of all
the natural number it had seen as a loop
marches forward.
You might wonder, why are we setting the
outer variable P to be 1 instead of 0?
Isn't that what we always do before?
The reason is because 0 multiplied by
anything is 0.
Suppose I accidentally initialize P = 0.
This is what's going to happen. P equals
to I * P, so P equals to 0 * 1, so P is
0.
P equals to 2 * 0, so P is still 0.
P equals to 3 * 0, so P is still 0.
As you see, the value of P is trapped at
0, never accumulating anything. This is
a problem.
Normally, when the purpose of the for
loop is to add bunch of numbers
together, the outer variable is
initialized to zero.
When the purpose of the loop is to
multiply bunch of numbers together, the
outer variable is initialized to one.
N is a variable, and I can initialize it
to a different value, such as 52.
Run this again.
The factorial of 52
Huh, this number is pretty big.
You know, the lesson so far today has
been pretty intense.
So, why don't we take a break together
by taking a detour on this 52 factorial?
This number is in fact so big that
people are making videos online
explaining how big the number is.
While I was watching them, I got a sense
that human beings are actually pretty
bad at comprehending big numbers.
52 factorial is a big number. The net
worth of Elon Musk is also a big number.
Currently, in 2022, Elon's net worth is
estimated to be around 200 billion
dollars.
I believe a few years later, that number
would increase to 800 billion dollars.
But, you know what's a number so much
and so much bigger than that? The number
of atoms on Earth.
Which is estimated to be 1.33 * 10 to
the power of 50.
All right. The next part might appear
weird to you, but imagine if every tiny
little single atom on Earth, each and
every one of them, transforms into their
own pile of 800 billion dollar cash. The
aggregation of total value here is
really immense.
And that equals to Elon money times num
atoms.
And this is truly a big number.
So, which number is bigger? 52 factorial
or 800 billion times the number of atoms
on Earth?
Personally, I think the latter number
must be bigger.
Oops, I'm wrong. 52 factorial is bigger.
Well, then what fraction does this big
number occupy in 52 factorial?
1.31 * 10 to the power of minus six.
That's a small portion. To visualize,
the yellow cube represents 800 billion
times the number of atom on Earth.
The larger green cube represents
52 factorial.
Wow, this 52 factorial is so big. In
what occasion does this even appear in a
human's life?
It's all the different unique ways you
can rearrange a poker deck.
All right, break is over. Let's come
back to Forloop.
Mushroom is taking a math class and she
got this answer for a question. This
entity may look unfamiliar to some
people. It's called the sigma notation.
It appears pretty often in math,
actually. Mushroom wants to find out the
numerical value of this expression.
Since Potato knows Python, she goes to
Potato for help.
However, Potato is scared anytime when a
Greek symbol appears in math. He doesn't
know whatever this thing is. So,
Mushroom explains.
The sigma notation is actually only
doing something very simple.
Consider all the natural numbers
starting at one ending at 10.
We pattern match this expression to them
one by one.
And then, we add them together.
Sigma notation is just a compact way of
describing summation.
Potato understands now.
But, wait a second. A sequence of
consecutive numbers adding a bunch of
numbers together both of which are just
screaming out use for loop.
I mean, one day in the future, whenever
you see a sigma notation in math, you're
just going to involuntarily ask yourself
it's just a for loop, right?
Potato picks up Python and passionately
programs.
And I will stop talking to let the
animation speak for itself.
Whenever we use a for loop, we can
literally put anything inside its
indented code block. Within loop, we can
print, we can do variable manipulation,
conditional statement, we really have
complete freedom as programmer.
But what if I put another for loop
inside a for loop, which results in a
nested for loop?
Loop inside loop. And this is when
things starts to get interesting. For
me, frankly, there's something
unsettling looking into a nested for
loop. What exactly happens? Most people
have this intuition about a nested
double for loop, saying, "Well, if a
single loop is all about one loop
variable hopping over each element of
its iterable,
then a double for loop must be about
Well, there are two different loop
variable,
then two different loop variable are
simultaneously running over two
iterable. And unfortunately, this
intuition about how double for loop
works is just incorrect.
So, what is the true behavior of a
double for loop?
Remember that there are two loop
variables, VL for the outer loop and VN
for the inner loop.
The VN needs to complete its entire trip
for VL to only advance by one step.
Or you can imagine this.
Every time when VL takes one step
forward, VN needs to complete its entire
loop.
VL takes another step forward, VN
completes another trip.
So, when the outer loop terminates, the
inner loop has been iterated over many,
many, many times.
At least for me, this behavior is
slightly unexpected. This is important.
Let's watch this again.
So, if we were to look at the output
generated by the print statement here,
it's going to look something like A1,
A2,
A3,
A4,
A5,
B1, B2, B3, B4, B5. You kind of get the
point.
You also notice the value of the outer
loop variable is persistent for the
entire duration of the inner loop. This
outer loop variable, which currently is
D, is used to concatenate with every
single inner loop element before it
expires and advance forward to become E.
Despite its complexity, nested for loop
is used inevitably in programming, such
as a matrix multiplication
and dynamic programming.
But today, let's just focus on a very
simple application.
Suppose I'm coding a poker game. So, the
first thing I need to do is to create a
deck of 52 cards,
which is conveniently represented as a
Python list.
I'll just manually add each card in
there one by one. I append two of
hearts, two of diamonds, clubs, and
spades.
And after doing this 52 times
diligently, I'm done with my deck of
cards. But, this just doesn't feel like
the most efficient way of doing things.
I could make the observation that a
poker deck only has 13 different rank
and four different suits.
The formation of a deck is just each
rank take four different version of
suit.
It's just combination.
And this looks suspiciously like a
double for loop.
Therefore, I can write my Python code as
I have my outer loop iterating through
all ranks.
The inner loop iterating over each suit.
And the card is simply the concatenation
of a suit and rank.
And I append the card to my cards.
And this double for loop will get my
deck of 52 cards.
At this point, it's totally okay to feel
nested loop is kind of hard.
Absolutely. Especially if it's your
first time seeing this.
Compared to a single for loop, the
complexity of double for loop is in fact
not double, but square.
Unfortunately, things can even get
harder from here.
Let's take a moment to compare the two
different program here.
Can you spot out why the code on the
right is much more difficult to
understand?
Feel free to pause and ponder.
All right.
If we look at the left program, the
inner loop always loops over from the
range 0 to 5, independent of the outer
loop.
On the contrary, the right one, the
inner loop is iterating over range 0 to
I.
But, what is I?
I is the outer loop variable, which
means it changes through the execution
of the program.
The iterable of the inner loop depends
on the outer loop. There is a
fundamental dependency between the two
loops.
So, what does that look like?
When the outer loop just begin, the loop
variable I binds to 5. According to our
program, the inner loop needs to take a
trip from 0 to 4.
As the inner loop terminates, the outer
loop take one step forward. I is now 6,
which means the inner loop needs to
iterate from 0 to 5, which is one more
than its previous run.
I binds to 7, the inner loop goes from 0
to 6.
I binds to 8, the inner loop needs to go
from 0 to 7.
I binds to 9, the inner loop needs to go
from 0 to 8. So, you already see the
pattern. As the program execute, the
extent in which the inner loop needs to
iterate also increase. The behavior of
the inner loop, how much work it does,
is depend on the outer loop.
Why would a programmer ever use a nested
loop that has dependency?
Well, remember from last chapter, we
learned about factorial.
If we want to compute the factorial of
integer n, we use a for loop from 1 to
n, iteratively multiplying each number,
and accumulate into p.
However, what happen if someone throws
me list of integer, and ask me to
compute each one's factorial? What
should I do?
Let's look at this again. This block of
code computes the factorial of n, and n
is a variable. I can change n to 11 and
see that the factorial of 11 is a big
number.
So, why can't I change n into a loop
variable iterating over number list? And
it would just migrate this entire code
block inside, of course, indented.
Notice we have a nested loop with
dependency of the inner loop range from
1 to n + 1.
The mission of the outer loop is to
iterate all integers from this given
list. The mission of the inner loop is
compute the factorial of that
corresponding n.
Execute.
Great.
There is one detail to point out. Notice
on line two, p equal to 1 is in between
the outer loop and the inner loop. This
means that every time when the variable
n take one step forward, rather than
launching the inner loop immediately,
the first thing it does is to reset p
back to 1, then follows the entire inner
loop. This reset is critically important
because when the inner loop is
calculating the factorial, the value p
gets pretty big.
As the outer loop take one step forward,
we need to reset p back to 1, so the
residual from the previous inner loop
does not linger over.
Loop is the most important concept in
the basics of programming, which is why
we spent three chapter on this topic. We
start learning about loop from a visual
approach. We see with our eye that the
loop is really running. In part two, we
learn about how to loop with the range.
And today, we saw the unexpected
complexity of a nested for loop.
In terms of big picture of essence of
Python, we have already added these
items to our toolbox. Variable
manipulation, which is a unit of
compute, conditional statement, decision
making, Python list, data structure, for
loop, automation of logic.
These combined may only occupy 20% of
Python's programming feature and syntax,
but the core concept are definitely
relevant in at least 80% of any
programming thinking.
An interesting note aside, when Vitalik
was developing the next generation
blockchain, Ethereum, he was especially
aware about the feature of loop. He
realized that Bitcoin's foundational
limitation is that it doesn't allow
loop. So, he added a loop in Ethereum,
which enabled it to become versatile
platform for developer to make
interesting application.
When people say function, we think about
the X and Y axis. This is a linear
function. This is a quadratic function.
Our impression about function is often a
static image we see in our math
textbook. However, this is actually
quite misleading because the essence of
function is not static. It's dynamic.
Function is all about the action of
transforming input into output.
For example, this function here computes
the squared value of any number.
Five enters the function. Five squared
is 25.
And 25 is the output.
And this function here is capable of
interleaving two input string. A A A A,
B B B B are the inputs. The function
inter- leaves them
and outputs the longer string.
What you saw just now very much
resembles the essence of a Python
function. There is an input. The
function does something to the input and
returns the output back to you.
This is a good mental picture to have
before the lesson begins.
In Python, function is a great way for
organizing logic and prevents you from
writing repetitive code. To motivate
this, let's see an example.
Recall in chapter five, potato mastered
the use of for loop, and now he's a
senior engineer at YouTube.
But, what is he doing today? You can
pause the video to take a look.
Seems like potato is writing a greeting
message to members in the gold, silver,
and bronze class.
If today is a member's birthday, he
writes, "Happy birthday." Plus the
person's name.
Else, simply greet with, "Hey."
Lastly, the message is printed with the
person's class tier.
However, if you were to notice, these
three blocks of code are identical. This
is very repetitive, and this is not good
code.
Really, in those three places, potato is
seeking for the same functionality.
Given person's name and his birthday as
input, we want to get its corresponding
message as the output. This is a great
place we should use a function.
Finally, how do you make a function?
We use the def keyword, short for
define. Follow that, we give the
function a name, greet, and then, within
the parentheses, we specify what are the
inputs to the function, which is the
name and birthday today.
The professional terminology is that
they are the parameters of the function.
Now, we can write code that performs
action to the parameters.
Notice indentation here, which is saying
everything within the indentation belong
to the function greet.
The return keyword specify the output,
which is the conditional message.
I want to take a moment to emphasize
that the return statement here is very
important. If you forget to write a
return statement, you can imagine that
your desired output gets stuck inside a
function with nothing visible from the
outside.
This entire thing is a function
definition. We define the input, the
action, the output. But when a function
is defined, nothing happens yet. It's
sort of just sleeping there, but ready
to be summoned.
It is not until the line you are calling
the function with actual input. Notice
the def keyword is not here, and the
parentheses contains real string and
boolean.
This is when you want your function to
do its magic.
The greet function transform the input
into the output.
Question. What exactly is this parameter
thing? Are they like variable?
They're a little unintuitive to look at.
Good question.
They're not exactly a variable.
If you think about it, when we define a
function, we never know what will be the
actual input. They could be anything.
Therefore, the parameter serves as a
placeholder variable, and we write code
in respect to those placeholder.
Only when we're calling the function,
the placeholder variable manifest into
the value of the real input.
Let's look at the execution of function
in detail.
Once again, when the function is
defined, nothing happens.
At line number eight, function greet is
called with tangible input name for Erin
and a false for birthday today.
The two placeholder now manifest into
Erin and false.
And then the original code for the
function executes.
And the value message is returned
and bind it to the variable M.
You might have noticed something. These
two assignment statement was not part of
the code we wrote.
But I intentionally put them here
because this is a correct mental picture
I want you to have.
When placeholder parameter finally takes
on the value of real input,
it is basically an implicit assignment
statement as they are top.
Now, we can go back and improve potato's
code.
Message now becomes the return value of
a function.
This code is much better now.
Let's look at a few more examples of
functions together, and you will see
just how versatile function design can
be.
You can make a function that has one
parameter,
or three parameters,
or 10 parameters,
or even zero parameter.
Notice when you're calling a function
that has zero parameter, you still need
to pass in the parentheses,
but just with nothing inside.
You can almost sort of think the
parentheses as something that activates
the function.
What code can I write inside a function?
Literally anything.
You can write a for loop. This function
computes a factorial. You can write
conditional statement. This function
converts temperature between Celsius and
Fahrenheit.
Your function can even use other
function, which is defined previously.
This check password strength function
uses two other function for its internal
logic.
In terms of function design, the only
limit is your imagination.
Many YouTube video out there often like
to motivate Python function as a way to
package print statement together,
rather than looking at the essence of
function, which is all about input and
output.
This type of teaching leads to a common
confusion, which is what is difference
between print versus return inside a
function.
Let's look at the two function here.
If I execute the function on the left,
it prints hi Bobo.
If I execute the function on the right,
it returns Hi Bobo and then I print it.
On both screen, I see Hi Bobo. So,
there's no difference, right?
Hm, not quite.
Notice the function on the left. It
doesn't have a return statement. So, the
function has no output.
What happens here?
When a function doesn't return anything,
Python secretly make the function return
a none value
to indicate the absence of anything.
So, if I bind the return value of the
function to M and I print M,
ah, what I get is a none here.
If I want to print Hi Bobo five times, I
cannot. I get none five times because
the variable M is a phony and the
function didn't return me anything at
all.
Whereas I could do with the function on
the right because it return.
Moral of the lesson here, don't fool
yourself by printing inside a function
and think that's a return value.
Before we wrap up a session today, there
is one more critical concept you need to
take away.
The return statement Python immediately
terminates the execution of function.
When the return statement is triggered,
the function stops in a non-negotiable
way.
My question for you, take a moment to
pause and ponder.
If I'm calling the boot computer
function on the input false,
would I see these lines of code being
printed?
The answer is no. I would not see them
because my input for has OS installed is
false.
At line number five, the if statement
evaluates not false, so true.
Then it executes the return zero, which
immediately terminates the function.
Everything afterwards is ignored.
By the way, I also just learned that you
could actually put emoji in Python
string.
Question. If you have a return statement
inside a for loop, can the return
statement even stop the for loop before
its completion?
Absolutely, it can.
Let's take a quick detour into prime
number. Quick review. A prime number is
a natural number strictly greater than
one that is not perfectly divisible by
any number smaller than itself other
than one.
For example, seven is a prime number
because when seven is divided by any
smaller number,
the remainder is always non-zero.
Nine on the other hand is not a prime
number
because 9 / 3 is 3
with a remainder of zero.
One thing I can tell you is that prime
number holds a special place in the
heart of mathematician and computer
scientist.
Prime numbers are the backbone for
internet security. Without prime, your
password, bank info, messages would all
just be public.
And as programmer can easily write a
function to check if a number is prime
or not.
The is_prime function uses for loop to
iteratively look at all the natural
numbers smaller than n, but excluding
one,
and it compute the remainder. If the
remainder is zero, then immediately we
know that it's not a prime number.
Only if all iteration of the loop has a
non-zero remainder, then n is a prime.
If what I said is a little confusing,
let's see what happens when I call the
function on the number 25.
So, firstly, n binds to 25. 25 is bigger
than one, so it skips here.
Now, we're running a for loop from 2 to
24.
We check the remainder of 25 / 2.
Not zero. So, we do not enter the return
statement.
We do the same for three.
We do the same for four.
Now, the remainder of 25 / 5 is zero and
the return statement is triggered.
This function immediately stop and
return false. 25 is not a prime number.
Notice how all the latter parts are just
ignored.
The loop didn't even get to them.
And finally, the return value false bind
with the variable result.
Well, since I just defined the is prime
function, I get to use it. Let's append
all the number between 1 and 1,000 to a
list.
Let's see who are these guys.
And how many of you are here?
What about between 1 and 10,000?
Hm, very cool.
Function, input, output. I said them a
million times and I bet you're tired of
it. But that's the essence of function
and I hope you can internalize that
idea.
Programming and computer science
ultimately is about abstraction
and abstractly speaking, many things are
functioning our life.
We know that this is a function,
but an ATM is also a function. The input
is your card and PIN number. The output
is the money.
Even this is a function. The input is a
character. The function miniaturize the
input and a smaller character is the
output.
You might have noticed something
interesting. Every time when a function
call happens, it creates a sandbox like
this.
All variable assignments, program logic
happens within.
When a function returns, this sandbox
vanishes.
You might wonder, what is so important
about this mental picture of a sandbox?
Well, let me show you.
We have variable X equal to 1.
Then we create a function.
But within a function, we have another
variable of the exact same name. X equal
to 25.
Then I do Y equal X, and then return Y.
So, the existential question here is
that when I assign Y equal to X, which X
am I looking at?
Is it the X outside the function, or the
X inside the function? Well, what's your
guess?
Let's execute the program and see what
happens.
Ooh.
It seems like it's 25.
So, the X inside the function.
During the function execution process,
we always prefer to look at the variable
within the sandbox.
Let me ask a follow-up question.
Suppose I change the code, and I remove
this line. So, there is no X within a
function definition.
If I still do Y equal to X, can Y find X
from line number one, or this program
would lead to an error?
Let's find out.
Y equal to X, but there's no X. So, the
function looks outside, and find X in
the global scope, and binds Y equal to
1. Seems like the program can indeed
find variable outside the function
definition.
These two examples just now highlights
an important message. In function
execution, we always prioritize variable
lookup in the local scope. Only if not
found, then we look into the global
scope.
The term local scope is a fancy but
professional way of saying everything
within the sandbox. And global scope is
everything outside.
Wait a second. Why would anyone write
code like this? This feels intentionally
confusing.
Why do you have to create a local
variable with the same name as a global
variable?
Just change it to a unique name and you
can avoid all this confusion.
Well,
I totally agree.
Almost no one at a tech company will
write code like this.
But distinguishing between local scope
and global scope is important. So, bear
with me here.
Local scope has access to global scope,
but the other way around is not true.
Once again, let me modify the code.
On line number nine, rather than print
result, I print K, which is defined only
within a function.
What happens?
When the program tries to print K,
but there is no K anywhere to be seen in
the global scope.
Well, there was a K when the function
was executing,
but it vanished alongside with the
sandbox.
So, this program actually give you an
error.
Let's talk about interesting topic
called function composition.
I have two function here. Function F
takes input R and compute pi r squared.
You might notice that's the formula for
area of a circle.
Function G simply multiply four for its
input.
What's so special about the four here?
It's because the surface area of a
sphere is actually four times the area
of a circle projection. So, if someone
asked me, what is the surface area of a
sphere that has radius five? I can first
compute the area of a circle whose
radius is five and store into variable
A. Then the function G multiply A by 4.
This gives me the surface area of that
sphere.
This is very good. But you might wonder,
since A is just a return value of of f
of phi,
why can't we just pass f of phi directly
as an input for the function G? And this
would actually make our code more
concise. Yes, you can.
But why is that?
In Python function call, you always
evaluate the input before jumping to the
function execution.
Evaluate is kind of my fancy way of
saying, if the input in my function is
actually another function call, I got to
figure out what is return value of the
inner function first.
This is what it looks like.
We first evaluate the function f of phi.
It returns 78.5.
And that becomes the input to the outer
function G.
And then the function call for G
happens.
I can even exaggerate this idea by
showing you a triple nested function
call.
But you already know what happens here.
We're calling function H.
But what is the input?
It's clearly not a number, but rather
another function call. So I need to
evaluate its input G of f of phi.
In order to do that, I need to evaluate
function G.
But I need to evaluate f of phi first.
You see, the process is almost like me
peeling an onion until we get to a
function call that's not nested. And
then it works its way backwards. When we
chain multiple functions together like
this, it's called composition.
And the idea of function composition
actually originates from math, not
programming.
For many difficult concepts in math, I
find myself only to truly understand
them after I learned programming. Where
I try to explain mathematical concepts
through the lens of Python.
In high school, the math class start
teaching you about function composition.
You probably see something like this.
And you already know, the next thing the
question is going I'm going you is, what
is f of g of 20?
After learning programming, this
question is almost trivial. I first
define f like the way it's written in
the question, and then I define g,
and then I define f of g,
and simply I call f of g on 20.
That's the answer.
Programming in essence is mathematics in
action. If this feels déjà vu to you,
you're probably thinking about chapter
6, where we said the scary sigma
notation is really just a for loop in
Python.
In the last two videos, I've been using
the word inputs a lot when describing
the thing that enters a function. I
didn't want to detour the lesson by
spending too much attention on
terminology. The more professional
terminology for input is actually
arguments for the function. Very
interesting choice of word right there.
Cool. Today's lesson is a bit tedious. I
want to end it with a quote from a
legendary programmer called Terry Davis.
Idiots admire complexity. Genius admire
simplicity. Terry is a bit of a
controversial figure, but I do agree
with this sentence here.
Programming is not about writing fancy
and convoluted code to impress your
peers. Programming is about writing the
simplest and readable code, so everyone
can tackle the same underlying problem
together. Let me give you an example. If
I were to look at this code, I won't be
able to figure out what's going on. I
see there's a for loop, but there's so
many things happening within the for
loop, which makes it difficult for my
brain to render what's the intention
behind. But the very identical logic
when written differently can be
instantly readable.
Look at the improved version here.
You see the programmer first break down
the big problem into multiple small
function definition,
and simply by reading the name of
function, you can gauge into its
underlying purpose.
And there's documentation. The goal is
to buy stocks based on Google search
trend and sentiment analysis based on
news article.
We have a list of potential candidates.
For each candidate, we check its Google
search trend. If there is momentum in
search volume for last 3 hours, it
passes the first round check as a trendy
stock.
Then, for the surviving trendy stock, we
fetch all the news from today about that
stock and ask GPT to rate a score from 0
to 10. If the score is more than 7, then
we buy it. And this is good code. This
is what peak programming looks like. It
pipeline multiple functions together
rather than trying to do everything in
one place. It feels like you're no
longer reading computer code, but you're
reading human language.
Today, we talk about a slightly advanced
topic, value versus reference.
A quick review. When we do variable
assignment for number and string, we
assign by making a copy of the value.
This is a correct mental picture.
Variable B takes on the value of 7, but
a common mistake is this one. In which
people imagining the assignment makes
variable B becoming variable A itself.
This is the incorrect mental picture to
have. With this understanding, we know
that if later on I reassign variable B
to something else, variable A is still
untouched. If I print A, it still prints
7.
Cool. So far, so good. If the variable
is number or string, we know what
happens. But, what about Python list?
Previously, I foreshadowed that when I
create a list, my variable is a
reference towards a list. Rather than my
variable containing a list, I never
explained why. So, what is your mental
picture when I do list assignment? Is it
this one? Do I create a reference to the
same list?
Or this one? I make a copy of the Python
list.
And surprisingly, we don't make a copy.
The new variable would have the same
reference to the original list. And this
has a profound implication.
Let's say if I change the second element
of list B,
I will first follow the reference
pointed by list B, and then modify the
second element. But you see, since the
list A also reference towards the same
data, my modification on B has
unintentionally affected A. If I print
A, it's no longer 1 2 3.
You see, assignment of list very much
forms a stark contrast with number and
string.
For number and string, you assign a new
variable by making a copy of the value.
Changing the new variable has absolutely
no impact on the original one. For list,
you make assignment by reference.
Changing the new variable induce
mutation on the original one.
Next, let's up notch the difficulty by
bringing function call into play. But
don't worry. I promise you that nothing
had fundamentally changed from just now.
So, what I have here is that I have a
function f of x.
The function increased the value of its
input by one,
and doesn't have return statement.
And then, I create a variable with k
equal to 10.
I call the function f on k.
So, if I print k, am I going to see k
becoming 11?
But first, let me ask you, what's your
mental model?
When I pass the variable k as input to
function f,
are you imagining something like this?
The value of k, which is integer 10,
enters the function.
Or is your mental model the variable k
itself entering the function?
The quick answer, not surprisingly,
is the former.
Just like a variable assignment, for
string and number, it's the value that
gets moved. Let's execute the code to
confirm.
Since the input is really the value of
10 entering the function, but not the
variable k, if I increase 10 to 11, that
has no business to do with the original
k.
After the function call, if I print k, I
still get 10.
What about if a variable is a list, and
we're passing it to a function? Which
one would it be?
Are you passing the reference to the
list to the function?
Or are you making a copy of the list?
You guessed it. We're passing the
reference because the variable is a
list. Let's see what happens with the
code here.
The function's input has the same
reference to the list V in the global
frame.
The function modify element zero to be
99.
And then it append a new element.
After the function call terminates, the
original list V is no longer the same.
At this point, the elephant in the room
is why.
Why do we have this complex rule and the
intricate difference between value and
reference?
Why can't we always just make a copy all
the time? So, we never need to worry
about reference.
And that's a wonderful question.
I'll first try to explain with analogy.
And then a more involved second
explanation from a computer hardware
perspective.
Suppose you read an interesting article
on your phone. And you want to share
with your friend. You have two options.
One, you type every single word verbatim
as a text message.
Or two. You can just share the link to
the article.
Well, preferably we would do the latter.
We don't want to spend time typing down
the text. Because the article is really
long. We just want to swiftly send the
link. And we know that our friend just
need to click open the link. The link is
just like a reference in Python. A
reference is lightweight. And passing
them is easy and fast.
Most Python lists in real programming
setting, they're very, very long.
Containing millions and billions of
elements.
We can't afford having computer making
copies of so many elements. Because
making copies data actually take time.
Now, let's dive into the second
explanation from a computer architecture
perspective.
Why does Python and many other language
use a reference?
Inside your computer, we have two key
players,
the CPU
and the memory.
The dance between them is what make your
code actually run.
Down here, we have the heap memory. This
is where large data lives, like Python
list.
For example, a list that contains the
entire book of Harry Potter sits right
here.
And near the top, we have the stack
memory, where function do their magic.
When a function runs, it needs
information about its input within the
stack memory. Now, we could copy the
entire list from the heap into the
stack. But look at this. It's slow, it
take extra space, and worst of all,
we're duplicating data we don't even
need to change.
Instead, we could do something smarter,
which is put a reference in the stack, a
tiny pointer, like a sticky note saying,
"Hey, look down there."
The CPU then follows the reference to
the heap, grabs the actual data, and get
to work.
Cool. Hopefully, you're starting to see
why references matter so much in
programming.
So for now, here's a simple rule you can
hold on to.
Lightweight data types, like numbers and
string, are passing by value.
But heavyweight types, like list,
dictionary, or class objects, are passed
by reference.
It's not a hard rule in every language,
but in Python, this general intuition
will take you pretty far.
Python dictionary, perhaps one of the
most useful data structure, in my
opinion.
Previously in chapter four, we learned
Python list, which can store a
collection of many arbitrary data. List
is also versatile for its operation. We
can easily access,
modify, add,
remove its element.
And most importantly, this is an
iterable, which allows us to run a for
loop. If Python list is so awesome, why
do we need dictionary?
A dictionary is all about relationship
between piece of data. And it turns out
we really care about those
relationships. Think about it. If you're
a football analyst, you don't just care
about how many goals Messi or Ronaldo
score in total. You care about how many
goals they score in a specific year. For
example, in 2015, how many goals Messi
score?
If you're a software engineer at Uber
Eats, you care about the name of a dish
and its corresponding price. If someone
order a pizza, how much does it cost?
If you manage inventory at Costco, you
probably want a data structure that
track the name of item and the quantity
left. How many dried mango we have?
Should we restock?
In all these cases, you're mapping one
piece of information to another. You're
saying, given this, tell me that.
This is exactly what Python dictionary
do. You provide what's called a key to a
dictionary. The dictionary finds the key
and it gives you back the corresponding
value.
And the beauty is that the key is
meaningful. The key is readable.
Compare that to a list, where you're
stuck with using the index to access the
element. The index has to be a number.
It does its job, but not as expressive.
Potato is now a software engineer at
McDonald. Today he's working on digital
menu. Let's help him out.
Potato wants to use the Python
dictionary to store the meals and its
price. So, he can easily look up the
price for particular meal. In this case,
the key is the name of the meal, the
value is the price.
To create a dictionary, we use the curly
bracket on our keyboard, not the square
bracket, because square bracket is
already used for Python list. Dictionary
fundamentally just store a collection of
many key-value pair. Let's first see how
we can create one key-value pair.
We write
the key,
colon,
and then its corresponding value, and
finish it off with a comma.
Now, we can use Big Mac to access its
corresponding price.
Next, we just need to apply the same
syntax for other pair.
Execute.
Here is our dictionary. I want to make a
remark that you can also just create an
entire dictionary on the same line like
this. Either way works. Your pick.
Next, we're going to learn about the
four primary operation for dictionary,
which are still access, add, modify,
remove.
A customer wants to buy a Big Mac, but
how much does a Big Mac cost? The syntax
for accessing the value is as follows.
We put the name of the dictionary, menu,
and square bracket.
Within the square bracket, the string
Big Mac.
Doing this, the variable P will bind to
the value of 5.99.
The key has to match precisely. One of
the most common bug is people
misspelling the key.
One interesting thing you might notice,
when we access the value, we're using
the square bracket, despite a dictionary
was created with a curly bracket. When I
learned Python, I also find this
unintuitive, but this is just how
Python's creator, Guido van Rossum, made
dictionary. We will just have to follow
his rule.
Once in a while, seasonal special item
ushers in. The Shamrock is a minty
flavor drink only offered around St.
Patrick's Day. Let's add it to our
dictionary menu. The syntax is on the
left-hand side of equal sign, we have
the dictionary name, menu, square
bracket, the key, Shamrock,
equal to its new value, 3.89.
That's it.
Here it is.
Next up, we have modification of
existing key value pair.
Due to inflation, McDonald has decided
to change the price of its popular item
Big Mac to $10 and double the price of
french fries. On line number 11, we
write menu square bracket Big Mac equal
to 10. This update Big Mac to $10.
But for french fries, we're doubling its
price, so it depends on the current
price.
Menu of french fries is equal to the
current menu of french fries times two.
You see, the syntax for modification is
identical to addition. If you give the
dictionary an existing key, it modify
its value within.
If you give the dictionary a new key,
then it adds the key value pair.
As Saint Patrick's Day pass by, seasonal
offering stops. It's time to remove
Shamrock from the dictionary, oh, I mean
menu. The syntax is menu.pop
parentheses the key you want to remove,
which is the string Shamrock in our
case.
Now, the Shamrock key value pair vanish
from the menu.
Dictionary is versatile.
I can create a dictionary, the key is
number and the value is string.
Vice versa, the key can be string and
the value is number.
I can also create a dictionary, the key
is string and the value is a Python
list.
The value of dictionary can even be
another dictionary.
This dictionary maps the name of
restaurant to its menu, which in turn is
another dictionary.
Let me show you how I can access a value
from something like this.
I do restaurant to menu with a key
Chick-fil-A
and its value is this inner dictionary
here.
Immediately after, I do another value
access with a key of waffle fries to
retrieve its price here. Variable P will
bind to 2.49. You almost have
unconstrained freedom with the creation
of dictionary. Yet, there's one
restriction. The keys of a dictionary
cannot be a Python list or dictionary.
If I try to create a dictionary with a
list as key, I would see this following
error message. The precise explanation
is outside the scope of essence of
Python, but you can sort of guess
roughly why we can do this. Dictionary
is just a collection of key-value pairs.
If my key is a Python list, and as we
know, the contents within a list can
change easily. So, our key can change.
If the key changes, it no longer unlocks
its corresponding value.
But, the reason we love Python lists is
because it's an iterable, which allows
us to run a for loop. At this point, we
know that loop is basically a superpower
in programming. Dictionary, luckily, is
also an iterable. However, doing a for
loop with dictionary is nuanced because
dictionary has both keys and values. We
need to be careful and precise about
what we're doing.
The keys method returns an iterable of
all the keys in the dictionary.
This lets us to loop through just the
keys and do something with each one,
like printing them out.
Similarly, the value method returns all
the values in the dictionary, which we
can loop through in the same way.
You might ask, "What about loop over
both the key and value simultaneously?"
And yes, that's possible. You'll need
the items method and use two separate
loop variables. The first one, K, is
key. The second one, V, is value. Here's
what the loops looks like.
>> Cool. Let's put it to use.
Another customer, Patrick, enters, asks
if we have something called Krabby Patty
in the menu. We write string Krabby
Patty in menu false, so we don't have
it.
Here, I'm also showing you interesting
property of Python dictionary. If you
don't specify dot keys or dot values,
Python would automatically assume you're
using dot keys.
Patrick then asks, "What do you have?"
So, we iterate through all the keys and
print each one of them. By the way, you
know the dot keys here can be optional.
Patrick says he only has $5, what can he
buy? We use the dot items method and
iterate through the keys and values
simultaneously. Within the loop, only if
the value is less than five, we print
the key-value pair.
Execute. Nice. Everything affordable for
Patrick here.
For our final coding example today,
let's try to combine various technique
and data structure we learned from
previous video together to truly show
the charisma of Python dictionary.
So, you probably heard about the novel
and movie series called Harry Potter.
We're going to use Python to count how
many times a certain name appear in the
first novel. You might ask, "What's the
point of doing this?" Well, I can argue
that the importance of a character is
proportional to how many times his or
her name appear in the novel.
Intuitively, we know that the name Harry
will appear the most, but exactly how
many times? And what about Ron,
Hermione, Malfoy? Who is the second most
important character? Let's find out with
Python dictionary.
I have a Python list called book, which
contains every single word of the first
novel.
It has 78,000 words.
My coding strategy is that I will
initialize a dictionary with a
character's name as a key and zero for
all their value.
The dictionary will store how many times
I've seen a character in the novel.
I will iterate through every single word
in the book. If the word match any
character's name, then I'll increment
that name's value in the dictionary by
one.
Let's execute the code.
And I print dictionary.
Harry appear 1,200 times and then we
have Ron, Hermione, and last Malfoy.
This kind of roughly aligns with my
expectation.
And also, just notice how fast Python
is. The for loop completed the entire
book almost instantaneously.
If you felt this code here is a bit
complex, I understand. Here is its
equivalent visualization.
This loop diligently examine every
single word, increment the dictionary if
necessary. For example, now the loop
variable binds to chapter.
Chapter is not in the dictionary, so the
if statement doesn't get triggered.
The word then binds to one, which is
also not in the dictionary and the if
statement doesn't get triggered.
Honestly, this loop is going to run for
a while and don't find anything
meaningful
until much later.
The word binds to Harry. Harry is indeed
in the dictionary, so I increment
Harry's value by one.
The loop runs, runs, runs, runs.
And it finds Harry again.
Harry's value increments by one.
So, you can imagine when this loop gets
to the very last word of chapter one,
the loop would have counted everyone's
name correctly into the dictionary.
Also, remember everything you saw just
now happens in less than a second.
That's just how fast Python is.
>> You embark on a journey seeking for the
truth and the wisdom of programming. You
learn various technique and data
structure. Now you can write code which
computes the factorial of any integer
using a for loop iterating over positive
integer from 1 to n.
The loop variable accumulates the
product along the way.
When the loop completes, you get your
factorial.
This is no small achievement, but you
keep going learning from friends and
sensei because you believe there's
another gem out there waiting for you.
However, the journey is harsh. For a
long time you believe some kind of loop
is the only way you can compute
factorial and there's nothing more out
there.
Until one day, you climb a mountain and
met with an old wise monk. You told him
about your mission seeking for the
elegance of programming.
He smiled and asked you,
>> Can you tell me what's the factorial of
five?
>> Oh, that's simple. It is 5 * 4 * 3 * 2 *
1.
>> How about factorial of six then?
>> That's just 6 * 5 4 3 2 1.
>> You notice something interesting.
What is this part?
>> That's just 5 * 4 3 2 1. Oh wait.
This is the factorial of five.
So, the factorial of six is the integer
six times the factorial of five.
>> Very well. Can we generalize for all
number?
>> Should be the factorial of n is the
integer n times the factorial of n minus
1.
>> Good. Take out your keyboard. Define
function factorial. What is the
factorial of one?
>> It's one. No question asked.
>> Else?
>> We said that it's the number n times the
factorial of n minus 1.
Wait, wait, wait. I'm still defining the
function. How can I be using the
function?
>> Don't worry. Give it a try. It's about
the recursive leap of faith.
>> Well, let me try.
The factorial 5 is indeed 120. This
function actually work. Wow.
If today is your first time seeing a
recursive function, you should be amazed
and confused.
So, let's slowly take a look at what
happened. We're calling the function
fact, short for factorial, on five. It
checked the if statement and is not one.
So, we entered the else case.
But now, it's n multiply with another
function call of itself. It's calling
fact of n minus one, which is factorial
4.
So, it creates a new function frame with
n of the exact same definition, but this
time, the input n is 4.
It failed the if statement and goes to
the else case.
It's calling n times fact of 3.
So, it opens another function frame with
input of 3.
Similar logic happens here. Fact 3 is
going to call n times fact 2.
And factor 2 is calling factor 1.
Okay. Here, the if statement finally
pass. We hit the base case. The function
frame returns 1. But what do we do with
this value 1 here? It's getting sent
back to its parent function frame here.
Now, the parent function frame, fact 2,
can fully evaluate the return statement,
n times 1, which is 2 times 1, and the
value 2 is sent back to its respective
parent frame,
which it will evaluate 3 times 2, 6,
and 6 is sent back to its parent frame,
which it would evaluate 4 times 6, 24.
Finally, we go back to the original
function frame, which returns 5 times
24, 120. That was a lot to digest. But
if we were to reflect on what just
happened, it's kind of like we were
peeling an onion layer by layer until we
arrive at a base case.
And then, its return value gradually
pass all the way back up.
The factorial function is the poster
child of learning recursion because it
demonstrates three important principles.
One, the function intends to call itself
during definition. This fears that the
function is trying to solve a problem
using itself. This might also be the
most unsettling part to look at because
you might wonder, wouldn't this just
lead to an infinite chain of a self
calling self calling self?
Two, the function has a base case, which
is how does it handle the smallest
problem size? For example, the factorial
of one is just one. No one's going to
question that. The base case is how the
function prevents an infinite recursive
call.
Three, the function has recursive case,
which helps to gradually reduce the
problem size. This is the most critical
part of recursive function. Notice,
while calling the function itself, the
argument is no longer factorial of n,
but factorial of n minus one.
Without doing this, your recursive
function would never get to the base
case.
These are the three characteristic you
need to remember by heart when designing
your own recursive function.
So, let me show you another example of
recursive function and see how the three
principles still apply. The goal here is
to design a function which can reverse a
string. For example, ABCD turns into
DCBA.
Generally, when solving a recursive
problem, it takes quite a bit of poking
around with experimental trial and
error.
Let's say I have ABCD here and I want to
reverse it. Here's an observation I
have. When a string is reversed, its
original first character will become the
last character.
So, I can try to break the string as the
first character A and then everything
else after it.
If someone else can magically reverse
BCD,
then I just need to stick the character
A at the very end and the string's
reversed. Simple.
But how can I reverse BCD?
Well, I can use the same process. I can
break into B and CD, the first character
and then everything afterwards.
If someone else can reverse CD into DC,
then I just need to put the B at the
very end.
Well, you notice I'm using the exact
same problem-solving methodology, but
just on a gradually smaller problem
size. I am reducing a bigger problem
into a smaller problem and using the
very same algorithm to solve the smaller
problem. Recursion is screaming to be
used.
I define the function reverse. First,
the base case. If we just have one
character, then nothing needs to be
reversed. It is just itself.
Else, if the string is longer, all we
need to do is to take the first
character and put it at the very end.
And I'll call the reverse function on
the substring from index one.
When I'm calling the reverse function,
I'm implicitly trusting it to return the
correct results to me.
I am trusting it to correctly reverse a
substring so I can combine it with the
first character later on.
I am confidently trusting this function
call even though it seems untrustworthy
because I'm still in the process of
defining the function.
And this unceremonious trust is the
essence of the recursive leap of faith.
It's a secret covenant between the
programmer and the recursive function.
If a visualization helps you to solidify
the understanding, here's how the
reverse function looks like.
>> Today's video is an informal tourist
guide to the concept of recursion in
programming.
Recursion is often one of those topics
with an infinite rabbit hole and very
high skill ceiling. This video alone
cannot make you a recursion master. I'm
here to spark your interest by
programming and share something I
personally find very elegant when I was
learning Python in college.
Let's end the video on a quote.
Beauty is everywhere. It is not that
she's lacking to our eye, but our eyes
which fail to perceive her. But really,
I can change the word beauty into
recursion because recursion is
everywhere in our life and in computer
science. It's our programmer's duty to
perceive them.
My plan for the future is that hopefully
one day I can make educational video
full-time. The potential candidate for
my next courses are operating system,
computer architecture, large language
model full stack.
Nowadays, YouTube is populated with
contents of high production value. Yet,
you chose to sit through 14 episodes of
Essence of Python to learn about coding.
You don't know who I am, and I don't
know who you are. Much like two
strangers going on journey together.
This reminds me of my favorite anime,
Samurai Champloo, which tells such a
story. The three main character, Jin,
Mugen, Fu, have never met, but they went
on a journey from Edo to Nagasaki
seeking for samurai with a scent of
sunflower. And this anime has quite a
beautiful ending song, Shiki no Uta.
I'd share that with you,
and let's end on that.
>> [music]
[music]
[music]
[music]
Ask follow-up questions or revisit key timestamps.
This video provides a comprehensive introduction to core programming concepts using Python. It covers variables and assignment, the importance of Boolean logic and conditional statements, list data structures, various looping techniques including for loops and nested loops, functions and their scope, the nuances of value vs. reference, dictionary data structures, and finally, recursion. The instruction emphasizes 'programming thinking' as a fundamental skill for leveraging AI and creating impactful projects, moving beyond simple 'vibe coding'.
Videos recently processed by our community