Today - function call, return, start String data type

• Data flows between functions
• How do functions exchange data?
• Function: black box model
data in: parameters
data out: return
• Today 1 function at a time, later build whole picture

## Theme: "Black Box" Function Model

Black-box model of a function: function runs with parameters as input, returns a value. All the complexity of its code is sealed inside. Calling it is relatively simple - provide parameters, get back an answer. ## 4 Functions Today

(had a tech problem in lecture, now fixed)

## 1. Parameters = Input

• Parameters are the inputs of a function
• Each parameter is like a variable with a value already in it
• Where does the value come from...
• (a) (non-answer) Not our concern how, but there's a value in there
• (b) (real answer) The caller code places a value between the parens when calling the function, and this is the value of each parameter

## 2. `return` yields the result of a function

Inside the body of a function, like this:

```def foo(n):
...
return n * 10
...
```
• Return exits the run of foo() immediately
• The run continues at the caller code
• The value, e.g. `n * 10` is the return value of the foo() call

Suppose the caller code looks like

```   # in caller function
a = foo(6)
b = foo(7)
```
• The return line within foo() causes 60 to be returned by `foo(6)`
essentially `a = 60`
• This is called the "return value" of the function
• The return value of `foo(7)` is 70, assigned to b

## Example 1. "Winnings1" Function

• Say we work for the CA lottery
• Writing a function for a game
• Rules: input is an int score 0..10 inclusive
• Output:
score 5 or more, winnings = score * 12
score 4 or less, winnings = score * 10
• We could make a table showing output for each input
• Writing code, we need to be organized thinking about the cases and their outputs
• Code needs to work for all cases, i.e. to be general
• Later: we'll see how to put in formal tests that the code is getting the right output for each input
```in out
0  0
1  10
2  20
3  30
4  40
5  60
6  72
7  84
8  96
9  108
10 120
```

## Winnings1 code

• This code works
• `score` parameter comes in
• if/logic to check for score >= 5
• return is used twice
• "pick off" of cases
• So past the first if.. what is true of score?
• We know score < 5
```def winnings1(score):
"""Compute winnings1 described above."""
if score >= 5:
return score * 12

# Run gets here: we know score < 5
return score * 10
```

## Winnings if-return Case Strategy

• The function needs to handle all input cases
• If-logic detects one case .. returns the answer
• The return exits the function immediately with a return value
• Lines below have if-return logic for other cases
• Code is "picking off" the cases one by one
• As we get past if-checks .. input must be a remaining case
• Analogy: coin sorting machine
coin rolls past holes, each a bit larger
dime, penny, nickel, quarter
a dime never makes it to the quarter-hole
The dime is picked-off earlier
• Bug demo: change <= to <

## Return None Default

• Demo: cut out the code, just have a "pass"
• If a function does not run a return, `None` is default function result
• If you see `None` in your output - maybe you forgot the return
• Or your if-logic for some case doesn't return

## Example 2. Winnings2 Example

Now there's 3 cases

Move the cutoffs around a little.

Winnings2: Input score is in the range 0..10 inclusive. Bonus! if score is 10 exactly, winnings is score * 15. If score is between 4 and 9 inclusive, winnings is score * 12. if score is 3 or less, winnings is score * 10.

## Winnings2 Example

Now there's 3 cases; the cutoff numbers are different. Write a series of if-return to pick off the cases.

Winnings2: Input score is in the range 0..10 inclusive. Bonus! if score is 10 exactly, winnings is score * 15. If score is between 4 and 9 inclusive, winnings is score * 12. if score is 3 or less, winnings is score * 10.

Solution:

```def winnings2(score):
"""Compute winnings2 described above."""
if score == 10:
return score * 15

if score >= 4:  # score <= 9 is implicit
return score * 12

# All score >= 4 cases have been picked off.
# so score < 4 here.
return score * 10
```

## Example 3. Lottery Scratcher Example

