Lecture 01 2018/08/22
1 The Zen of Python:
1 | import this |
2 Different ways for reading files:
1 | file = open('myFile', 'r') |
Lecture 02 2018/08/24
1 The intrinsic name and the bound name of a function:
1 | def myfunc(args): |
2 Function name should always be in lowercase.
3 Don't use tabs, use spaces.
4 help(fun_name): enter q to quit help
5 Docstring Syntax:
1 | def myfunc(args): |
Lab 00
1 Test labs and homework using ok in your own computer:
1 | python ok -q question_function_name --local # test for a specific question. |
Notice: only the latest version(currently the ok for 2020 Fall class
and 2021 Spring class) of ok can be working without the
--local
parameter. If you use an older version of ok python
without --local
, it will display
checking for software updates
and stuck at there.
HW 01
1 Answer for the Quine question:
1 | quline = 'print("quline = " + repr(quline) + ";eval(quline)")';eval(quline) |
The built-in eval
function, which evaluates a string as
a Python expression.
The built-in repr
function, which returns an expression
that evaluates to its argument.
Lecture 03 2018/08/27
1 If not short-circuiting, and
and or
Boolean operator returns the last expression they evaluate.
2 Some examples for including 1/0(ZeroDivisionError)
in
the Boolean expressions:
1 | True and 1/0 # No short-circuiting, so the result is ZeroDivisionError |
3 Debugging Methods:
- Running doctests for codes in a file:
1 | python -m doctest <filename.py> # simply run the test in docstrings |
- Running doctests for codes in the interactive session:
1 | from doctest import testmod |
Lab 01
1 Assertion statement should be included in a release version of your codes, but debugging print statement should not.
2 Traceback Message:
File <file_name> line <line_number> in <function>
3 Error Message:
<error type> : <error message>
4 Common Error Types: SyntaxError, IndentationError, TypeError, ZeroDivisionError, NameError, IndexError
5 Common Bugs: spelling, missing parentheses, missing close quotes, == v.s. =, infinite loops, off-by-one errors(boundary condition error)
Lecture 04 2018/08/29
1 Notice the following tuple assignment:
1 | def func(n,term): |
2 Currying(so-called 柯里化 in Chinese): use high-order function to convert functions with multiple arguments into a chin of functions(in some languages like Haskell a function can have at most one argument ):
1 | # Use g(x)(y) to represent f(x,y) |
3 Lambda expression :
1 | # An example: the three ways return the identical result |
4 Decorator: apply a higher-order function as part of executing a def statement:
1 | def trace(func): |
5 Newton's Method:
The procedure of Newton's method:
have the function \[func\] and an initial guess \[x_0\]
update the guess by the relationship: \[x_n = x_{n-1} - \frac{func(x_{n-1})}{d func(x_{n-1})}\]
stop the iteration when \[func(x_{n}) = 0\] or \[|func(x_n) - target| < boundary\]
1 | # The following example calculates the nth root of a number using Newton's Method: |
Project 01 Hog
1 Randint
1 | randint(a,b) # a <= x <= b |
2 Nonlocal variable: a variable declared in the outer function but can be modified in the inner function
1 | def f1(): |
3 Sort
1 | ls.sort(key = lambda x: x[0],reverse = True) # Default ascending |
Lecture 05 2018/08/31
1 How to draw an environment diagram:
- when defines an function:
1 | fn: <fun_name> --------------------> func <fun_name>(<formal_parameters>)[parent = <parent>] |
- when calls a function:
- create a local frame titled with the function being called
- copy the parent of the function to the local frame: [parent = < parent >]
- bind < formal-parameters > to arguments in the local frame
- execute the body of the function in the environment that starts with the local frame
Lecture 06 2018/09/03
1 Self-reference: a function that refers to itself in the function body is called self-reference
Lecture 07 2018/09/05
1 Recursive function: can be divided into a base case and recursive function calls
2 Mutual recursion: if a recursive procedure can be divided into two functions that call each other, then the two functions are mutual recursive.
The mutual recursive functions can be rewritten into a single recursive function. Say if you use two functions to represent the even and odd cases respectively, you can then replace them using one function that uses a step of two.
3 Tree recursion: a function calls itself multiple times in the function body
1 | # An example: let par(n, m) be the function that calculates the number of different partitions of an integer n, requiring each fraction no greater than m |
4 The Luhn algorithm: used for the credit card number checker.
The credit card number consists of two parts: the ordinary digits and the check digit at last. For example,if the account number is "7992739871" and the check number is X, then the full account number is "7992739871X". The Luhn algorithm is used to calculate what the number X is.
Steps of Luhn Algorithm:
Starting from the rightmost digit(the check digit) and moving left, double the value of every second digit; if the result of doubling operation is greater than 9, then sum the digits of the result(e.g., 6 * 2 = 12 > 9, then replace 12 with 1 + 2 = 3 )
Add the resulting numbers in each place, denote it as sum_results, then the check digit can be calculated in the following ways:
\[ check\_digit = (10 - (sum\_results \quad mod \quad 10) \quad mod \quad 10)\]
For the above example, the sum_results = 1 * 2 + 7 + 7( 8 * 2 => 7) + 9 + 3 * 2 + 7 + 2 * 2 + 9 + 9( 9 * 2 => 9) + 7 = 67. Then check_digit = (10 - 67 % 10) % 10 = 3. So the full credit card number is "7992739871X".
A simple way to verify whether the check_digit is correct: if
(sum_results + check_digit) % 10 == 0
, then the check_digit
is correct.
Lecture 08 2018/09/07
1 Functional abstractions: concrete implementations do not matter in this situation.
2 Choosing names:
- Names should convey the meaning or purpose of the values to which they're bound
- The type of values bound to the name is best documented in the docstring of the function
- Names can be long if they help to document your code
- Names can be short if they represent generic quantities
3 Test-driven development:
- Write test for functions before you actually implementing the function itself
- Develop incrementally and test each piece before moving on
- Run your code interactively
Lecture 09 2018/09/12
1 Different ways to implementations recursive functions:
- If multiple implementations of a function are equally clear, then you should choose the shorter one
- If the longer one is more clear, then you should use it
- Sometimes the base case is omitted so the function implementations are shorter, however, the function is still recursive in these cases
2 If you store every intermediate value during the recursion process, then it is pretty slow compared with Iteration.
HW 02
1 This one is quite hard for me, I spent a long time converting iteration to recursion for the ping pong question and the count coins question. At first there were duplicate results in my recursion formula and I checked for a while, also I turned to the hint videos on the website for inspiration. I learnt several lessons from these two problems:
- If you want to preserve several variables through the recursion process, define a helper function with all needed variables as arguments inside the target function.
- If counting from the largest number doesn't work, then try starting from the smallest one.
- A formula: term(n, m) = term(n - m, m) + term(n, fun(m))
2 The recursive lambda:
1 | lambda func: lambda args: func(func,args) # This is a general form for recursive lambda, the func is a two-argument recursive function |
Lecture 10 2018/09/14
1 Each value in Python has a class that determines the type of the value, values share class and behavior.
2 Native data types:
- Properties:
- literals: expressions that evaluate to values of native data types
- functions and operations to manipulate values of native data types
- Categories:
- numeric types: int (integers), float (real numbers), complex (complex numbers)
- no-numeric types: bool (True / False), the majority of no-numeric data types cannot be described in native data types
3 Data abstraction: separate segments of programs that deal with how data are represented from segments of programs that deal with how data are manipulated
Function abstraction: separate how function is implemented and how function is used
4 Abstraction barrier: lower-layer functions are invisible to function users, they only access higher-layer functions, the higher-layer functions can interact with the lower-layer functions. In such a way, how data is actually implemented, how lower-layer function is defined (like selector/ constructor e.g., get/set, constructor in Java) don't affect the behavior of the data types/ functions.
Lecture 11 2018/09/17
1 Sequence: an ordered collection of values, there are different types of sequences, but they share common behaviors.
- Properties: finite length, an empty sequence has 0 length
- Indexing: starting from 0
- list is a type of sequence, string is a type of sequence, range is a type of sequence
1 | for _ in range(n) # use underscore instead of index like i |
Lab 04
1 I stuck at question 5 for some time, so I put the question here:
A subsequence of a number is a series of (not necessarily contiguous) digits of the number. For example, 12345 has subsequences that include 123, 234, 124, 245, etc. Your task is to get the maximum subsequence below a certain length.
1 | def max_subseq(n, t): |
Lecture 12 2018/09/19
1 Box- and-pointer notation: a way to represent lists in the environment diagrams
It's a series of adjacent boxes with two parts in each box, the first part is an index, the second is a value for this box or a pointer pointing to another box or pointing to a function.
2 Processing container values:
1 | sum(iterable [, initial_value]) -> value |
3 Trees abstraction:
- Recursive definition: a root label and a list of branches, each branch is a tree; a tree with no branch is called a leaf
- Relative definition: each location in a tree is a node, each node has a label that can be any value; one node can be parent/child of another
4 Sum of nested list: return a list, the element of which is the sum of the elements of the original list at that place
5 Functions that take trees as inputs/outputs are often tree-recursive themselves
Lecture 13 2018/09/21
1 Objects combine data and behavior, objects represent information, but also behave like the things they represent.
1 | from datetime import date |
2 Swapcase of a string
1 | str.swapcase() # swap lowercase and uppercase in a string |
3 Identity operator and equality operator:
1 | a is b # True is a and b points to the same object |
Lecture 14 2018/09/24
1 Motivation: Sometimes we want to maintain some state within a
function. For example, if we define a function called
withdraw(amount)
to withdraw a certain amount of money from
the bank, the function returns the remaining balance in your account. We
then want to maintain the balance within the function, which means if we
call the function multiple times with the same amount parameter, we
should have different returning values.
There are two different ways to tackle this problem:
- We can use the nonlocal statement:
1 | def withdraw_account(balance): |
- We can use the mutable objects:
1 | def withdraw_account(balance): |
2 If you use nonlocal statement for a variable, then it must be bound in the first non-local parent frame of the current frame.
3 Python pre-computes which frame contains which names before executing the function body. Within a function body, all instances of a name must refer to the same frame.
1 | def withdraw_account(balance): |
In the above example, if we omit the nonlocal statement, it will
cause a UnboundLocalError, because within the body of function withdraw,
the local variable balance
is referenced before
assignment.
4 Referential transparency: if an expression can be replaced by its value without changing the meaning of a program, then the expression is called referential transparent.
5 An example of nonlocal statement:
1 | def f(x): |
Lecture 15 2018/09/26
1 Iterators:
An container can provide and iterator that provides access to its elements in some order
Some related functions: 2.1. iter(iterable): returns an iterator 2.2. next(iterator): returns the next element in the iterator 2.3. list(iterator): list the unvisited elements in the iterator. If listed, then an iterator cannot be used again, otherwise an StopIterationError will be caused
Iterators are mutable objects
2 Dictionary iteration:
- The values, keys and items of a dictionary are all iterable. For Python version 3.6 and higher, the iteration order is the order by which items are added into the dictionary, for Python version 3.5 or lower, the iteration order is arbitrary.
- If the size of the dictionary is changed during iteration(you add something into the dictionary or you pop something during iteration), an RuntimeError will be caused. For lists, you can do whatever you want to the list during iteration.
1 | # If you want to remove keys of which the values are empty,the following way is wrong and causes an RuntimeError |
3 For iteration over iterables and iterators:
1 | for _ in iterable: # Iteration on iterables can be executed multiple times |
4 Built-in iterator functions:
Many operations on sequences return iterators that compute results lazily, lazy computation means result is only computed when needed
Example functions:
2.1 map(func, iterable)
2.2 filter(func, iterable)
2.3 zip(first_iter, second_iter)
2.4 reserved(seq)
5 Generators:
- Generators are special iterators that they are results of generator functions
- A generator function uses yield statements to replace the return statement, all yield statements can be executed and related values can be returned
- Yield from statement: returns al values in an iterable or an iterator,the following two ways are equal:
1 | for x in iterable: |
Lab 05
1 Q9: Riffle Shuffle
The familiar riffle shuffle of a deck of cards (or in our case, of a sequence of things) results in a new configuration of cards in which the top card is followed by the middle card, then by the second card, then the card after the middle, and so forth. Assuming the deck (sequence) contains an even number of cards, write a list comprehension that produces the shuffled sequence.
Hint: To write this as a single comprehension, you may find
the expression k%2
, which evaluates to 0 on even numbers
and 1 on odd numbers, to be useful. Consider how you can use the 0 or 1
returned by k%2
to alternatively access the beginning and
the middle of the list.
1 | def riffle(deck): |
My intuition: we first make a list of nested lists, each element in the out-layer list is a two-element list, then we reduce the out-layer list to one dimension,
1 | return [item for sublist in [[deck[k], deck[k + len(deck) // 2] for k in range(len(deck) // 2)] for item in sublist] |
Another solution following the hint: for odd position, the index
needs to plus len(deck) // 2
, for even position, the index
is just k
, so we simply use k % 2
to separate
the indices,
1 | return [deck[k + (k % 2) * (len(deck) // 2)] for k in range(len(deck))] |
Lab 06
1 Q4: Insert Items
Write a function which takes in a list lst
, an argument
entry
, and another argument elem
. This
function will check through each item present in lst
to see
if it is equivalent with entry
. Upon finding an equivalent
entry, the function should modify the list by placing elem
into the list right after the found entry. At the end of the function,
the modified list should be returned. See the doctests for examples on
how this function is utilized. Use list mutation to modify the original
list, no new lists should be created or returned.
Be careful in situations where the values passed into
entry
and elem
are equivalent, so as not to
create an infinitely long list while iterating through it. If
you find that your code is taking more than a few seconds to run, it is
most likely that the function is in a loop of inserting new values.
1 | def insert_items(lst, entry, elem): |
At first , I did not notice the restriction that no new lists were allowed to be created, so I wrote the following codes.
1 | def insert_items(lst, entry, elem): |
The main idea is that we can add a marker to identify whether the element locates originally in the list or is inserted into the list. Although this way does not meet the requirement of this question, I still find it interesting.
The following codes do meet the needs of this question:
1 | def insert_items(lst, entry, elem): |
Lecture 16 2018/09/28
1 Classes and objects:
- class is a template for objects that share same attributes and behaviors, the objects are class instances
- creating an instance of a class is also called instancing the class
- attributes associated with instances are also called instance attributed, fields, properties and instance variables, function attributes are also called methods
2 Dual roles of object in the object.method
expression:
- the object means the method is not in the global environment, but inside the local environment of a class
- the object is bound to the first argument
self
in the method
3 Functions and methods:
1 | type(Class_name.func_name(object, arguments)) # <class 'function'> |
Notice that when calling a method using a Class object, we need to
explicitly specify the object that is bound to the self
argument in the method, but when calling a method using an instance, we
don't need to do that.
4 Class attributes:
- Evaluation procedure of
<object>.<attribute>
:- we first evaluate the object
- then we look up the attribute in the current class, if it exists, return the value of that attribute
- if the attribute doesn't exist in the current class, we look up the attribute in parent classes, if it is found, return the value of that attribute
- if the attribute is a method instead, return the result value of that method
- attribute assignment
1 | instance.attribute = value |
Lecture 17 2018/10/01
1 Terminologies: parent class/ base class/ superclass/ subclass
2 Interfaces:
- Def: a collection of attributes and conditions on those attributes
- In Java, you need to explicitly declare an interface, however, in Python, Ruby and Go, an object with the appropriate name implements an interface already
3 Multiple inheritances:
- Order: from bottom to top, from left to right in the same layer(the left-to-right order is the same as the order written in the class arguments)
- The algorithm is called the C3 Resolution Ordering
4 Object-oriented design principles:
- Don't repeat yourself, use existing implementations
- Attributes that have been overridden are still accessible via class
objects:
Parent_class_name.attribute
- Look up attributes on instances whenever possible
Lab 07
1 Don't modify a list while iterating on it using for loop, it may result in wrong results, particularly, don't remove elements during iteration.
Reason: the for loop uses list indices to record the iteration process, however, deleting items in the list results in mismatch of the current list with the original indices.
1 | """ |
There are several ways to correct this:
- Use filter function:
1 | ls = list(filter(lambda x: x != 0, ls)) |
- Use list comprehension:
1 | ls = [x for x in ls if x != 0] |
- Iterate on the copy:
1 | for item in ls.copy(): |
Lecture 18 2018/10/03
1 Generic functions:
- Can accept values of multiple types
- Three implementing techniques: shared interfaces, type dispatching and type coercion
2 String conversion:
- Python has two ways of string representation:
- Human-interpretable text:
str(x)
- Python-interpretable expression:
repr(x)
- Human-interpretable text:
repr(object)
: returns the canonical string representation of the object- For most cases we have:
eval(repr(object)) == object
- For some cases like built-in functions the above relationship doesn't hold
- For most cases we have:
- Use string representations in user-defined classes:
- The
str(object)
invokesobject.__str__()
, it is used in theprint(object)
- The
repr(object)
invokesobject.__repr__()
, it is used in the interactive session - If we explicitly overwrite the
__str__()
and the__repr__()
in our class, then instances of our class have string representation
- The
3 Special Methods:
__init__()
: construct objects__str__()
: print objects__repr__()
: display objects in the interactive session__bool__()
: return bool values of objects,For instances of user-defined classes, the default
object.__bool__()
returnsTrue
We can overwrite the function to manipulate its behavior:
1
__bool__ = lambda self: self.__len__() == 0 # If the length of an object equals to zero, returns True
__call__()
: allows objects to behave like higher-order function
4 Three ways to implement generic function:
Shared Interfaces: use a collection of shared attributes and behaviors, interface is additive
Type Dispatching: check instance types, do different things for instances of different types, the
isinstance(object, type)
function is used hereType Coercion: we can deem subclass objects as superclass objects, or we can explicitly define a conversion function to transfer objects of one type into objects of another type
Lecture 19 2018/10/05
1 Efficiency:
Definition: the computational resources used by a representation or processes
Measurement: time and space
Space requirements: how memory is used, preserved and reclaimed
- All active environments with referenced values and frames are preserved by the interpreter
Efficiency can be greatly improved by memorization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
def memo(f):
cache = {}
def memoized(n):
if n not in cache:
cache[n] = f(n)
return cache[n]
return memoized
fib = memo(fib)
#Notice we can use dp to solve this:
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
p, q, r = 0, 0, 1
for _ in range(2, n + 1):
p, q = q, r
r = p + q
return r
2 Order of growth:
- Definition: let input scale be \(n\), let resource requirement b \(R(n)\), We say that \(R(n)\) as order of growth \(\Theta(f(n))\), written \(R(n) = \Theta(f(n))\), if there are positive constants \(k_1\), \(k_2\) such that \(k_1 * f(n) \leq R(n) \leq k_2 * f(n)\)
- Tips:
- Constants do not affect order of growth
- Base of logarithms does not affect order of growth
- When an inner computational process is repeated for each step in an outer process, then the order of growth of the entire process is a product of the number of steps in the outer and inner processes
- Common categories:
- \(\Theta(1)\): constant
- \(\Theta(logn)\): logarithm
- \(\Theta(n)\): linear
- \(\Theta(n^2)\): quadratic
- \(\Theta(b ^n)\): exponential
Lecture 20 2018/10/08
1 Linked List:
- Definition: first + rest of the list; the rest of the linked list is also a linked list;
- Empty linked list: no first element, no rest of the list
- A linked list is a sequence, it has finite length and supports element selection
2 Tree:
- Definition: a label + branches; each branch is also a tree
- Leaf: a tree is a leaf if it has no branches
Lecture 21 2018/10/10
1 Set:
Definition: unordered collection or elements, no duplication in a set
Printing order of a set may be different from the displaying order of a set
Set operations:
- union, intersection, isdisjoint, issubset, issuperset
- add, remove, discard, pop
- remove: return None, if the element is not in the set, KeyError is caused
- discard: return None, if the element is not in the set, KeyError is not caused
- pop: return the removed element, if the element is not in the set, KeyError is caused
- clear, update
Set implementations: let $m, n $ be the length or the set
- Unordered sequence: implemented by linked list:
- Contain operation complexity: \(\Theta(n)\)
- Intersect operation complexity: \(\Theta(n * m)\)
- Ordered sequence: implemented by linked list:
- Contain operation complexity: \(\Theta(n)\)
- Intersect operation complexity: \(\Theta(n + m)\)
- Binary search tree: implemented by tree:
- Contain operation complexity: \(\Theta(log_2n)\)
- Adjourn operation complexity: \(\Theta(1)\)
- Unordered sequence: implemented by linked list:
Lecture 22 2018/10/12
1 Binary Search Tree(BST):
Definition:
A leaf is a BST
Each node has at most two children, each children is a BST
For each node, the entries in the left child should be smaller than the label of the node
For each node, the entries in the right child should be larger than the label of the node
For adjourn operation, a BST is faster than an ordered linked list
Lecture 23 2018/10/15 & Lecture 24 2018/10/19
Lecture 23 is a review session, nothing interesting.
Lecture 24 is Alan Kay's presentation on the history of programming language and user interface design.
Lecture 25 2018/10/22
1 Interpreter: a program that carries out evaluation and execution procedures
2 Programming Language:
- PLs vary widely in syntactic structures, features and domain of application
- A powerful PL can exist without object system, higher-order function, assignment or even control structures
- The part of Scheme discussed in this class is limited into topics like expressions but not statements, symbolic computations and mutable values
3 Functional Programming
Low-level machine language cares about representation of data and control in terms of storage and primitive machine structures, higher-level language has means of combination and abstraction to apply in larger-scale organization of software systems
Scheme is a dialect of Lisp
Expressions:
- Expressions are bound with parentheses
- If
statement:
(if <predicate> <consequent> <alternative>)
. Scheme does not haveelif
statement, you need to use nested if statements.- For numeric comparison:
(> 2 3)
is okay
- For numeric comparison:
- Boolean values:
#t
forTrue
,#f
forFalse
- Logical operations, short-circuiting is applied:
(and <e1> ... <en>)
(or <e1> ... <en>)
(not <e>)
Definitions
- Definition of variable:
(define <var> <value>)
- Definition of function:
(define (<name> <formal parameters>) <body>)
- Call of function:
(<name> <arguments>)
- Lambda expression/ anonymous function:
(lambda (<formal parameters> <body>))
- Definition of variable:
Compound Values:
Built-in pairs:
(cons <value1> <value2>)
1
2
3
4
5
6(define x (cons 1 2))
; Anything followed by a ';' is a comment
; Take the first element of a pair
(car x) ; 1
; Take the second element of a pair
(cdr x) ; 2Built-in lists:
- The empty list
nil
or'()
1
2
3
4
5
6
7; You can construct a list from nested pairs
(cons 1
(cons 2
(cons 3
(cons 4 nil)))); (1 2 3 4)
; You can also use the list notation
(list 1 2 3 4); (1 2 3 4)- The empty list
Symbolic Data: use a quotation mark to denote symbolic data
1
2
3
4(define a 1)
(define b 2)
(cons a b); (1 2)
(cons 'a b); (a 2)
Lab 10
1 cond
expression
- Syntax:
(cond (<p1> <e1>) (<p2> <e2>) ... (<pn> <en>) [(else <else expression>)])
<p1> ... <pn>
are predicates that can be evaluated to be#t
or#f
,<e1> ... <en>
are expressions that could be executed- If
<p1> ... <pk-1>
are all#f
and<pk>
is#t
, then<ek>
is executed and thecond
expression terminates here
2 Selecting elements from a list:
- The
car
operator selects the first element in the list - The
cdr
operator selects all elements in the list but the first one, is returns a sublist
1 | (define x list(1 2 3 4)) |
Lecture 26 2018/10/24
1 Python interpreter handles errors by terminating immediately and
printing an error message.
2 Raise an expression is a technique for interrupting the normal flow
of execution in a program, signaling that some exceptional circumstance
has arisen, and returning directly to an enclosing part of a program
that was designated to react to this circumstance.
3 You can raise an exception with the raise
statement or
the assert
statement
- An exception is an object instance inherited from the
BaseException
class assert
statement raises an exception with theAssertionError
class- Syntax for raise statement:
raise Exception('Error information string')
- If an exception occurs, no further statements in the same block of
codes will be executed unless the exception is handled, the interpreter
will also print a stack backtree to indicate the source of the
exception
4 Handling an exception:
- Syntax
1 | try: |
5 Exception objects:
- Exception objects can have attributes
- You can build a class that inherits the Exception class and
customize instance behaviors
Lecture 27 2018/10/26
1 The Scheme-Syntax Calculator(aka Calculator)
- It has two legal expressions: numbers that are self-evaluating and call expressions that are Pair instances representing Scheme lists
- It has an interpreter that takes in text inputs and outputs
Calculator expressions
2 Parsing Expressions:
- Definition: generate expression trees from raw text input
- Construction of the parser: lexical parser(aka tokenizer) and the
syntactic analyzer
- Lexical parser: partitions one text input expression into tokens
- Syntactic parser: construct an expression tree from tokens, it is a
tree-recursive process
3 The REPL
- The interactive interface of an interpreter is a REPL(read-eval-print loop)
- The implementation of a REPL can be independent of the interpreter it uses
- The process of the REPL:
- Print a prompt to denote the REPL is ready
- Read the text input
- Parse the text input into expression
- Evaluate the expression
- If error occurs, report the error, otherwise print the expression value and return to Step 1
Lecture 28 2018/10/29
1 Scheme interpreter structure:
- parsing
- evaluation
- procedure application
- eval/apply recursion
2 Logical special forms: only evaluate some sub-expressions
If
expressionAnd
oror
Cond
expression
3 Quotation:
- Definition: the
quote
special form evaluates to the quoted expression, which is not evaluated - Syntax:
(quote <expression>)
'<expression>
is shorthand for(quote <expression>)
4 Lambda expression:
- Lambda expressions evaluate to user-defined procedures
- Syntax
(lambda (<formal-parameters>) <body>)
5 Frames and environments:
- A frame represents an environment by having a parent frame
- Frames are Python instances with methods
lookup
anddefine
6 Define expressions:
- Define binds a symbol to a value in the first frame of the current environment
- Procedure definition is shorthand of define with a lambda expression
- To apply a user-defined procedure, create a new frame in which
formal parameters are bound to argument values, whose parent is the
env
of the procedure
Lecture 29 2018/10/31
1 Dynamic scope:
The way names are looked up in Python and Scheme is called lexical scope(static scope)
- Lexical scope: the parent of a frame is the environment in which a procedure is defined
- Dynamic scope: the parent of a frame is the environment in which a procedure is called
2 Tail recursion:
Functional programming
- Properties:
- All functions are pure functions
- No reassignment and no mutable data types
- Name-value bindings are permanent
- Advantage:
- Value of expression is independent of order of sub-expression evaluation, sub-expression can be evaluated in parallel or in demand(lazily)
- Referential transparency: expression value does not change if sub-expression is replaced by its value
- Properties:
Iteration: Implementation of Scheme are required to be properly tail-recursive, this allows the execution of an iterative computation in constant space (\(\Theta(1)\)), even if the iterative computation is described by a syntactically recursive procedure
Tail calls:
- A procedure call that has not returned is active, some procedure calls are tail calls. A scheme interpreter should have an unbounded number of tail calls using only a constant amount of space
- A tail call is a call expression in a tail context, for example:
- The last body sub-expression in a lambda expression
- Sub-expression 2 & sub-expression in a tail context
if
expression, namely, the consequent expression and the alternative expression - All non-predicate sub-expressions in a tail context
cond
expression - The last sub-expression in a tail context
and
oror
- The last sub-expression in a tail context
begin
- The return value of the tail call should be the return value of the current procedure call, in this way, tail calls don't increase environment size
Lecture 30 2018/11/02
1 A scheme expression is a scheme list:
- A scheme expression is a scheme list
- Expression: primitive expression and combination
- Eval a scheme list of expressions return the value of the expression
2 Macros:
- A macro is an operation performed on source code before evaluation that allows you to define special forms
- Scheme has a define-macro special form that defines a source code transformation
- Evaluation procedure of a macro call expression:
- Evaluate the operator sub-expression, which evaluates to a macro
- Call the macro procedure on the operand expressions without evaluating them first
3 Quasi-quotation
1 | ;quotation |
Lecture 31 2018/11/05
1 Sequence operations: map, filter, reduce
2 Streams are lazy Scheme lists:
- A stream is a list, but the rest of the list is computed only when needed. If there is error in the rest part, there will be no error report before it is actually computed
- Syntax:
cons-stream
,car
,cdr-stream
3 Infinite-streams:
- An integer stream is a stream of consecutive integer numbers, the rest of the stream is not yet computed when created
4 Higher-order functions:
cons
->cons-stream
,car
->car
,cdr
->cdr-stream
- A stream of primes, the "Sieve of Eratosthenes:
- A list: \(1, 2, ..., n\)
- Let \(p = 2\), mark all \(k * p\) in the list
- Find the smallest unmarked number in the list, if it exists, let \(p\) equal to it and repeat step 2, otherwise stop the procedure
- When the procedure is stopped, the unmarked numbers in the list are all primes
5 Promises:
- A promise is an expression along with an environment in which to evaluate it
- Delaying an expression creates a promise to evaluate it later in the current environment
- Forcing a promise returns its value in the environment in which it is defined
Lab 11
1 Two programing languages for an interpreter:
- The language being interpreted
- The underlying implementation language
For Python interpreter, the first is of course Python itself. The
most common Python interpreter CPython
is implemented using
C, another popular Python interpreter PyPy
is written in
Python.
2 The REPL
Read: The interpreter takes the user input (a string) and passes it through a lexer and parser
- The lexer turns the user input string into atomic pieces (tokens) that are like "words" of the implemented language
- The parser takes the tokens and organizes them into data structures that the underlying language can understand
Eval: Mutual recursion between eval and apply evaluate the expression to obtain a value
- Eval takes an expression and evaluates it according to the rules of the language. Evaluating a call expression involves calling apply to apply an evaluated operator to its evaluated operands
- Apply takes an evaluated operator, i.e., a function, and applies it to the call expression's arguments. Apply may call eval to do more work in the body of the function, so eval and apply are mutually recursive
Print: Display the result of evaluating the user input
1 | +-------------------------------- Loop -----------+ |
§ 4.1 - § 4.2
1 Introduction:
- A sequence can be represented without each element being stored explicitly in the memory of the computer. The elements can be computed on demand(lazily).
- For example, the range container is computed lazily.
2 Iterators: provide sequential access to values
- Usage:
iter(object)
,next(iterator)
,StopIteration
exception,iter(iterator)
returns itself, not an copy of the iterator - Comparison:
- Range: arbitrary element accessible(this is called random access)
- Iterator: only the next element is accessible(this is called the sequential access), the class of underlying sequential datasets are broader
3 Iterables: any value that can produce an iterator is called an
iterable value, i.e., anything that can be passed in the
iter
function is an iterable
- For example, tuples, strings, lists iterators are all iterables
- If a dictionary adds, removes or modifies its keys, then all existing iterators become invalid
- If an object has
__iter__
method, then it is an iterable
4 Streams: apart from iterator, stream is another way to represent sequential data implicitly
- A stream is a lazily computed list
- Like a linked list, a stream has first part and rest part, the rest part is also a stream, but the rest part is computed lazily
- Higher-order functions like
map
,filter
also apply to streams
Lecture 32 2018/11/07
1 Databases:
- A database is a collection of data. A database management system(DBMS) is a software which can be used to manage the data by storing it onto the database and by retrieving and transforming it from the database. Each value stored is called a record, records with similar structure are grouped into tables.
- A query is a statement in a query language, and it is used for retrieving and transforming data values. The most common query language now is the structured query language(SQL). SQL is ANSI and ISO standard, but DBMS's implement custom variants
- SQL is an example of declarative language where statements do not
describe computations directly but describe the result of computations.
The database systems use a query interpreter to design and perform a
computational process to get the result
- Declarative language: a program is a description of the desired result, the interpreter figures out how to generate the result, such as SQL and Prolog
- Imperative language: a program is a description of computational processes, the interpreter carries out execution rules, such as Python and Scheme
2 Tables:
- A table is also called a relation, the columns in a table are named and typed, each row in a table is a record
- Create a table using the
select
statement
1 | -- SQlite Syntax |
1 | -- MySQL Syntax |
3 Select Statements:
- A select statement defines a new table either by listing values in a
single row or by projecting an existing table using
from
clause:select [column description] from [existing table name]
- Details:
- The
[column description]
can be an arithmetic expression on the column - You can rename the the column in the expression:
select [column description] as [new column name]
- Example:
select (distance - 40) as dist_diff from city
- The
where
clause: conditionorder by
clause: ordering by a specific column, default order is ascending, you can alter to descending
1 | select <column name> from <table name> order by <column name> -- Ascending by default |
HW 08
1 How many times scheme_eval
and
scheme_apply
are called in the Scheme interpreter.
1 | (+ 2 3 (- 4 5) 1) |
Lecture 33 2018/11/09
1 Joining tables:
- The type of join used in this class is cross join, since a table with \(m\) rows joins a table with \(n\) rows results in a Cartesian product table with \(m * n\) rows
- Aliases and dot expressions:
- Join with overlapping columns:
select A.x, B.y from A, B where A.n = B.m
- If the column names are same in two two tables, use
table_name.column_name
to distinguish
- Join with overlapping columns:
2 Numerical expressions: expressions can contain function calls and arithmetic operators
- Combine values:
+
,-
,*
,/
,%
,and
,or
- Transform values:
abs
,round
,not
,-
- Compare values:
>
,>=
,<
,<=
,<>
,!=
,=
3 String expressions:
- String concatenation using
||
:'hello, ' || 'world'
=>hello, world
substr(<string name>, <start index>, <length>)
: notice the index starts from 1 not 0 in SQLinstr(<string name>, <substring name>)
: returns the index by which the substring appears first in the string
Lecture 34 2018/10/14
1 Aggregation and grouping:
- Aggregate functions:
max
,min
,sum
,count
- In the class, the professor says in the situation where we want to
count unique values in a column, we should use
count(distinct <column name>)
instead ofcount(*)
count(1)
: ignores all columns in the table and counts a new column with all elements equal to 1, doesn't ignorenull
value, the result is the number of rows in the tablecount(*)
: takes all columns in the table into consideration, doesn't ignorenull
values, the result is the number of rows in the tablecount(<column name>)
: takes only one column into consideration, ignoresnull
values in the column- if the
is the primary key in the table , then count(<column name>)
is the fastest, otherwisecount(1)
is the fastest
- In the class, the professor says in the situation where we want to
count unique values in a column, we should use
- You can put aggregate functions and ordinary columns in one
select
statement, but the result may not be meaningful. - Grouping records:
group by
,having
group by <column name>
: partition the table into groups using a specific group or groupshaving <condition>
: filter in each group- Firstly you use
where
to filter the whole table, then you usegroup by
to partition the filtered table into groups, after thathaving
is executed on each group to do further filtration - Aggregate function is executed for each group if there is a
group by
clause
2 Select grammar:
- You can refer to this link :https://sqlite.org/lang_select.html
- Order of execution:
with(comma-separated tables)
=>select(distinct/all/ ...)
=>from(table/ sub-query)
=>where(condition)
=>group by(column names)
=>having(column names)
=>values(comma-separated values)
=>order by(column names)
=>limit(number)
=>offset(number)
- You can use values to inject values into a table, you can also use
select to inject values, but there is a restriction called
SQLITE_LIMIT_COMPOUND_SELECT
limiting the maximum number of terms in a select statement
- You can use values to inject values into a table, you can also use
select to inject values, but there is a restriction called
Lecture 35 2018/11/19
1 Create tables and drop table:
Create statement usage:
create table if not exists <table name> (column definitions);
- Column definition:
<column name> <column type> <column constraints>
;like <not null>
,<primary key>
,<unique>
,<default default-values>
- Check tables in the current database:
1
2
3
4
5-- SQlite3
.tables
-- MySQL
show tables;- Show table definition:
1
2
3
4-- SQlite3
.schema <table name>
-- MySQL
show create table <table name>;- Column definition:
Drop statement usage:
drop it exists <table name>
2 Modify tables
- Insert statement usage:
insert <replace> into table (column names) values (expressions)
- If insert into all columns, the
(column names)
can be omitted - Insert into multiple rows:
values (row1), (row2), ..., (rownn)
- If insert into all columns, the
- Update statement usage:
update table <table name> set <column name> = <expression> where <condition>
- Set the intersection of rows specified in where clause and column name clause equal to expression
- Delete statement usage:
delete from table <table name> where <condition>
- If there is no where clause, the all rows in the table are deleted. Notice in this situation, the records in the table are deleted but the table itself still exists, which is different from the drop statement where the whole table together with all records is deleted
3 Python and SQL
1 | import sqlite3 |
4 SQL injection attack: it's better to use template and Python object to avoid SQL injection attack
Lecture 36 2018/11/26
1 Language ambiguity: natural languages have ambiguity, say the part of speech can be ambiguous, but there is no ambiguity in a programming language
2 Syntax trees: a tree represents a phrase, a leaf represents a word
3 Context-free grammar rules:
- A grammar rule describes how a tag can be expanded into a sequence of tags or words
- Examples:
(N, cow)
,(V, cow)
, the former one refers to the noun cow, while the latter one refers to the verb cow
4 Parsing: a parser takes in sentences and outputs syntax structures, a parser is similar to a syntax generator
5 Learning: since not all trees are equally common, you can score a tree using relative frequencies
6 Translation: natural language parsing can be used in translation, which may include syntactic reordering
Lecture 37 2018/11/28
1 Trees
- Tree-structured data: label + branches -> a tree
- You can define ADT using functions alone, or you can use a class to do so
- BTree: binary tree
2 Tree processing steps:
- Understand the problems
- Draw a figure for the data structure and work through some examples
- Code recursively
3 How to design programs: from «How to design programs» at here
- From problem analysis to data definitions
- Write function documentations: signature, purpose statement, header
- Functional examples
- Function templates
- Function definition
- Testing