There is no p10c.sql

A brief review of tuples and lists:

Lists:

Lists are used in the way that most programming 
languages would use Arrays.


You "declare" a List by assigning it some list data.
That can even be the empty list:

li1 =  []
li2 = [1, 2, 3, 4, 5, 6]

Lists are indexed starting with 0.

You can access lists like you normally access an array:

li2[0]  --> Returns 1.
li2[5]  --> Returns 6.

The length of a list is found with the function len()

len(list2) --> 6

As opposed to arrays, Lists can contain different data
types:

li3 = [4, 'ab', 5]

Lists can even contain sublists:

li3 = [4, 'abc', 5, [9, 10, 11]]



The most interesting list operation is called "slice."
While there is an explicit call to slice(), most of the
time you do slicing by putting appropriate expression
between [ and ].

For example:

li3[0:3] -> [4, 'abc', 5]

The first number - the 0 - is the starting index.
The second number - the 3 - is the ending index.

Important:  The first number - the 0 - is inclusive.
The second number - the 3 - is exclusive. Always.

So li3[0,3] returns the list consisting of 
li3[0], li3[1], li3[2]



A slice of one element is different from the element.

li3[0:1] --->  [4]

Note that this is DIFFERENT from

li3[0] ---> 4

In the first case, a list is returned.
In the second case, an element is returned.



Now there are shortened versions of slicing.

li4 --> ['a', 'b', 'c', 'd', 'e', 'f']

li4[:2] --> ['a', 'b']

In other words,  li4[:2] means li4[0:2] !!!!!!!!!!



Now to the explicit slicing operation slice().

li4 --> ['a', 'b', 'c', 'd', 'e', 'f']

sliceobject1 = slice(1,3)

Note that there was a COMMA above, not a COLON. It's a function call.

li4[sliceobject1] --> ['b', 'c']




A negative start number in a slice will return the last element:

>>> li4
['a', 'b', 'c', 'd', 'e', 'f']

li4[-1] -->  'f'




Now it gets more complicated.
li4[-3:-1]  --> ['d', 'e']

li4[-1:-3] --> []

li4[3:1] --->  []

This gives you the whole List: Always.

li4[0:len(li4)]) 

Slicing allows a third parameter.


li4[0:6:2]  -->  ['a', 'c', 'e']


So the third parameter - the 2 - means
show every second element. It starts with
the 0th element, followed by the 2nd element
followed by the 4th element, etc.




We are now returning to li2.

li2 -->  [1, 2, 3, 4, 5, 6]

li2b = li2

li2b --->  [1, 2, 3, 4, 5, 6]

List variables are POINTERS.  This is important!!!
Look:

We are changing li2:

li2[3] = 'x'

li2 --> [1, 2, 3, 'x', 5, 6]

Now look at li2b

li2b --> [1, 2, 3, 'x', 5, 6]

It was ALSO CHANGED.




When a list is passed as parameter to a function
and then changed in the function, this changes
the list in the main program!!

def testalist (li):
    li[4] = 'Z'

	
testalist(li2)
li2 --> [1, 2, 3, 'x', 'Z', 6]

Again, this is not obvious.




What if you want to define a large empty array?

That's actually not obvious either.

The most basic way to do it is like this:

li5 = []
for i in range(10):
    li5.append(i)

li5  --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

But there are many syntactic variations, some of them
quite unusual.

For example:

li6 = [0] *10
li6 -->  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Stackoverflow has a whole article about this:

https://stackoverflow.com/questions/10712002/create-an-empty-list-in-python-with-certain-size




Why is it called a List not an Array?

Maybe because many of the standard list operations
are allowed for these lists. Now we are treating them
like objects.

Examples:

li2.append(99999)

li2 ---> [1, 2, 3, 'x', 'Z', 6, 99999]

li2.reverse()

li2 --> [99999, 6, 'Z', 'x', 3, 2, 1]

IMPORTANT: This is a destructive change.
You have changed li2 itself.
Not a copy of it. (In other programming languages
this is different.)




That's enough about Lists.

Now about Tuples.

Tuples:

Tuples are in many respects like lists.

The three important points about tuples are:

1) Python uses Tuples internally for several
purposes. So you will encounter them.

2) Tuples are initialized with round parentheses.
But other than that, confusingly, for element access
and slicing you still use [ ].

See example:

tup1 = ('z', 'y', 'x', 'w')
tup1 -> ('z', 'y', 'x', 'w')
tup1[0] -> 'z'

tup1(0) -> ERRRRROR

Why is this so?  Because a name followed by () is considered
a function call.

You CAN define a FUNCTION tup1 and this will work.

def tup1 (x):
    return x

tup1(9999) --> 9999

So elements in the tuple data structure tup1 cannot be accessed with ().
But you CANNOT have a tuple variable and a function with
the same name tup1.  So above definition of the function tup1
wipes out the Tuple tup1.

So I have to recreate the tuple tup1.
Tuples can be sliced just like lists:

tup1 = ('z', 'y', 'x', 'w')

There is no error message that you are losing the the function tup1!!!

tup1[0:4:2] -->  ('z', 'x')

3) Tuples cannot be changed.
See example:

tup1[3] = 4  --> ERRRROR

The technical term that you will hear a lot is 
Tuples are IMMUTABLE.

-------------------------
While we have managed to implement everything from PL/SQL,
except for integer out parameters, in Python there are 
may powerful data structures besides Lists and Tuples 
and functions processing those data structures that we did 
not cover and cannot cover.

See http://docs.python.org/3/library/stdtypes.html


There are complex numbers
iterator
range (we saw a little of that)
str (we saw a little of that)
bytes
dict (dictionaries, sort of like hashes in Java)

Furthermore,

Modules, Classes, Functions, Methods, 
Code Objects, Type Objects, etc. are also considered data types.

We cannot cover any of this.