NOTES while going through DCIC

A Data-Centric Introduction to Computing

1. → 2. Intro

Values

  1. Software is made to run and be maintenained and read by others.
  2. Programmers are responsible for their software.
  3. Programms should be predictable in what they do.

Performance, reliability and predictability

Perspective on data

In particular, the structure of data serve as a point of departure for thinking about and achieving some of the values above—performance, reliability, and predictability—using the many tools of computer science.

data-centric curriculum as :

data centric = data science + data structures

In the order of :

  1. ideas from data science
  2. classical ideas from data structures
  3. the rest from computer science

notional machines: abstractions of program behavior meant to help students understand how programs work

[...] programs are not only objects that run, but also objects that we reason about.

socially-responsible computing

3.1 Getting Started

flag observations:

questions about flags and how to make them:

questions i MIGHT have about the flag problem :

Do I need to be able to draw these images by hand?

Will we be able to generate different sized flags from the same code?

What if we have a non-rectangular flag?

We might want to compute the heights of the stripes from the overall flag dimensions (we’ll write programs using numbers)

We need a way to describe colors to our program (we’ll learn strings)

We need a way to creat

3.1.2 Numbers

A software or web-application in which you write and run programs is called a programming environment.

We will use the term expression to refer a computation written in a format that Pyret can understand and evaluate to an answer.

Expression: a computation written in the formal notation of a programming language Value: an expression that can’t be computed further (it is its own result) Program: a sequence of expressions that you want to run

Note: a literal is a value stated in the program itself. It has not bee computed.

functions and expression composing

3.1.6.1

“functions” is the term more commonly used in computing, whereas your math classes likely used “operations”

Every function produces a value, which can be used as input to another function. We build up expressions by using values and the outputs of functions as inputs to other functions. This idea of using the output of one function as input to another is known as composition.

Types and Contracts

3.1.7.1

In programming, values are organized into types (e.g., number, string, image). These types are used in turn to describe what kind of inputs and results (a.k.a., outputs) a function works with.

the term contract refers to the required types of inputs and promised types of outputs when using a specific function.

Whenever you compose smaller expressions into more complex expressions, the types produced by the smaller expressions have to match the types required by the function you are using to compose them.

A contract also summarizes how many inputs a function expects.

Types of errors in programming

In programming, we use the term syntax to refer to the rules of writing proper expressions (we explicitly didn’t say “rules of punctuation” because the rules go beyond what you think of as punctuation, but that’s a fair place to start).

3.1.7.2

"evaluated" errors, for type errors or incorrect number of inputs.

space around operators close parentheses space after comma quotes around strings

Every programming language comes with documentation, which is where you find out the various operations and functions that are available, and your options for configuring their parameters.

3.2 Naming Values

unkown words at this point when typing puppy :

“unbound identifier” when an expression contains a name that has not been associated with (or bound to) a value.

So far, we've seen words used for :

Main differences:

Expressions vs Statements

Definitions and expressions are two useful aspects of programs, each with their own role. Definitions tell Pyret to associate names with values. Expressions tell Pyret to perform a computation and return the result.

In programming, we distinguish expressions, which yield values, from statements, which don’t yield values but instead give some other kind of instruction to the language. So far, definitions are the only kinds of statements we’ve seen.

The program directory is an essential part of how programs evaluate.

Might need to delete later, if substitution is defined


How does Pyret evaluate (width * 3)? Since width is a word (not a string), Pyret looks up its value in the directory. Pyret substitutes that value for the name in the expression, resulting in 30 * 3, which then evaluates to 90. After running these two expressions, the directory looks like:

Directory

width →  30

height →  90

Note that the entry for height in the directory has the result of width * 3, not the expression. This will become important as we use named values to prevent us from doing the same computation more than once.

In practice, programmers don’t name every individual image or expression result when creating more complex expressions. They name ones that will get used more than once, or ones that have particular significance for understanding their program. We’ll have more to say about naming as our programs get more complicated.

3.3 From Repeated Expressions to Functions

To make this program work, then, we need the ability to add our own operators (henceforth called functions) to Pyret.

Functions

In programming, a function takes one or more (configuration) parameters and uses them to produce a result.

Strategy: Creating Functions From Expressions

Programmers often use angle brackets to say “replace with something appropriate”; the brackets themselves aren’t part of the notation.

When we provide values for the parameters of a function to get a result, we say that we are calling the function. We use the term call for expressions of this form.

Substitution restores that expression, while still allowing the programmer to write the shorthand (func name).

annotating the parameters with information about the expected type of value for each parameter.

 fun three-stripe-flag(top :: String,
      middle :: String,
      bottom :: String):
  frame(
    above(rectangle(120, 30, "solid", top),
      above(rectangle(120, 30, "solid", middle),
        rectangle(120, 30, "solid", bottom))))
end

Is this setting a type ?

It is also common practice to add a type annotation that captures the type of the function’s output. That annotation goes after the list of parameters:

Note that all of these type annotations are optional. Pyret will run your program whether or not you include them. You can put type annotations on some parameters and not others; you can include the output type but not any of the parameter types. Different programming languages have different rules about types.