An extra example for practice.

Lottery scratcher game: Have three icons called a, b, and c. Each is in the range 0..10 inclusive. If all three are the same, winnings is 100. Otherwise if 2 are the same, winnings is 50. Otherwise winnings is 0. Use and/or/== to make the tests.

```def scratcher(a, b, c):
"""Return scratcher winnings described above."""
pass
```

Solution:

```def scratcher(a, b, c):
"""Return scratcher winnings described above."""
# 1. All 3 the same
if a == b and b == c:
return 100

# 2. Pair the same (3 same picked off above)
if a == b or b == c or a == c:
return 50

# 3. Run gets to here, nothing matched
return 0
```

## Interpreter

• Python interpreter - runs the python code
• You can type at it directly - super handy
• Try out little ideas, see what they do
• Interpreter has `>>>` prompt
• You type an expression
• It evaluates, prints result
• How to get the interpreter:
• 1. Use "console" tab in PyCharm lower left
• 2. Use "Interpreter" button at bottom of parlante.org pages
```>>> 1 + 1  # you type this
2          # Python prints this
```

## Strings

• Super common data type
• Sequence of 'characters' or 'chars'
• data e.g. urls, paragraphs
• In Python code a string "literal" is written on one line within single quote marks ' (prefer)
• Double quote also works "
'hello'
"hello"

## len() function

• Returns number of chars in string
• Works on other collections too
• Not noun.verb style
```>>> len('Hello')
5
>>> len('x')
1
>>> len('')
0
```

## Empty String ''

• The "empty" string is just the string with 0 chars
• Written like: '' or ""
• This is a valid string, len 0
• A common case to think about with string algorithms
• Does the code need to work on the empty string?
• Probably yes.

## Zero Based Indexing - Super Common

• Everything in computers uses this zero-based indexing scheme
• Characters in strings are "indexed" from 0
• e.g. 'Python'
```Python
012345
```
• Index number addresses each char in the string
• Foreshadow: we'll use index numbers, [ ], and len()...
For many many linear type data arrangements
But for today strings ## String Square Bracket Index

• Use square bracket to access a char by index number
• Valid index numbers: 0..len-1
• Get out of bounds error
```>>> s = 'Python'
>>> s
'P'
>>> s
'y'
>>> s
'o'
>>> s
'n'
>>> s
IndexError: string index out of range
```

## String Immutable

• A string in memory is not editable
• "Immutable" is official the official term
• e.g. square bracket cannot change
```>>> s = 'Python'
'P'
>>> s = 'X'  # write not ok
TypeError: 'str' object does not support item assignment
```

## String +

• Plus operator `+` combines 2 strings to make a bigger string
aka "concatenate"
• Uses new memory
• Does not change original string
```>>> a = 'Hello'
>>> b = 'there'
>>> a + b
'Hellothere'
>>> a
'Hello'
```

## String + Add At End Pattern

• Use + to add at the end of a string, yielding a new bigger string
• Use like this to grow the string s: `s = s + 'xxx'`
• The += operator works here too
• The individual strings are still immutable -
We are constructing a new, bigger string with each step
```>>> a = 'Hi'
>>> a = a + '!'
>>> a
'Hi!'
>>> a = a + '!'
>>> a
'Hi!!'
>>> a = a + '!'
>>> a
'Hi!!!'
```

## Loop Over All Chars in a String

• How to loop over all chars in string s
• Use index numbers: 0 .. len-1
• Use range function to make the numbers, 0, 1, 2, ... len(s)-1
• Typical to use "i" as variable in loop
```for i in range(len(s)):
# access s[i] in here
```

## Example 4, double_char

• Start result as empty string
• Loop over each char in string
• For each char: use + to add it at the end of the result

Solution code:

```def doublechar(s):
"""Return doublechar as described above."""
result = ''
for i in range(len(s)):
result = result + s[i] + s[i]
return result
```