[MUSIC PLAYING]
DAVID MALAN: There's any number of languages in which we can communicate,
English being just one of them.
But computers, of course, only understand binary zeros and ones.
And so somehow we have to communicate our thoughts-- ultimately in binary--
in order to solve some problem with a computer.
But certainly, we don't want to program computers by writing zeros and ones
and memorizing the patterns that they'll understand,
so we do somehow need to adopt a process by which
we can express our thoughts and the solutions to the problems
that we have in mind but in such a way that the computer can understand them.
Now it turns out it's quite meaningful when
you see something like Intel inside or AMD
or any number of other computer manufacturers
that make what are called CPUs, Central Processing Units, which
you can think of as the brains of sorts inside of your computer.
Well, turns out that Intel and AMD and other companies
have decided in advance what patterns of bits--
zeros and ones-- that their CPUs understand.
A certain pattern of zeros and ones might represent addition.
Another pattern of zeros and ones might represent subtraction or multiplication
or division or the act of moving information around in memory
or saving information from memory.
And so those patterns are very much computer or CPU specific.
And frankly, I'd like to be able to write software and write
code that can run on your computer and my computer
and some other manufacturer's computer without really having
to know all about those zeros and ones.
So there too it would be nice if there's a process, a workflow, a tool
chain via which we can communicate our thoughts in a fairly accessible way
but have them ultimately translated into those zeros and ones.
So what is it that Intel inside really means?
What is it that a CPU actually understands?
Well, it's what's called machine code, zeros and ones that ultimately dictate
what the computer should do-- add, subtract, multiply, or something else
altogether.
That machine code literally might look something like this.
In fact, let me give you just a moment and from all of these zeros and ones,
can you glean perhaps what this very program would do if run on a computer?
No?
Well, odds are you couldn't imagine what this would do because even
I can't read these zeros and ones.
But if it turns out you fed these very zeros and ones to a computer,
it would print out on the screen quite simply
"hello world," which is sort of the canonical phrase, one
of the first phrases ever printed on a computer screen back in the day
when computer languages were first being invented.
Now you, of course, would never know that, should never know that,
nor should even the most sophisticated of programmers
because this is just too low of a level to communicate one's thoughts in.
Far more compelling would be to operate at a level closer to English
or whatever your spoken language might be.
And so quickly, when humans invented computers decades ago,
did we decide we need something different from machine code.
We need something at a higher level of abstraction,
if you will, something that's more familiar to us
but that's close enough that the computer can somehow figure out
what to do.
And so thus was born assembly code.
Assembly code is an example more generally
of what's called source code, which is typically
English-like syntax more familiar to us humans that can somehow be translated
eventually down to machine code.
And assembly code, which is one of the earliest
incarnations of this general idea, looked a little something like this.
Now it too looks pretty cryptic, though hopefully
not quite as cryptic as just seemingly random patterns of zeros and ones
because there is some organization to this text here.
It's not quite English-like I would say, but there's
some familiar sequences of characters that I can perhaps
ascribe some meaning to.
I see words that look a little familiar-- push queue or at least push,
move queue, or perhaps move.
Sub-- maybe that means subtract or call, as in to call a function
or a procedure, xor and add and pop and others--
these seem to be reminiscent of English words.
And so while cryptic--
and I would surely need a manual in order to figure out what these mean--
this is a standardization of how you might communicate instructions
to a computer.
Indeed, all of those keywords push queue, move queue, sub queue and so
forth are literally called instructions, and those
are the names given to the instructions, the commands that Intel and AMD have
decided that their brains, their CPUs shall understand.
To the right of these instructions is some cryptic looking syntax now--
dollar signs and percent signs, commas, and others.
Well, those are used to communicate what are called registers.
It turns out that the smallest unit of useful memory
typically inside of a computer--
in particular inside of a CPU--
is what's called a register.
A register might be eight bits back in the day, 32 bits
more moderately, or even 64 bits.
And that is really the smallest piece of information
that you can do some operation on, the smallest unit of information
that you can add to something else or subtract from something else and so
forth.
So when a CPU is doing its arithmetic-- addition, subtraction, multiplication,
division, and so forth--
it's operating on pretty small values.
They might be big numbers, but they only take up maybe 32 or 64 bits.
And those registers, those chunks of memory have names.
The names to be fair are cryptic, but they're expressed
in the same language, assembly code.
So that you're telling the computer in this language what should
you move to where and what should you add to what.
And so once you acquire a taste, if you will, for this language,
does all of this begin to make more sense.
And frankly, if I really scour it, aha, down here at the bottom,
I do see explicit mention of that phrase hello world.
And these other lines simply call in to action the printing of that phrase
on the screen.
But frankly, this doesn't look all that compelling still.
It's certainly better than zeros and ones,
but assembly code is generally considered
to be fairly low level, not as low level as zeros and ones and not as low
level as electricity from the wall.
But it's still low level enough that it's not really
that pleasant to program in.
Now back in the day, decades ago, this was all you had at your disposal.
And so surely, this was better than nothing else.
And in fact, some of the earliest games and some of the earliest software
were written in assembly language.
So it truly was experts back in the day writing frequently
in this low language, and you might still use it today
for the smallest of details.
But on top of assembly language have evolved more modern forms
of source code-- newer languages with easier to understand syntax
and more and more features.
And one of the first successors to something like assembly code
was a language called C, quite simply.
C looks like this.
Now I dare say this too remains fairly cryptic,
but I feel like we're walking up a set of stairs
here where things are finally starting to look a little more
familiar and a little more comfortable, even though there might still
be some distractions of syntax.
These angled braces and these curly braces
and quotes and parentheses and a semicolon--
all of that you might get to in an actual course on programming itself.
But here we use this as demonstrative of a fairly more English-like syntax
with which you can express the same program.
Printing hello world in a language called
C can be implemented with precisely this code.
But frankly, we're starting to stray pretty far from that low level language
that computers ultimately understand and need to accept as their input binary.
So how do we get from this higher level language, so to speak,
called C down to those zeros and ones?
Well, frankly, the process by which this happens
tends to make an intermediate stop in what's called that assembly language.
So a human might write code like this, like I did here
in C. You might then use a program that converts the C code to assembly code
and then another program that converts that assembly code down
to those zeros and ones.
And frankly, you could probably use a tool
that does both of those steps at once so that it
creates the illusion of going directly from this to so-called machine code.
Those zeros and ones.
So let's take a moment and actually do that on my computer here.
This is a process that you can do on a Mac or PC
running Mac OS, Windows, Linux, or any number of operating systems.
I happen to be doing it on a Mac here.
And I'm going to use a fairly common program these days called a text
editor.
This is a very lightweight version of a word processor.
It doesn't have bold facing and underline and italics.
It really just allows you to type text, but it
does tend to colorize it for you to draw your attention
to the disparate parts of a program.
And I'm also going to open up what's called a terminal window.
A terminal window is a keyboard-only interface to what your computer can do.
So while I still have access to my mouse,
it's not going to be all that useful for this environment
because anytime I want to run a program or execute a command or make my Mac
do something, I'm going to have to do it from my keyboard alone.
Let's take a look.
Now here I am in front of my text editor,
and I'm going to go ahead and create a file called sayhello.c,
dot c being a conventional file name to indicate to the computer
that the code I am about to write is going
to be implemented in that language called C. Now
at the top of this program is where I'm going to write my code,
and I'm simply going to transcribe what we just saw.
Include standard IO dot h int main void open curly brace
followed by a closing curly brace ultimately
and then printf quote unquote hello comma world backslash n, finally,
a semicolon.
So herein I've written my source code.
And indeed, it's been colorized by the text editor
simply to draw my attention to disparate parts of this program
and were we to dive deeper into C, in particular,
we'd see what each of these different symbols mean.
But for now, let me propose that we only care
about what this program is meant to do, which
is to print ultimately hello world.
But all I've done is write a program in C.
I somehow have to get it to its form of zeros and ones
that the computer ultimately understands.
So it's not sufficient just to save the file because, indeed,
all that's been saved are these letters and symbols in some file called
hello.c.
I somehow have to convert that file to zeros and ones.
Well, it turns out that there exists tools called compilers.
A compiler is simply a piece of software--
written presumably by someone else--
that knows how to understand C, perhaps knows about assembly code,
but definitely knows about those patterns and ones
that Intel and AMD ultimately require that I output in order
to get them to execute commands.
So how do I go about compiling hello.c?
Well, it turns out installed it on my computer--
and perhaps even yours-- is a program called CC, the C compiler, if you
will-- compile simply referring to this process of translating
one language to another.
And so I'm going to go down here to the lower portion of my screen
wherein I have a prompt, dollar sign that for whatever
historical reason simply represents a prompt in a terminal window.
And it's in here that I can type these textural commands,
cc -o hello space hello.c--
a fairly cryptic incantation of commands.
But ultimately, this has created a new file
on my computer called quite simply hello with no file extension.
Now on a typical Mac or PC, when you want to run a program,
you would typically just double click its icon, and it would be loaded up.
And you would see its ultimate behavior.
But here in this command line interface, so to speak,
wherein I can only type commands textually,
I have to tell the computer to run this program only via my keyboard.
And the convention via which you can do this
is quite simply to say dot slash hello where dot refers to the current folder
or directory in which this file is.
The slash just separates from its name, and hello, of course,
is the name of the program I've written.
And here we go.
With the stroke of enter, I now see hello world.
And thus was born my very first program in C.
Of course, it took me quite a while to get to this point,
and I didn't necessarily even understand all of those lines of code
along the way.
But I did understand that my goal at hand and the problem to be solved
was to print quite simply hello.
But there's a bit of overhead, of course,
to a language like C wherein there's not only the syntactic complexity of it,
but that frankly gets much more familiar over time.
There's also this additional step, this middleman,
a compiler that has to exist and somehow translate your source
code to machine code.
In fact, what we've effectively done just now is this.
If up here is my so-called source code stored in any file,
for instance, hello.c, and I want to convert it ultimately
to so-called machine code, the zeros and ones that my computer understands,
I somehow have to get from input to output.
And the middleman here is again this tool
called compiler in the context of my having written this program call in C.
I used a program, a compiler, called CC, but any number of other options exist.
You might have heard of Visual Studio perhaps or Eclipse or yet others still.
This middleman simply takes as input that source code in C
and produces as its output that machine code that the computer expects.
And so when I type that Command cc -o hello hello.c,
it ultimately was telling my computer take as input hello.c,
produce as output a new file called hello, inside
of which are those zeros and ones.
Now not all languages operate like this.
It turns out that more modern languages skip that step of compilation
altogether or at least hide that detail from the user
so that he or she doesn't necessarily need to know how to compile their code.
It's handled more automatically.
Now some languages instead use not a compiler but an interpreter.
Whereas a compiler takes as input one language like C and produces as output
another language like machine code, an interpreter
instead takes as input some source code and then runs it
or interprets it line by line top to bottom, left to right.
And whenever it sees an instruction like print, it thinks to itself,
oh, I know how to print something on the screen,
and it goes and does it on behalf of that source code.
It does not, strictly speaking, convert those instructions
instead to zeros and one.
It is instead the interpreter itself which
is just a program that itself is implemented with zeros and ones
that the CPU understands.
And those zeros and ones collectively know
how to recognize keywords or functions in that source code language
it takes as input in order to execute it on the program's behalf.
So what is an example of an interpreted language?
Well, among the most popular ones today is that called Python.
Python is especially popular in the world of data science and web
programming and in command line applications
ones written at the so-called terminal window via which I can solve problems.
And so Python is notable too for its relative simplicity-- certainly vis
a vie something like c.
In fact, in order to implement the equivalent program in Python
that I just implemented in C, it suffices to write just this.
Say what you mean and little more.
There's less syntax here.
There's fewer keywords that are unfamiliar.
It's just instead the verb or function print followed by hello world--
no semicolon, no curly braces, fewer symbols altogether.
But how do I go about running a program in Python?
Well, it turns out that typically Python is interpreted, not compiled.
So I'm not going to run it through a compiler per se,
but I am instead going to interpret it line by line-- albeit just one
line with this particular program.
So let me go back into my text editor and terminal window
and this time create a file called hello.py-- dot
py being the conventional file extension for any program written in Python.
And in hello.py, I am now going to write that one line
program print open parenthesis quote unquote hello world.
How do I interpret this file called hello.py?
Well, it turns out I run a program that itself is
called Python, which is my interpreter.
And so I run in my terminal window Python space hello.py,
and the output is now the same.
So what has just happened?
Albeit just one line, what that program called Python has done
is open up this file called hello, read it top to bottom,
left to right, albeit quite quickly, recognized
that it knows this keyword or function called print and therefore knew
what to do next.
It went ahead and printed hello world on the screen and then automatically quit.
So this seems like a nice thing.
No longer do I have to remember and take the time to compile my code,
but surely, there must be some price.
And indeed, one of the implications of saving that step no longer having
to compile your code but instead just jumping right
to its execution or interpretation is that you pay potentially
a performance penalty.
You certainly can't quite see it in a program as short as hello world.
But if you were to write a program with hundreds or thousands
or millions of lines, the overhead required
to read that file top to bottom, left to right,
and to figure out based on the instructions
therein what it is the programmer intended actually
does take non-zero amount of time.
And it can surely add up for the most computationally complex of problems--
anything involving an analysis, anything involving loops or cycles,
you can certainly begin to feel its effects.
But that's OK because we humans have been fairly creative over time.
And as we've invented more and more programming languages,
we have fortunately also invented more and more solutions
to problems like these.
And so it turns out that even though this all happened quite quickly, when
I ran this interpreter called Python, odds are if my computer were smart,
it was probably doing me a favor underneath the hood
without my even knowing.
And in fact, what Python and some other interpreters
do is actually compile your code for you,
save the results in a temporary file that you yourself might not even see,
and the next time I run this program, especially if it's large and complex,
Python will skip this step of reinterpreting the file again and again
and instead look at that precompiled version of my same program--
therefore, saving some time but achieving the same results.
Only if I go back and change my code and make changes to my program
does Python need to regenerate that cached version of code, if you will,
in order to reuse that again and again.
And this intermediately cached code is generally called byte code.
It's not quite zeros and ones, but it's closer to it than Python itself.
And so for this same program in Python, were my computer
to actually compile it for me, what I would actually see is code like this.
Much like assembly code is it fairly cryptic, but at least
in there is some familiar phrase hello world as well
as the function I ultimately called, which is that known as print.
Now Python and C are not the only languages out there.
In fact, there are dozens in vogue these days.
And there are hundreds-- if not thousands--
that humans have created over time.
For instance, depicted here is perhaps one with which you
yourself are familiar or at least heard of.
It's called Java, and it happens to be an object oriented programming
language in a language, which means it has features
beyond those earliest of ones like C. Here, though,
is perhaps the simplest way via which you can implement that same program
hello world, but it-- not unlike C-- has a bit
of overhead, a number of samples and words that at first glance
certainly are not obvious.
But ultimately, that's all this program does.
But Java is distinct in that it took a different approach to another problem
that we've not yet tripped over.
I've been running and running this program thus far in my Mac,
and I compiled it particularly for this Mac on an Intel CPU.
But it certainly stands to reason that you or someone else
might not have the same computer or operating system as I,
and it would seem to be quite the burden on the programmer
if they have to compile their code in a different way for you and for me
and for everyone else.
And so it turns out that this cost of doing business, if you will,
shipping different shrink wrapped boxes back in the day of the same program
for different computers and OSes is ultimately
solved by way of a virtual machine.
A virtual machine, as the name implies, is not
a physical machine but a virtual one implemented, as they say in software--
software that humans have written that mimics the behavior
of a virtual imaginary machine.
And then companies like Sun and others have implemented support
for that virtual machine for Macs and for PCs
and for multiple operating systems.
And so Java subscribes to the monitor of write once run anywhere.
You needn't compile it again and again for different platforms,
rather you can install on each platform its own virtual machine
and run the exact same code.
So it's simply a different approach to an otherwise omnipresent problem,
but Java falls into a class of languages that uses that particular technique.
And what other languages are out there?
Well, very popular these days on both servers and clients is a language
called JavaScript, such as that pictured here.
Here is yet another language-- this one called
Ruby-- that replaces the word print with just put,
but it too is more syntactically simpler, much like Python itself is.
On the other hand, here is C++, incredibly common still,
especially on PCs, along with other languages as well.
But in C++, you see code reminiscent of C itself, also conventionally compiled.
Now if you'd like to see any number of overwhelming examples of how you might
quite simply in hundreds of languages say hello world,
take a look at this URL here.
And in fact, there's so many other languages
that are popular and powerful, sometimes in different ways.
In fact, it's not just the case that you use one particular language
for one particular job.
There are many tools that you might bring to bear
on the exact same problem at hand.
In fact, the reason that so many languages exist
is that over time, we humans have perhaps rightly or arrogantly
decided that we can do better than languages past.
And so humans invent new languages that have other features,
different approaches, and of course, reasonable people can disagree.
And so you have some languages that can achieve the very same task.
They just do it differently.
The text looks a little different.
The features are a little bit different.
And so it's up to the programmer as part of the design process
to decide what tool is best for the job, not unlike a physical tool
that you might have in a home tool chest.
Among the most popular these days perhaps are these here Bash and C
and C++ and C#, Closure and Erlang and F# and Go, Haskell, Java, JavaScript,
Objective OCaml and then PHP, Python, R, Ruby, Scala, Scheme, SQL, Swift,
and so many more.
In fact, if you'd like to see a nearly exhaustive list of all
of the language humans have invented, take a look at Wikipedia
here, which goes into so much more detail.
So suffice it to say that computers can print hello world,
but they can do so much more as well.
So what are the basic building blocks that can be found in languages like C,
like Python, Java, C++, and any number of others?
Well, let's return to the pseudocode with which we began to program, albeit
verbally, some time ago.
Here we had the algorithm via which to find Mike Smith in a phone book
and to search more generally.
Recall, though, that laced throughout this pseudocode or program
really were a few constructs that were semantically distinct from each other.
Let me go ahead and highlight in yellow some of the verbs that we saw last.
Pickup and open to and look at call, open and open and quit--
all of these were calls to actions, verbs or functions,
as we described them previously.
Well, it turns out that in C and in Python and other languages as well,
we have not necessarily these same words but these same actions.
For instance, we've seen in two languages
already that you can print hello world on the screen.
That function or verb in C happens to be called printf.
In Python, it happens to be called more specifically print.
And so those are examples of functions in those languages as well.
What else did we see last time?
Well, we had if and else if and else if and else,
and we describe these as conditions or branches
via which you can make decisions and either go left or right down
the fork in the road.
Well, in Python in C and in other languages
too do we have those same features as well.
We then looked at Boolean expressions, questions
that you can ask to which there are yes and no or true and false answers.
In Python and C, we're going to see those as well.
And lastly, we saw loops-- go back to step two, go back to step two--
a programming construct via which you can do something again and again.
Now here we did this in order to keep looking for Mike.
But in the real world, might you be searching for data in a large database
or an Excel file, a CSV file, Comma Separated Values.
And so you might want to have some programming code that
opens that file and then iterate or loops over one line
after another doing some calculation or analysis,
loops enable us to do exactly that.
So let's go ahead now and see if we can see
these constructs in a common modern programming language.
We'll use Python if only because it's incredibly commonly used at the command
line within a terminal window.
It's incredibly commonly used nowadays for web programming.
And it's ultimately incredibly useful in the context of data analysis and data
science, more generally.
So let's begin where we left off recreating a program in Python that
quite simply says hello world.
To do that in my text editor here, I've created
a file now called hello0.py to suggest that we're going to do this
a few times, starting at zero, in order to iteratively improve on this program.
Well, herein I'm going to do print quote unquote hello world, saving my file.
No need to compile it but I'm now going to go ahead
and type Python space hello0.py.
And there we have hello world.
Now this program, of course, is by definition not terribly dynamic.
No matter how many times I run this program,
it's going to print hello world, hello world again and again.
Suppose that I instead wanted to get user inputs.
Well, it turns out that Python--
like other languages-- has a function via which you can do exactly that.
And a function, of course, takes optionally inputs
and optionally produces some output.
Now in Python, perhaps the easiest function with which to get user's input
is called quite simply input.
So let's see how we might use that.
Let me go ahead and create a new file called hello1.py.
And in this file am I first going to call that function called input,
and input is called exactly that.
But it takes as input some input, which is to say the string or the phrase,
the sentence via which you want to prompt the human for his or her input.
So for instance, I'll go ahead here and ask
what is your name question mark space.
And now this function is going to return to me a value, so to speak.
I don't know how input is implemented.
Someone else yesteryear implement this for me.
But I do know per its documentation that when I call it,
it's going to do the equivalent of handing me back a slip of paper
on which is written the user's input from his or her keyboard.
But for me to make use of this input, I need to store it somewhere.
And in a programming language, much like in math,
you have access to what are called variables.
Now a mathematician might call their variables x or y or z.
And in programming could we also call our variables
the same but more conventional is ways to use
names that are a little more descriptive-- actual words that
describe their contents and the means by which I can store the return value,
so to speak.
What is handed back from a function like input is in a variable
that I'll call perhaps aptly name.
And in order to store the return value of input into a variable called name,
it suffices simply to use a single equal sign.
And this equal sign is not to be confused with the equal sign
that you and I know in math.
After all, in math, using an equal sign would
apply that what is on the left equals what is on the right.
And ultimately, that's our goal.
But initially, we only know the value on the right--
the so-called return value from input that's handed back to me, so to speak,
from the function.
So in fact, with the equal sign in many programming languages is called--
Python among them-- is the so-called assignment operator.
And so you consider what's happening here as being this.
On the right-hand side is a function.
It's handing back some value, the user's name.
The equal sign implies copy that value from right
to left into whatever is on the left.
And so if on the left I have a variable called name,
that variable-- much like x or y or z-- is
going to store the user's input ultimately,
so I can use it subsequently in some other line of code.
Now what might that subsequent line of code be?
Well, suppose I'd like to print not just hello world but hello, David,
or hello, someone else.
Well, now I can simply structure the second line is taking as input
the contents of that variable.
So I might go ahead here and say print quote unquote hello comma,
and I now somehow need to append to that shorter phrase
the name that has actually been typed in.
And there's a number of ways we can do this.
For instance, we might do it as follows.
It turns out in Python that you can use plus not
only to add two numbers together but also, in some sense,
to add two words or two strings as they're called in a language.
And if I do plus in between hello and that variable name,
the effect is going to be to join or to contaminate those two strings of text
together so that would ultimately is printed
is hopefully hello comma David or hello comma yourself.
So let me go ahead and save this file and in my terminal window now run
Python of hello.py, I'll input my name David.
And now notice my input is separated by one space from that question mark
because on line one did I preemptively include
that space between those double quotes.
Now I'm going to go ahead and hit enter, the effect
of which is to have input return that value, storing it
in the variable called name, and then in line two, just go ahead
and print hello comma followed by name.
Now just to be clear that this is not hardcoded somewhere else.
Let me go ahead and run it one more time.
Python hello1.py.
And let's go ahead and type your name and there too
do we see hello your name.
So the program is now dynamic, and I've outputted dynamically
whatever it is the human is typed in.
But it turns out there's any number of other ways we can do this.
And so just so that we've seen a few different ways,
let me go ahead and create a new file called hello2.py
wherein we have almost the same code, but we're displaying your name
or mine a little bit differently.
As before, I'll go ahead and define my variable called name, assign to it
the return value of input asking the user for his or her name.
And then on my second line of code, we'll again go ahead
and say print hello comma.
And then following that, I actually have a choice.
I don't necessarily have to just concatenate
one input onto that first string hello.
I can actually combine these as two separate inputs to print.
It turns out that print, like many functions,
can take either zero or one or two or any number of inputs.
You simply, as the programmer, need to separate them
by commas, not the comma that's inside of the quotes.
That's just my English grammar separating hello from your name
but outside of the quotes.
And so my text editor is displaying it a little bit disparately in white.
So in this case, am I using the same function
print passing in not one but two arguments or inputs, the second
of which is that actual name.
Now there's a minor bug here that I'm not yet anticipating,
but let's see what happens next.
If I go ahead now and run Python hello2.py, type in my name, voila
it's so close and almost right.
But here perhaps is my first bug.
It's not necessarily a bug that's breaking the program altogether.
But to be a little bit nit-picky, I think we could do a little bit better,
say, grammatically there seems to be two spaces instead of one
but where are they coming from?
Well, it turns out that functions, when they take inputs,
can also have default behavior.
And in this case, print is designed by its authors
whenever you pass in two or more arguments
to just separate them by default with one space.
Now I can fix this mistake in a couple of ways.
But the simplest way is just to remove it from my own input,
saving the file again and rerunning Python of hello2.py
gives me now what is your name?
David.
And we're back-- hello, David.
Now it turns out there is yet more ways than you can format information
on the screen.
And just so that we've seen one other way,
allow me to create a fourth file called hello4.py.
In hello3.py, am I going to start almost exactly the same declaring a variable
called name, assigning to it the return value of input,
asking the user for their name.
And then in my second line of code am I again going
to use print starting with a parenthesis and a close parenthesis.
In there am I ultimately going to say hello comma something,
and the something this time is literally going to be name but not quite name
on its own.
Indeed, if I were to run this program now,
what do you think might be printed?
I've said to the program print hello comma name, but unfortunately,
name is as written, N-A-M-E. And so I would literally see that on the screen
no matter what I typed in.
But it turns out in Python, if you surround your variable's name with
curly braces, typically found just above the Enter key--
at least on a US keyboard-- you can tell Python that this N-A-M-E is actually
the name of a variable, not just a string of text on its own.
But in order to tell Python that it should treat this string, this input
to print a little bit differently than usual,
you have to fairly cryptically prefix it with a single F,
thereby telling Python that this is what should
be called a format string or string--
That is, a string of text a sequence of characters
that should be treated as formatted in a special way.
And according to Python's own documentation,
if you format your input using these curly braces inside of which
is the name of a variable, Python will go
through the trouble of plugging in the value of that variable
there and therefore formatting it for you.
And so I'll again go ahead and run Python of hello3.pi, input my name,
and voila, we're back to Hello, David.
So what are the takeaways then from the simplest of programs?
Well, we clearly have the ability to print information on the screen.
But we have at our disposal a function, a piece of code
that someone wrote long before me that takes as input perhaps just
a string like hello world.
But if you pass it multiple strings will it handle those as well.
And if you pass it a special type of string will it handle that as well too.
And so depending on the documentation for some language
do you have these and so many more features available to you.
And so one of the first steps in learning any programming language
is not to take a formal lesson or class but simply,
quite honestly to read the documentation.
And once you have under your belt some knowledge of one or two
or few programming languages, it is much easier in the computer world
to pick up new ones than I daresay it is in the human world where you
have a much larger vocabulary at hand.
All right, enough about printing.
What more can we do?
Well, programming languages can typically
handle any number of arithmetic or mathematical operations--
addition, subtraction, and many others among them.
So why don't we go ahead and create a program that quite simply prompts
the user not just for their name or string
but rather two inputs, two numbers.
And we'll call them more familiarly x and y.
I'm going to go ahead and declare a variable called
x assigned to it from right to left the result of asking
for the user's input of x.
And then I'm going to go ahead and do precisely the same thing
on another line of code defining a variable called y
and assigning to it from right to left the return value of another call
or invocation of input--
this time, prompting the user for y.
And then I quite simply am going to go ahead and print out the result.
So print x plus y--
this time not using any double quotes at all
because what I literally want to print is x plus y,
not the concatenation of two strings, not a string at all,
but rather just a number.
In my terminal window now will I go ahead and run Python
on the name of this file, arithmetic.py.
I'm prompted for x, which I'll say one.
I'm prompted for y, so I'll say two.
And indeed, 1 plus 2 is 12, but no 1 plus 2 is not 12.
So what's actually happened here?
Well, this is the first real bug, if you will, that I've introduced to my code.
Now step one here has x equals input, prompting the user for x, and step
two has y equals input prompting the user for y--
little different from before that we've chosen different variable names.
Now on line four--
and I've separated with this blank space just
to make it a bit easier to read do I have print x plus y.
But 1 plus 1 is surely not 12.
But what appears to be happening?
Well, it's no coincidence that the answer Python's
giving me is my first input followed by my second.
It would seem, in fact, that Python is contaminating
my first input to my second that is joining them just
like hello comma David.
So why is that happening?
Well, I would hope that plus when given two numbers
would, in fact, add those two together.
But here's a catch.
It turns out that underneath the hood, Python, like many languages,
actually has what are called types or data types--
that is to say, different ways of representing information
if that information is a number like an integer
or even a real number with a decimal point
or a string of text that is a sequence of characters or maybe more
generally, a value like true or false, a so-called Boolean value, and even
others still.
And in this case, indeed, it turns out-- and you would only know this by trial
and error by having read the documentation first--
does the input function built into Python return
to you not a number, but a string.
Even though what I typed on my keyboard looks like a number
and surely is in practice, it's actually not being stored as such by Python.
It's being stored and said in such a way that the computer is interpreting it
as a string of text.
So we have to be ever more explicit.
And indeed, computers don't necessarily know what I intend.
Maybe the goal at hand was to write a program that concatenates
one number against another.
And so if I really want the user's input to be treated as numbers,
I somehow have to coerce it to such or convert it
or more technically, to cast it from string to an int.
Now it turns out Python has other functions with which I
can fix this mistake, and I might do this in between these lines here.
X should not just be whatever the user typed in.
I want x to be the result of converting whatever
the human typed in into the integer or into version of that string.
Meanwhile, can I fix y by the same y equals int of y,
thereby telling Python even though, yes, the human typed something
in a keyboard, therefore implying a string,
go ahead and convert that one and that two
to an actual integer underneath the hood, a pattern of bits, if you will,
that represents not the ASCII value or the Unicode value
that the user typed in but the underlying pattern of bits
that represents one and represents two.
Let's go ahead now and rerun this file as Python arithmetic py,
inputting again 1, inputting again 2, and lastly, hitting Enter.
This time, we have three.
Unfortunately, we've paid a bit of a price, albeit just a matter of style.
I seem to have increased the length of this program from just three lines
to five just to fix a simple mistake.
But it turns out that just as functions take input and produce outputs,
so can you pipeline them, so to speak, or nest them so that one function's
output is another function's input.
The result of which is that we can kind of tighten this up.
I can instead on line 2 here delete what I have
and on line one alone simply say that once
I get back the return value of input--
the user's input, if you will, return on a conceptual sheet of paper--
go ahead and pass it immediately to that function called
int and surround the whole thing with parentheses,
thereby passing the output of input into the input of int
and then assigning the result from right to left.
And again, here can I get rid of line 3, passing the output of this input
to the input of this int and again surrounding it
with parentheses in order to pass its output into its input,
ultimately assigning from right to left that value.
Let's to be sure go ahead and save this file.
And then in my terminal window, run one final time Python of arithmetic.py,
inputting 1, inputting 2, and still do we get 3.
Suppose, though, that we not only want to take a user's input
but make a decision based on it, taking out for a spin
this notion of a condition with some Boolean expressions.
How might we do that?
Well, let me go ahead and create here a file called conditions.py,
inside of which I'm again going to ask the user for two inputs,
call it x and y, and then I'd like to determine whether x is less than y,
greater than y, or exactly the same.
Well, as before, I can declare my variable called x, assign to it--
preemptively this time--
the result of passing to int the return value of input
asking the user for just x and then define another variable
called y, assigning to it the return value
of int, which is passed, in turn, the return value of input,
asking the user for y.
And now with these two numbers in hand, am
I going to proceed to do the following.
If x is less than y followed by a colon, indented below that,
I'm going to say quite simply, well, you know what?
X is less than y, simply to inform the user as much.
And then back aligns with the if.
Am I going to say else if x is greater than y with a colon
and then indented below that, print x is greater than y.
Now those are the semantics that I intend,
but it turns out you have to read the fine print.
In Python, it is not else if that you can use.
Humans some years ago decided that slightly more succinct than else
if would be literally elif.
And that, in fact, is the correct syntax to use when you have a second fork here
in the road.
But indeed when we have now a third, we might do this else if--
there I go again--
elif x equals y, let me go ahead and say print x equals y.
But there's a bug here already because equal does not mean what you think.
Indeed, before we've been using the equal sign as the assignment operator,
so to speak, copying from right to left some value.
And in fact, here on line 8, there's already a bug.
Using a single equal sign between my x and y
here would have the effect not of comparing those two values
but instead copying from right to left that value in y
into that value for x, thereby making them equal no matter what.
And so it turns out that we humans painted ourselves
into a bit of a corner some years ago.
We've already used equal to assign one thing to another.
So it turns out that humans in many languages
decided well, let's still use equal but two of them back to back.
And so if you want to use not the assignment operator but the equality
operator, do you want to use two of these things back to back.
And so now I have a program that asks quite simply if x less than y, print
as much.
Elif x greater than y, print as much then.
Elif x equals y, print the same, x equals y.
But we don't necessarily need this third condition, it turns out.
Logically, if you've got two numbers, two integers,
I'm pretty sure, by definition, it'll either be greater than 1,
less than the other, or exactly the same.
And so we can save a tiny bit of time here
by not even asking that third question.
If I know that x is not less than y and I know that x is not greater than y,
I might as well just confer logically that they must be actually equal.
And so here we have the first of my Python programs
that much like my pseudocode for finding Mike Smith allows
me to take users' input and then compare it
in order to take a different fork in the road based on its value.
So my Boolean expressions here are x less than y and x greater than y
and that's it.
And my conditions here or the syntax with which I induced these branches
are my if, my elif and my else.
But important in Python--
unlike in pseudocode-- is some of this syntax.
The colon very specifically say do the following if this is true,
and the indentation, the multiple spaces--
here I typed four--
is ever so important as well.
Whereas many programming languages are a bit loose
when it comes to whitespace, so to speak,
how many times you hit the spacebar or tab.
Python enforces that you have the same amount of indentation everywhere.
And so if I want lines four and six and eight here to all line up logically,
they must literally do so in the file.
Similarly, must lines five and seven and nine line
up just right so that they are only executed
if the lines are just above them are true.
Let me go ahead and save this file.
And in my terminal window, run Python of conditions.py,
typing in shall we say 1 for x, 2 for y.
And indeed, x is less than y.
Let's run it again, this time, flipping things around.
X is 2.
Y is 1.
And indeed, x is greater than y.
And one third time, which should be inferred if x is 1 and y is 1,
then, indeed, x equals y.
Now programming is not all about numbers.
In fact, pictured here in a program I wrote
already is answer.py wherein we have lines of code that, again,
prompt the user for input but this time, leave it as a string.
And so on line four am I asking the user for his or her answer
to a yes or no question.
Presumably the user might type little y or little n or perhaps capital Y
or capital N.
And indeed, I'd like this program to handle any number of those cases,
as any program might.
On line 7 here am I asking then two possible questions.
If c-- the name of the variable I gave to the users' input--
equals equals capital Y. Or to be robust, that variable c equals
equals lowercase y.
Let me go ahead and conclude and print that the user meant yes.
Meanwhile, if c equals equals n capitalized
or if c equals equals c in lowercase, similarly
do I want to conclude that the user meant no.
And so here the new operative word is quite literally or.
In Python, it tends to be fairly English-like,
ever more so than C and other languages where
if you want to do something or something else,
you literally say quite simply or.
If I wanted both situations to be true, albeit illogically,
could I use the actual word and.
But of course, the user's input can't simultaneously be capital
Y or lowercase y.
And so here is using or apt.
Now when programming, you don't have to use
only those functions that are handed to you by the particular language.
You yourself can invent functions of your own.
For instance, let me go ahead and in a file called
return.py implement a program that takes as input an integer
or number from the user and then quite simply prints out the square thereof.
So if the human types in 2, I'll print out 4.
If the human types in 3, I'll print out 9.
And so how when might we go about doing this?
Well, perhaps in a familiar way now, x shall
equal the result of converting to an integer whatever the user's input is
after prompting them for x.
And then I'm going to go ahead quite simply and print out, well, x times x.
That is the square of x.
I'll go ahead and save this and in my terminal window
run Python on return.py and as proposed, square 2 and as proposed, square 3.
And indeed, this program works exactly like this but to square a value
has kind of a nice ring to it.
And the fact that it happens to be implemented as x times x
is really just an mathematical implementation detail--
something that I shouldn't really have to worry about or remember.
I would just like to square the user's input.
So wouldn't it be nice if there were a function in Python--
or any language for that matter--
quite simply called square.
Indeed, I can make that happen, whether or not it exists,
and simply define it myself.
And so here I'm going to go and do the following.
Using Python's keyword called def--
short for define--
I'm going to go ahead and define a function called square.
I'm going to specify to Python that that new function shall take input
that I'll arbitrarily but conventionally call n for number.
And then with a colon as before and some indentation 2
am I'm going to go ahead and return quite simply n times n.
In other words, the math is the same.
The implementation details are the same.
But what's new here is this new keyword return.
Just like with the input function built into Python,
some human in that function's own implementation
had a line of code that said return to the user whatever they have typed in.
Here am I doing the same but returning not some users' input but rather
n times n.
And so you can think of this function called square much like that input
function, jotting down on a digital piece of paper that value,
handing it back to the caller or whoever uses this function,
and letting them use it as they see fit.
So now rather than use x times x myself can I more conceptually clearly say
square of the user's input x.
And the fact that x is not the same as n is quite OK.
It is this function square that presumes to call its own input n.
I can call my own input to square whatever I want, say, x.
So let's go ahead now and run this program and see what happens.
Based on these definitions, it would seem that I could square x in this way.
If I go ahead and run this program again, Python return.py, and type to,
I get more output than I surely intended.
In fact, this is the first of my truly bad mistakes.
Here do I see what's called traceback, which is sort of a trace
or a log of everything the computer tried to do.
And you'll see some hints, however, arcane this output, that line two is
where my mistake probably is.
In particular, I have some kind of name error in Python
where the named square is not defined, and yet it's right here.
Well, it turns out that Python and a lot of languages
take things fairly literally.
And if when you're interpreting your file
they're reading top to bottom, left to right,
unfortunately, it's too late to define square on line four
if you yourself want to use it on line two.
But we could surely fix this logically.
As by moving that code up top down below, defining square at the top,
writing my own logic below, now trusting that Python will see square and only
use it on line five.
Let's go ahead and save this file, rerunning Python of return.py,
again typing 2.
And voila, now it works.
But this isn't quite the most conventional way to solve this problem.
As naive as Python is reading your code top to bottom,
it's a bit of a regression now, a bit of a mistake
that I'm putting the actual code that I care
about at the bottom and the actual code that I
was trying to abstract away, if you will, and give name to at the very top.
At the end of this day, the program I care about
are these lines here, not my implementation of square.
And so I would actually prefer, albeit a bit nit pickily,
to put that code actually where it was.
And so if you do that, thereby keeping the main part of your program
at the top, as is convention, you still have to solve the same problem.
So how might we do that?
Well, the Pythonic way or the conventional way in Python
is to do this--
to define a function that most people call itself main, again,
ending it with a colon, indenting below that the lines you have written.
And then at the bottom of the file is the very last thing
you do, telling Python to call that function called main.
Because here now is what Python will do.
It will read this file in interpreting it top to bottom, left to right,
defining a function called main and then here defining a function called square
and then here calling one of those two functions, which
in turn, calls the second.
But in this way have you taken care to define all of your functions first
and never calling any of them until everything's been defined.
Now so that you've seen it too, it's not quite conventional to just run
main at the bottom.
Instead, you'll typically see a more magical incantation like this.
If underscore name underscore underscore equals equals quote unquote underscore
underscore main underscore colon, indented below that
will be your actual call to main.
This, for more arcane reasons, ensures that if you're
using a small program as part of a bigger one,
it won't necessarily get executed at the wrong time.
But logically, what the key takeaways here
are what you can actually do by defining your own functions.
Here too do we have an abstraction.
What does it mean to square two values?
Simply multiplying one against the other.
But it would be nice to just refer to that as a verb unto itself like square.
And so by defining this function do we now abstract away that multiplication
and just treat this as the idea we actually care about.
Now, of course, we're not saving all that much time,
and my programs even bigger than need be.
But it's demonstrative of this principle of abstracting away implementation
details and building your more interesting product on top of work
that you or someone else has already done.
Now what if we want to do more than just square a number?
We instead want to prompt the user for input, convert that input to a number,
and then ensure that that number is the type of number we want.
Indeed, it's not uncommon in a program to ask the user for a positive
integer-- something useful--
again and again until he or she provides just that.
For instance, the user might type 0 or negative or something else still,
but you want to pester them again and again until they
provide exactly the input you expect.
Much like in a website where you're forced to type a number
or email address or something else, similarly
can we do that in Python in code.
So let's go ahead and do exactly that.
And assume for the moment that there exists already a function called, say,
get positive int--
a function whose purpose in life is to get from the human an
integer from one on up.
Let me go ahead and preemptively this time
to find my own main function with def.
And inside of that code, go ahead and declare a variable called I for integer
and then just presume for the moment to call
a function called get positive int, which itself will take a prompt as
before asking the user, say, for I. And what do I
want it down to with this number?
Well, let's keep it simple for now and just print I itself.
But I now need to implement that function called get positive int.
So for that, I can use def and say def get positive int.
But I need this function to take itself a prompt.
And I'm going to go ahead and call it exactly
that, which is to say when I call this function,
as I've done on line two passing in some string of text
that I want the user to see, well, in my definition of get positive int on line
five, I need to tell Python to give that input a name,
so I can refer to it ultimately.
Because, indeed, when I'm going to do after adding that colon
and indenting underneath is ultimately, we
want to call input, passing in precisely that prompt.
After all, get positive int is not going to presume
to know in advance what the programmer wants to prompt the user with.
Instead, it's going to get it just in time via that input or argument.
But I need to pester the user again and again if they don't actually
give me a positive int.
And so how might I do that?
Well, just like in pseudocode, we might define for ourselves loops--
blocks of code that do something again.
And again as we go to step two, as before, so can I do that in Python
in any number of ways, but perhaps the simplest here is this--
to simply say you know what, Python?
Go ahead and give me an infinite loop while true--
while being my operative word here, inducing a loop while something
is true.
Well, you know what's true always is the word true.
And indeed, built into Python are Boolean values--
true and false literally-- by definition, capital T and capital F.
So by saying while true colon, I'm saying Python, please
go ahead and do something again and again until I tell you to stop.
Well, what do you want Python to do in this loop?
I want to go ahead and declare a variable called say n,
assign to it the return value of calling that in function,
passing to it the output of input.
And then and only then if the human has obliged and given me a positive int,
I'll go ahead and say, well, if n greater than zero,
go ahead and break out of this loop.
So a different approach than we saw in pseudocode where I simply said
go to and go to and go to again.
Here I've instead said, Python, do this forever until I say break.
And only once n is greater than zero, as per the user's input,
do I break out of this loop entirely and therefore return
when I'm ready that value called n.
And so here on my last line of code am I again using return, handing back,
if you will, a sheet of paper on which is that number.
But I only reach this line 10 after I've said break on line 9,
and so does this function get positive in ultimately return exactly that.
So as always, let me go ahead now and save this file but only
after adding that last cryptic line.
If the name of this file is implicitly underscore underscore main,
then do I want to go ahead and call main so
that we avoid all of those issues of code in the wrong order.
I'll go ahead and click Save and do Python of positive.py,
providing an input, say, negative 1, being
prompted again for a number so negative 2-- still
not a positive int nor a zero.
But if I finally type of value like one do
I actually see the one that I inputted.
But it turns out Python supports other types of loops
as well, not just via this keyword called while but actually
via a preposition called for.
For instance, suppose that I want to implement a program that,
not unlike a charting program like Excel,
prints for me some kind of bar chart.
These bar charts will be purely textual using, say,
hash marks to represent values.
But to do this, I'm going to have to prompt the user for input
and then print out precisely that many hashes horizontally.
Well, let's see what I get.
I'm going to go ahead and as always prompt the user for input.
We'll call it say n.
And that user's input shall be converted via int after asking them
for that value of n.
And then once I have that value do I want to iterate
that is loop some number of times--
some number of times equal to whatever the user's input was.
So if the user has inputted one, I'll print just one hash mark.
If the user inputs 10, I want to print 10 of those hashes.
But how do I do this?
A while true loop or forever loop that infinitely loops is probably
not the right approach here.
But rather I want to iterate some finite number of times.
And so a for loop allows us to do exactly that
with built-in functionality as follows.
Let me go ahead and say Python for I in the range
of n, which is the user's input, go ahead per the colon
and do the following next.
Go ahead and print out a single hash for each value.
And so what is this line of code doing?
Here on line 3 do I have for I in the range of n.
Well, it turns out that range is a function built into Python
that returns to you effectively a range of values.
By default, that range starts at 0 and goes up to but not through the value
you ask for.
So if you pass to range the value like 1, you will iterate only one
time the range of 0 to but not through 1.
If you instead input a value of 10, you'll iterate over a range of 0
through 9 up to but not through 10 and get precisely that many hashes.
My goal, again, is to print a bar chart of sorts
with one hash representing each of these values
from left to right, a horizontal bar chart.
So let me go ahead and save this file here and in my terminal window
run Python of score.py.
We'll input a number like 10.
And unfortunately, they all seem to be vertical.
And if I scrolled up higher in my terminal window would I see all 10
but again, one on top of the other.
So how do I somehow keep my cursor, if you will, on the same line?
Well, all this time, I've been using print.
I've been getting a new line for free, so to speak.
At the end of printing anything has Python
been moving my cursor, not unlike an old school typewriter,
to the bottom left of the next line.
But sometimes I want my cursor to stay on the same line,
even as I do something again and again.
And it turns out Python--
and knowably know this by having looked at the documentation,
therefore is that the print function can take
a second input that is not necessarily just
some other string you want to print.
But instead it's a named parameter--
that is, an input that has a predetermined named--
in this case called n--
that you can set equal to a specific value like nothing.
It turns out, albeit non-obviously, that, by default, Python
ends each line with a carriage return, if you will, or a blank line,
otherwise represented here technically as a backslash n, which itself
is technically distinct from an old school carriage return
but has the effect of moving that cursor down to the next line.
So this is implicit.
It would be incredibly annoying if any time you wrote Python code
and wanted to print something that you had
to type out that sequence of symbols.
And so you get those for free, so to speak.
But if you want to override that default behavior,
you need to instead tell Python's print function, you know what?
End your lines with nothing at all, quote unquote with nothing in between.
But when I'm done printing all of them, it
would be nice to move my cursor to the next line so that my next prompt--
that dollar sign we keep seeing in my terminal window--
is at least on its own.
So I'm going to go ahead and say print open paren closed paren
with nothing inside that because if I get for free a blank line,
I don't need to pass anything to print.
That is as the very last step just going to move my cursor to the next line.
So let me go ahead and save this program now and again,
in my terminal, window run Python of score.py, typing in this time 10.
And there do I get my 10 hashes horizontally.
So it turns out Python has what are called types, and the only time you
really need to know or care about this is when, frankly, it
starts to bite you, like it did us.
Indeed, when I asked for the user's input
and expecting an integer but the user typed exactly
that but I didn't convert it in advance to an int,
I got ultimately that weird behavior of concatenating one string to another.
So underneath the hood are there are any number of types built into Python--
a bool like true/false, integers like numbers, strs or strings of text,
and even floats, real numbers that have a decimal point
and some number of digits after.
But beyond that are more sophisticated data types or data structures still.
Dict or dictionary, which is as we'll call it a hash table of sorts,
list which can be any number of values back to back,
a range of values as we've just seen, or a set wherein
you have no duplicate values and a tuple, not unlike x comma y or latitude
comma longitude.
But it turns out that an appreciation of these types
can help you avoid some very serious mistakes in code
because it turns out that depending on how you store your data in a computer's
memory, you might actually get behavior that you didn't actually intend.
For instance, let me quite simply write a program that prints out
the value of, oh, say, 1 divided by 10.
I have here a file called, say, imprecision that I quite simply
am going to do this--
prompt the user for an input called x, converting their input to an int,
as always, asking for x, and then defining another variable called y--
this time, converting to an integer the user's input after prompting for y.
And then quite simply, I'm going to print x divided by y.
Let me go ahead and save this and in my terminal window
run Python of imprecision.py, hitting Enter here,
typing in, say, 1 divided by 10.
And so 0.1 is the answer, just as you'd expect.
Let's go ahead and print out more digits than 1 after that decimal point,
just to make sure that 1/10 is indeed 0.1 with implicitly an infinite number
of zeros to the right.
Well, let me go ahead just for simplicity's sake
and first store the value x divided by y and a third variable z.
And then in my print statement here, let me print out exactly that z,
but let me format it a bit differently than usual.
Using Python support for an or format string,
using that prefix f, which connotes give me a format string,
am I going to print exactly that quote unquote z.
But recall that you need to surround it with those so-called curly braces
to make clear to Python that you want to plug in its value and not literally z.
Well, it turns out there's additional syntax we can use, albeit cryptic,
via which to tell Python, yes, print z but to this many decimal places.
And the syntax for that is a colon right after the variable,
followed by a literal period, and the number of decimal points
that you'd like to print.
And because this is a so-called floating point value or real number,
we need one additional F. Now with this syntax
should I be able to print that same value but to a specific number
of decimal places.
Let's see.
In my terminal window, let me go ahead and run Python and imprecision.py,
again inputting 1, again inputting 10, and whew,
I indeed see point 1 followed by nine more zeros--
a total of 10 digits.
Well, let me get a little more curious and instead print out, oh, shall
we say 20 decimal places--
again running my program in precision.py,
inputting 1, followed by 10, and all looks almost good until wow, 5, 5, 5.
I'm a little curious now as to what's going on.
Let me go ahead and run this one last time after printing 30 decimal places.
Here I'm going to go ahead and run Python of imprecision.py,
hoping that the I'm not going to get worse, and it does.
It seems if you look far enough out, you start to see some weirdness.
In fact, let me go as far as out as--
I don't know-- 55 decimal places, going ahead and running
Python of imprecision.py, and putting one in 10.
Oh, my god, it does get worse.
So it would seem that all of us taught in grade school that one divided by 10
indeed equals 1/10 is not quite true.
If you look far enough beyond the decimal point, eventually,
things go horribly, horribly awry, and that is because computers quite often,
as powerful and sophisticated as they are, can't quite do everything
and can't quite do everything that we humans do.
Now why is this?
It would seem that Python is ever so slightly
off when it comes to the representation of this floating
point or this real value.
Now why is that?
Well, inside of a computer is hardware like this, RAM or Random Access Memory,
which is a little chip of memory inside of your computer wherein files
and programs or stored when they're open or running.
And inside of each of these black chips is some number of bytes or bits
that are ultimately used to represent any of the values in your program.
The catch here, though, is that this device, like any physical device
in the real world, has only a finite amount of space or capacity, which
is to say, no matter how big or expensive this particular RAM is,
it has a finite number of bytes--
maybe one billion if it's a gigabyte or two billion if it's two gigs.
But it's a finite number in total.
And by default, what Python and most languages do is
they decide a priori how many bits or bytes
to use to represent any of the values in your program.
And so if your number is so precise or so big
that it can't quite be represented in only that many bits,
the language, like Python, is going to come as close as it can
and represent that value with some approximation.
And that's what we're seeing.
One divided by 10 is surely a mathematically well-defined number.
Indeed, it's 1/10 or 0.1 and mathematically should be 0.10000 ad
nauseum infinitely.
But in Python, if you're only using, say, 32 or 64 any number of bits,
you can't possibly represent the infinite number
of numbers that exist in the world and represent all of them
perfectly, precisely.
To do so, you would surely need an infinite number of bits,
and we don't have that in our physical world.
And so you have to suffer, unfortunately,
this potential for floating point imprecision where values you care about
are going to be close but not quite what you
intend, unless you, the programmer or designer of the system,
are willing to spend more than just 32 or 64 bits
but more and more and more and enough that you
can get that decimal point and those values as far off to the right,
so to speak, as you can.
But darn it, if there isn't another problem that derives
from precisely the same constraint.
Not only can you have imprecision when it comes to floating point values,
even integers are potentially flawed, not necessarily in Python
because in the latest version of Python have they designed in the language
the ability to use as many bits as you need to represent integers--
specifically, numbers like negative 1 and 0 and 1 and everything
to the left and everything to the right.
The language itself will use more and more bits
to store exactly the integer you want, but that
did not used to be the case in Python and is still not the case
in some languages.
Some languages are vulnerable to what's called integer overflow whereby
if in that language you try to count so high that you need
to represent a number that's too big to fit in the amount of storage
you've allocated--
32 or 60 or some number of other bits--
you're going to overflow the value.
The result of which is that you might be going up and up and up and up
and representing a bigger and bigger value.
But it gets so big that all of the ones in that number become zeros.
And somehow accidentally.
you end up overflowing and starting all over numerically.
Now how might that be?
Well, consider a number that's represented with only three digits,
and let's start counting, for instance, from 123.
Adding ones to that gives you 124, followed by 125, 126, 127, 128, 129.
And what do we do in our human mathematical world?
Well, if you were about to hit nine and we now need to go to 10,
you don't just write 10.
Rather you write zero, and you carry the one, so to speak,
continuing now with your logic, adding that one to that two, giving you 130.
And that's OK.
We've stayed within the confines of that three-digit number.
But of course, if we go count up long enough, we'll eventually reach 999.
But if we have decided to only allocate three digits for this number
where or what is going to happen when we add one number to this?
Well, you might be inclined to carry the one and carry then one again.
And in the world where you have the luxury of pen and paper,
you might simply write down 1,000, which is the right value.
But if your computer or device is only representing values with three digits,
you have overflowed this particular value,
and you've overflowed in the sense that even though I have it here
on the screen, that doesn't actually have room in which to fit.
And so your number 1,000 becomes mistaken for 0 0 0,
thereby having you've overflowed and wrapped around from a big number
to a small.
Now you might think that this is fairly contrived and why would
you ever do something so foolish as to only represent numbers
with three digits.
Well, we humans have done worse.
Now it wasn't all that long ago that we humans made precisely this mistake
using just two digits to store years.
After all, if almost all of your dates start with 1900-something,
you might as well just store those last two digits.
Unfortunately, by December 31, 1999, were many of us quite a bit nervous
that we hadn't found all of the code and all of the devices in the world
that we're still using just two digits because, as
with integer overflow, if you have a number already counted up to 99
and you only have two digits, you might run the risk that 99 rolls over,
overflowing to 0 0.
And all of a sudden, it is not the year 2000 but 1900 again--
quite simply the result of integer overflow
by using a fixed amount of memory to represent
something and not having anticipated that you might eventually need more.
But you can certainly design for this and engineer defenses against this.
Indeed, some games have done better than we as a society.
Indeed, this game here Lego Star Wars has a point system wherein
you can accumulate coins over time.
And if you play this game long enough, you
can accumulate apparently as many as 4 billion of these coins
but unfortunately no more because the engineers
who designed this game decided that the maximum number of points you can accrue
is just that--
4 billion.
But why?
Well, it turns out that in many computers and game consoles,
it's conventional to store your integers or ints 32-bit values--
32 zeros or ones back to back, which means
you have 2 to the 32 possible permutations
thereof, which means you have roughly four billion possible values.
You actually have a few more than 4 billion,
but it's perhaps cleaner in a game to just choose
a clean value with lots of zeros.
But in this game did they anticipate that you'd play this game too long,
and you might eventually overflow.
And who knows what might happen to that gamer
if he or she plays the game so long, and all of a sudden,
their high score becomes zero.
So these problems are solvable.
You just have to anticipate and actually engineer those solutions.
But sometimes we don't, including companies like Boeing.
It wasn't all that long ago that the Boeing 747 had a software bug in it
whereby the plane's power system might actually
turn off while the plane in the worst case were actually flying.
One article put it as follows.
A model 787 airplane that has been powered continuously for 248 days
can lose all alternating current--
AC electrical power-- due to the Generator Control Units,
GCUs, simultaneously going into failsafe mode, the memo stated.
This condition is caused by the software counter
internal to the GCUs that will overflow after 248 days of continuous power.
Boeing is in the process of developing a GCU software upgrade that
will remedy the unsafe condition.
Another website analyzed the situation as follows.
A simple guess suggests that the problem is a signed 32-bit overflow as 2
to the 31st power is the number of seconds in 248 days multiplied by 100--
that is, a counter in hundreds of second.
Which is to say it is presumed that Boeing had stored some form of integer
in its own software, and that integer was
representing the number of hundreds of seconds
for which the power had been on.
But if you're only using 32 bits and only have at your disposal
roughly 4 billion one hundredths of seconds, turns out mathematically,
after 248 days, that counter, which is clearly important,
might overflow and wrap around to zero, the result of which
is that the power of an airplane might shut off.
And so the temporary work around, before the software upgrade was deployed,
was to quite literally reboot the airplane while on the ground
before that 248th day.
Now we've only just scratched the surface
of what you could do with Python, and in fact, we've
looked at some of those characteristics that are demonstrative of the features
that you might find in any number of programming languages.
But we're not even limited to the functions that come
built into the core language itself.
It turns out there are called libraries and frameworks and yet more
in any number of languages that provide additional features that you somehow
have to load or import manually in order to use.
One such example might be in Python.
If you want to generate random or technically pseudorandom numbers
to create some kind of variation in how your program behaves,
it turns out that you can't just call a random function right out of the box.
You need to tell Python to please load or import that feature for you.
So let's go ahead and write a program in a file called pseudorandom.py
that allows us to generate a random number between, say, 1 in 10.
I want to go ahead, though, first and do this.
From a library called random, go ahead and import a function
called randint for random int.
And then if I want to go ahead and generate or select and then
print a pseudorandom number between 1 and 10 inclusive,
I can simply do print rand int 1 comma 10,
and the effect will be to let Python somehow figure out
how to choose a number between those two values
and return to it to me so that I can print it.
Let's go ahead and save the file and then in my terminal window
run Python of pseudorandom.py and 10.
Let's go ahead and run it again.
And this time, I get six.
Let's go ahead and run it yet again, and this time, I get 10.
Yet again, let's go ahead and run it, and this, time I get one.
And if I were to run it shall we say an infinite number of times, over time,
I would see a uniform distribution, hopefully, of those 10 possible values.
And that's what's meant by pseudorandom itself.
It turns out that languages and computers more generally can't really
pick a random number off the top of their head like you
and I can, rather they need to use algorithms, which themselves
are deterministic processes-- code that does the same thing again
and again in order to create, if you will, the illusion of randomness
by creating a statistically uniform distribution over some range of values.
Now often, a computer will use something that's
changing, like the clock that's built into it,
taking a look at the current time, and then generating
a random number or again pseudorandom number based on that variation
or if it has a microphone or a camera taking some ambient noise of sorts
and using that to feed into whatever algorithm it's
using to choose something randomly.
But suppose I want to now do something with this value and not just print it.
Suppose that we want to implement a bit of a game for a user--
pick a random number between 1 and 10 and see if they can guess it correctly.
Well, let's see how a program might like that might be implemented.
Let's first go ahead.
And from that library called random, import as before,
a function called rand int.
Although it turns out if you want to use not only this function or others,
you can more succinctly just say import random.
The difference being if you only import random,
we are going to have to prefix with the word random followed
by a dot every use of a function.
So we'll do it that way this time.
Let's go ahead and declare a variable called n
and assign to it right to left the result of calling rand int.
But this time, because we've not mentioned rand int by name,
I need to qualify this symbol and say random dot rand int,
thereby making clear to Python that the function I'd like you to call
is actually inside of that library called random.
But I can otherwise use it as before passing in two values--
a lower and upper bound like this, one comma 10--
and that should give me an n, a random number in that range.
Now I want to go ahead and ask the user for their guess,
and I'll go ahead and define another variable called, say, guess.
That is the result of converting to an int whatever
the user's input is for their guess.
And now, quite simply for this game, I want to compare those two values
and print, say, correct or incorrect.
And so I'll go ahead and say if the users guess equals equals n,
go ahead and print out as much correct.
Else if the user's guess is not right, let's implicitly
infer that, nope, incorrect.
And so we'll print that instead.
Let me go ahead and save this file now and run Python of guess.py.
This time, I'll be prompted for my guess.
I'll say five.
Unfortunately, it's incorrect.
Let's go ahead and play again this time running Python guess.py.
This time, I'll go with 10 since we saw it so many times before and correct.
I'll run it a third time and see what's going on.
This time, I'll guess one but incorrect.
Unfortunately, I've implemented perhaps the most frustrating game ever
because I'm not even telling the user what the actual number was.
But surely, I could do that by printing the value of the variable
I stored that random int in, but that would be yet another game altogether.
All right, let's now bring all of this together and solve an actual problem--
one say from yesteryear.
You might recall this game here, Super Mario Brothers, the original, and this
was a two-dimensional world built up of images like this.
Well, there in the sky so, to speak, do I see four question marks.
And that seems like an opportunity to do something again and again.
How might I print out for question marks in a row, not nearly as graphically
as that here but just with my terminal window and text editor?
Well, here let me go ahead and open those two.
And in a file called, say, mario.py, let me go ahead and print out,
quite simply, four questions.
I'll go ahead and print out quote unquote question mark question mark
question mark question mark.
Saving that file again in mario0.py, running in my terminal window
Python of mario0.puy and voila do I get an approximation
of what Nintendo did in yesteryear.
Now of course, doing something again and again
is clearly an opportunity for, say, a loop and not just printing it
all at once but just doing something again and again.
And while this will complicate the code initially,
it sets us up for a more interesting solution thereafter.
In a file then called mario1.py, let me go ahead
and implement that same sequence of question marks
but this time using that familiar loop, not a while loop or an infinite loop
but perhaps just a for loop like this.
For I in the range of 0 up to, but not through 4,
go ahead and print out just one question mark.
Saving that file brings me now to my terminal window in Python of mario1.py
enter.
Unfortunately, I have created not quite the right level.
But that's OK because remember that with print, you
get one new line for free every time you call it,
unless you override that default behavior.
So let's say, no, Python instead and your line with nothing,
and only once I'm completely done do I want you to print
one of those free new lines for me.
I'll go ahead and save my file again in my terminal
window, rerun Python of mario.py, and now we have the same exact result.
But later in the game do we see different aspects of Mario's world, not
unlike this thing here underground.
Pictured here are a number of blocks in the underworld,
and it looks to me like that bigger block
there is a composition of, say, 4.
So let's go ahead now and print out a block of bricks,
these are all represented by hashes so that I have four horizontally,
four vertically as well, and everything else filled in too.
We've not yet printed out anything on multiple axes,
if you will, both rows and columns of sorts.
So how to do this?
Well, in my terminal window, I'm going to create a file called mario2.py.
And in this file, I'm going to decompose this problem conceptually, so to speak,
into two different problems.
Built into that underworld are some number
of rows of bricks within which are these columns.
And I bet I could bite those off each one at a time.
So let me go ahead and do this.
For I in range of 4, go ahead and print what?
Well, for every row of bricks, do I want to print some number of columns too?
Because it's a square, the same number of columns as rows and so I
know how to print that many things too.
I can simply use another loop perhaps with a different variable
with which to count like for j, as is conventional in range also of 4.
And then inside of this inner nested loop, so to speak,
might I go ahead and print out just one hash ending each of my lines
with, as before, nothing.
In fact, I only want to move my cursor to the next line
after I've printed each of those columns.
And so only underneath that innermost loop do I want that call to print.
Let me go ahead and save this file now and run Python of mario2.py.
And if I've gotten this logic right, I can go ahead and print out
those rows and columns too.
And indeed, that's what I get.
It's not quite a perfect square because those hashes are
a little taller than they are wider.
But via a for loop, one nested inside of another can
I handle two problems at once--
the act of iterating from row to row to row and within each row, iterating
left to right via column.
And in fact, to make that ever more clear, why
do I even call my variables more aptly.
For each row in the range of four in each column in the range of four,
go ahead and print each of those hashes.
Frankly, it doesn't even matter what I call these variables because I'm never
actually using them per se.
I'm simply telling Python to count up from 0 to 4 using
those particular names.
Those then are some programming languages--
Python especially among them.
And just as we saw in pseudocode, the ability to express functions
and conditions with Boolean expressions and loops and then things like
variables and more, so can we express those exact same ideas in Python, in C,
C++, and Java.
And with each of those languages do you get different features,
with each of those languages do you get different techniques.
But ultimately, those languages are all just
tools for one's toolkit with which to solve any number of problems with data.