Introduction to Computing
Programming languages at their core are very simple. There are at most about 6 things a simple programming language needs: iteration, assignment operations, Functions, data types, collections and control flow. I will explain why they exist in the simplest terms I can, while showing examples of different languages and syntaxes. This introduction assumes no knowledge of programming whatsoever.
Assignment Operations
If I say it is going to rain tomorrow, what do you think? Chances are it has something to do with the weather, specific the type of weather called rain, and also has something to do with tomorrow. If you believe me, perhaps I'm particularly trustworthy, or have some insider information about the weather, you have committed to memory that tomorrow, the weather will be rainy.
You have just assigned the value rain, to the weather attribute for the time
tomorrow. Somewhere in your mind you have a idea about tomorrow, decided it
would be a good idea to bind the idea of rain to tomorrow. Bind
is just
another term that means assignment.
Computers in the same sense have to bind information to certain symbols, so that
you can reference them inside your program. Similarly, if I had no way to
communicate to you the idea of tomorrow, I also have no way to communicate an
attribute tomorrow has. When I say attribute
I just mean a characteristic
something can have, tomorrow has a date attribute, it has a weather attribute,
it may also have events associated to it, you get the picture.
This is really important! If you told me your yo-yo is red, and I have no idea what a yo-yo is, I'd probably forget you have a yo-yo or that it was red, despite knowing what the color red means. Similarly if I tell you I have a thingamajig and its color is green, you'd probably forget too. Computers work the same way, you have to give them a symbol to remember some bit of information by.
Imagine if we had no shared word for tomorrow, Earlier I said tomorrow will be rainy, but suppose you've forgotten, as you had nothing to attach that idea to. Now when I would like to talk about tomorrow again, I have to remind you what tomorrow is, so I say "Tomorrow is a date". I remember that I also told you it would be rainy, so I say again "Tomorrow will be rainy." and then get to adding what I wanted to add and say "Tomorrow is a Thursday." Great! Now you know all that about tomorrow, but as soon as we start talking about something else, all those ideas of what tomorrow is are gone. Computers have to have a way to reference those ideas, or else programming would be a huge pain, and so we have assignment!
Assignment allows us to pull out something by name, when we reference something by name we know we have the contents of that symbol, but we do not know if it is what we expect it to be.
Control Flow
Control Flow is all about making decisions. If it rains tomorrow, you should bring an umbrella, is an example of control flow. We have a if key word that lets you know the structure of the sentence, followed by a statement that can either be true or false, and then some action that should be performed if the statement is true. Here is what that would look like in Lisp.
(if (rain? tomorrow)
(bring umbrella))
;; the english version would be the following
(if (this) (then this))
As you can see control flow is pretty simple, but its critical to how computers operate. More complicated control flow technique you might run across in Python is a else if tree. Here's an example of one:
if rain(tomorrow):
bring(umbrella)
elif snow(tomorrow):
bring(boots)
else:
print("Nothing special to bring!")
This looks a little complicated, but its really a bunch of if statements lined up in order. The only difference from what we had earlier is that instead of just having one single conditional we now have 3 of them chained together. Once one of those conditions is met then you only execute the code in that statement, all the elif is doing is pulling all this code into a larger block. So in this example, if it was for some reason both snowing and raining you would only bring the umbrella. In order to fix this you would have to do the following:
if rain(tomorrow) and snow(tomorrow):
bring(umbrella)
bring(boots)
elif rain(tomorrow):
bring(umbrella)
elif snow(tomorrow):
bring(boots)
else:
print("Nothing special to bring!")
There are a few other ways to fix this, but I'll leave that to you to play around with.
Functions
You're writing a small programs to organize your music, say your have a few thousand MP3 files. We'd like to be able to add or remove music to the collection, and it'd also be great if we could look at specific genres or artists.
You can think of a program as a tree. The bottom of the tree is where the
program starts, and it terminates at one of the leaves. To make things just a
little more complicated parts of the tree will point back to a lower or higher
part of the tree. If we think about this a little more we find that the tree
metaphor isn't really the best way to think about things, we'd be better off
making it more abstract. So instead of a tree we're going to call it a graph. A
graph is a thing that has vertices
and edges
. You can think of a vertex as a
smaller piece of content, and an edge as a connection between that content. So
we have a start vertex and a bunch of end vertices.
So if we look at our graph, we see that there is a start node that points to two
other nodes. You can either add or remove from the start node. Let's say we
choose add, once there we add the music we want to add, then we have to go to
another
as it's the only option from add. another
asks us if we want to go
again, and if so, which thing would we like to do, add
or remove
. If we say
yes, and remove then we can remove the music we just added. If we say no we exit
the program. The graph represents the program, but something important is
happening with another
.
We can reach another
two ways, one from remove
and the other from add
. If
we didn't have functions we would have to have two separate versions of another,
one each for add and remove. Two distinct versions of the same thing is
generally a bad idea, if you want to update how another
behaves you have to
change it in both places. It may seem like a small issue for this example, but
if another
is used a few thousand times, nobody is going to go and change
every single one.
Functions generally give you something back, in our case add
and remove
don't return anything. They modify the database of our music without returning
any value. Take the following python example of our program.
music = []
def start():
print("Welcome to Music Manager")
while true:
choice = another();
if choice == "add":
add_music();
elif choice == "rem":
remove_music();
elif choice == "exit":
break
print("Leaving Music Manager")
See how we didn't pass in any values into add_music
, thats because it modifies
the music
variable we assigned outside the function. Let's take a look at
another
:
def another():
while true:
choice = input("(A)dd music, (R)emove music, or (Q)uit?")
if choice == "A" or choice == "a":
return "add"
elif choice == "R" or choice == "r":
return "rem"
elif choice == "Q" or choice == "q":
return "exit"
else
print("Invalid Character: Please try again!")
Another
returns a value, in this case the choice that the user made. Each time
you call another
it will produce the exact same result, and return one of
three values.
There are a few different types of functions. We've talked about the most
general type, which is a function that can have side effects, and can access
things you declared in the file it lives in. Another type is a pure function
,
which always returns the same value if called with the same inputs, and cannot
have side effects. The only thing that changes from a pure function
is the
result you get back. You can also have functions that are attached to certain
types of data. These are generally called methods
, and are found in Object
Oriented Languages like Java. Here's an example of some method calls, they all
modify whats going on inside the object they're called on.
RaspberryPi raspPi = new RaspberryPi();
raspPi.turnOn();
raspPi.runProgram("Music Manager")
Data Types
Now that we know what functions are we would like to know what types of things those functions can take as arguments. Languages handle this issue in different ways, dynamically typed languages infer the type you mean, based upon its content, and then tries to execute the function you called on it. If it fails it gives you an error message, in statically typed languages you would not be able to compile a function that is called with the wrong arguments.
Types are combinations of other types, or map symbols to keys. The combination of other types approach is common in statically typed languages, and keys to values is common in dynamic languages.
struct car{
char * make;
char * model;
int year;
}
They char * might be a little confusing, it just points to the first char in the total string. The key thing to note is that each of these items in the structure have specific types. The same general idea applies to object oriented programming, but in OOP each type has associated methods.
Clojure and other dynamic languages take a different approach.
(defstruct car :make :model :year ...)
You can think of this as a table, in which each key corresponds to a value.
| key | val | | :----- | :------ | | :make | Porsche | | :model | 911 | | :year | 1973 |
The key thing to note is that each of these keys do not have an associated type.
Collections
Collections are groups of specific types. It makes assignment easier as you don't have to think of each item as a separate thing, but rather part of a large collection. Here's an example of what python would look like without collections.
In [3]: game1 = "Checkers"
In [4]: game2 = "Chess"
In [5]: game3 = "Go"
In [6]: game4 = "Backgammon"
In [7]: game5 = "Poker"
In [8]: game2
Out[8]: 'Chess'
This is a pain to type and a pain to reference. I'd rather do something like this:
In [1]: games = ["Checkers", "Chess", "Go", "Backgammon", "Poker"]
In [2]: games[1]
Out[2]: 'Chess'
This looks a little different as it is copied and pasted from an iPython command
line, but its similar to what we've seen before. Note that accessing games[1]
gives me "Chess", as opposed to "Checkers". This is because indexing in computer
science starts at the 0th index. So games[0]
results in "Checkers".
In this example games is an array of elements, that means they're ordered and you can get each element pretty quickly. There are other data structures that optimize specific operations, or only allow you to access elements in a specific way. They vary dramatically, but it's important to remember that each data structure is optimizing specific access patterns.
Iteration
Suppose you have a collection of things. I'm going to assume you choose cars, because that is going to be the running example through out this post, feel free to replace car with a type of thing of your choice. Imagine further that your car collection is so large you can't take in more than one car at a time. How do you get a look at your collection in order to see what cars you have?
Most people would decide to start with a specific car, and work thru the whole
group. This is iteration. As you pass over each car you generally either take
some sort of information from it, apply a function to it, or decide if you want
to add it to a smaller collection, Say only your trucks. In fact, this is the
core idea of map
, filter
and reduce
.
To map
a function to a list is to iterate over each item in a list, and apply
that item along with other items to the function, and then store the result.
A filter
is iterating over a list, and returning back a list with some
elements removed. The function you pass in to the filter method - along with the
arguments - is generally called a predicate function.
To reduce
a list is to iterate over each item, and then pull out some content
from the list, and combine it with another item. Perhaps you want to find the
total HP in your garage, well you would create a place to store the total, the
iterate over the items in the list, adding each item's HP to the total HP.
This terminology is not prevalent in most languages, the most common looping
constructs are based on for
, while
or foreach
loops. They usually take the
following forms:
//Prints every car in your garage
for(Car c : garage){
System.out.println(c)
}
//Prints integers from 0 to 99
for(int i = 0; i < 100; i++){
System.out.println(i)
}
//Checks to see if there is a Porsche in your garage.
boolean carFound = false;
int index = 0;
while(!carFound){
if(garage.get(i).getMake().equals("Porsche")){
carFound = true;
break;
}
}
if(carFound){
System.out.println("Your garage has at least one Porsche")
}
As you can see foreach
loops iterate over every member in the collection, while for
loops have a very specific set of initializers.
Lets unpack (int i = 0; i < 100; i++)
a bit more an get an understanding of what each of the terms means. In english it would be (do this at the start; loop while this is true; do this after every loop)
If you found this useful or useless I'd love to hear from you about ways to improve this introduction. You can email devin@devinmcgloin.