We will think of types as playing two roles: giving Pyret information that it can use to focus error messages more accurately, and guiding human readers of programs as to the proper use of user-defined functions.

Programmers also annotate a function with a docstring, a short, human-language description of what the function does.

Python has docstrigs, java has javadoc, go has godoc, etc.

the DRY principle, where DRY means "don’t repeat yourself".

Writing good code annotation is hard. Try to be as concise and straight to the point as possible.

In each of the functions above, we’ve started with some examples of what we wanted to compute, generalized from there to a generic formula, turned this into a function, and then used the function in place of the original expressions.

Keep examples to use as tests.

Software Evolves. Over time, any program that has any use will change and grow, and as a result may end up producing different values than it did initially.

Therefore, it’s always useful to keep those examples around for future reference, so you can immediately be alerted if the function deviates from the examples it was supposed to generalize.

In fact, this is so valuable in professional software development that good programmers always write down large collections of examples—called tests—to make sure their programs are behaving as they expect.

For our purposes, we are writing examples as part of the process of making sure we understand the problem. It’s always a good idea to make sure you understand the question before you start writing code to solve a problem. Examples are a nice intermediate point: you can sketch out the relevant computation on concrete values first, then worry about turning it into a function. If you can’t write the examples, chances are you won’t be able to write the function either. Examples break down the programming process into smaller, manageable steps

we also want to include special yet valid cases that the function might have to handle, such as an empty message.

Always create the test first with your expected returned values, before running the function itself. Don't use the function to provide the returned values, as this might poison your tests with erroneous examples, if you borked the function.

3.4 Conditionals and Booleans

when choosing examples, use edge cases to fine-tune tests;

proposed examples from the course follow this strategy: define the boundary, just over the boundary, both natural and real(decimal) numbers floats and ints, and use both types of static value that is being used in the computation.

add-shipping expression is different from other functions used so far in that there are two computed values that appear only inside the function, and change based on the input value.

This calls for being able to ask questions about inputs within our programs.

Conditionals: Computations with Decisions

new expression: if expression (or statement) that can ask a questions that produce true or false (Booleans).

else marks the false case(in the example)

end instructs pyret that we're stopping the interrogation

Booleans

The values true and false

names after George Boole

Many built-in functions return boolean

x == x

== check if two values are equal

Strings are case-sensitive! Compare strings to determine their alphabetical order → this means that a comes before b, thus a < b; a is "smaller than b"

alphabetical order = ascending order

With strings, keep in mind of ASCII and what's outside of ASCII. → Falsehoods programmers believe about languages

Notably, ordering depends on location (geography).

In general, you can compare almost any two values for equality.

There are specific operator to compare values of a specific kind (or type?) Those operators are only accept the type, and only the type. It returns a useful error because it does type-checking(?) not work on another type. Free debugging!!

Then, Pyret will signal an error if you go wrong, instead of blindly returning an answer (false) which lets your program continue to compute a nonsensical value.

In fact, just about every kind of data will have some Boolean-valued operators to enable comparisons.

Combining Booleans

Make decisions on more than one boolean.

Pyret offers three main operations: and, or, and not.

Explain why numbers and strings are not good ways to express the answer to a true/false question.

well, I'm guessing its something to do with how operators determines if something is true or false? I believe that numbers or strings don't necessarily describe the complexity of the question? Or maybe they might not have any relation to the question and thus are not easily understandable?

Asking multiple questions

else if → evaluates each question expression in order, until it returns the result in case the answer that returns true.

In the exercise, there is a implicit between expression: (x >= 30) after a if statement for (x <= 10) but there is also the if (x > 10) and (x <= 30) to explicit the between 10 and 30

The second option improves readability, especially for future readers. It makes the if statement error proof against an earlier question that would be erroneous, since we are evaluating a specific range. It would also hold up much better in case of modifications of the earlier statement too.

Exercise:

fun show-ad(haircolor :: String, age :: Number) -> Boolean:
  doc: "show an ad if the haircolor is pink/purple and the age is between 9-18"
  if ((haircolor == "pink") or (haircolor == "purple")) and ((age >= 9) and (age <= 18)):
    true
  else:
    false
  end
where: 
  show-ad("purple",9) is true
  show-ad("purple",18) is true
  show-ad("purple",7) is false
  show-ad("green",9) is false
  show-ad("pink",18) is true
  show-ad("pink",19) is false
end  

Responsible:

Assumptions about users get encoded in even the simplest functions.

What data about individuals should be used to represent them for processing by programs and what stereotypes might those data encode. [...] Decisions based on those predictions can be inaccurate and hence harmful.

Function Composition

two examples:

fun1(fun2 (arg), arg)

and

output_fun2 = fun2(arg) 
fun1(output_fun2, arg)

The second option commits the output_fun2 to "memory" as an extra step and is more verbose, while the first option directly substitutes the fun2 output as an argument for fun1.

Two styles, each with pros and cons. There seems to be an importance in how programs evaluate. My intuition would be that it's the programming language who is responsible.

Directory entries made within a function are local (private) to the function body

It's the whole global var vs var declared inside a function as in local var.


For some reason, the course material keeps mentioning a directory that is created for each program, where data values are store. Is this a reference to memory and memory management?


Nested Conditionals

↑ Back to the top