forked from the-learning-collective/tlcjs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
03_functions.txt
516 lines (440 loc) · 18.2 KB
/
03_functions.txt
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
:chap_num: 3
:prev_link: 02_program_structure
:next_link: 04_data
= Functions =
////
Notes:
* focus is on DESIGNING FUNCTIONS
* mantra: "shape of the data hews as closely as possible to shape of the world, shape of the code follows shape of data"
* a function is a transformation from one piece of data to another
* a big computation is a series of transformations from one piece of data to another
* input/output, consuming/producing
* information
- The number (piece of data) 5 could represent a baseball player's number, a child's age, the number of boroughs in NYC (things in the world)
- The string (piece of data) "Austin" could represent city, movie character, TLC member (things in the world)
- The person Austin Guest (thing in the world) could be represented by: 35, "Austin Guest", "[email protected]", "355 Broadway, Paris, TX" (pieces of data)
# Actual recipe:
1. Describe information in terms of data (define types)
2. Signature (type of the function)
3. Purpose Statement (what it does -- in human language!)
4. Function Header (fn name, argument names, brackets, stub return value)
5. Examples === Tests (run them, watch them fail)
6. Mechanical Template (exapand template for every argument based on type)
7. Now you may write the body! (run tests until they pass!)
Problem:
- rent space for bake sale (either fixed amount $15 or 10% share of sale)
- selling lemonade (25c) and cookies (50c)
- function should take in the number of cookies and cups of lemonade sold, and whether you want to do a fixed price.
////
In the previous chapter, we were introduced to how to write
functions. We saw some simple ones that manipulated numbers, like
`square`:
[source,javascript]
----
function square(x) {
return x * x;
}
----
And we defined functions that produced images that we then used to
construct animations. In this chapter, we are going to take a much
deeper dive into functions, and focus on how we _design_ functions in
order to solve problems. On the way, we'll explore programming in a
more thorough way, and develop some concrete tools that we can use to
approach problems.
== Designing Functions ==
When we introduced functions in the last chapter, we described them
(first) as a way to produce values that differed. But, there are other
ways of thinking about them. Functions can also reduce duplication (as we saw in
the example with drawing the frames of the bike motion). In this
chapter we want discuss the process of actually creating them, as a
creative _design_ process.
Fundamentally, a function is a way of transforming one piece of
information into another. In the example of `square` above, the first
piece of information (or the _input_) was a number, and the second (or
the _output_) was that number multiplied by itself. This relationship
(between a number and it multiplied by itself) matches what we think
of as _squaring_, so the name matches it well.
All programming is about similar transformations. And, it is mostly
about the structure (ie, what it looks like, what it contains) of that
information. Up until now, we have introduced different types of data
that we can represent with javascript (like numbers, strings, images,
etc) but haven't talked much about how we choose what representation
to use for a given task. It may seem, up until now, that there isn't
really any choice - if we are dealing with a number, we represent it
as a number, and so on. But, we are usually writing programs about
bigger ideas than just numbers or strings.
For example, let's say we wanted to represent our friend "Austin" in
our program. How should we do it? We could use the string `"Austin"`,
but later on, it's possible that we would want to represent another
"Austin" in our program, and that might be problematic. Or we might
realize that we want to send an email to them, so maybe we should
represent them by the string "[email protected]", which is their
(fictional, at least for now!) email address. An email address seems
more unique, but what if "Austin" changes their email? Perhaps we
should just represent "Austin", and any other people we have to deal
with in our program, by (unique) numbers (for example, "Austin" could
be `42`), and if we need their email (or one of their emails) we can
store that somewhere else, connected to `42`, our unique number for
"Austin".
Of course, we have the same problem in the opposite direction - if we
have the number 42, what does it represent? It could be the number of
members of TLC, it could be the age of someone, it could be someones
favorite number, and any number of other things.
Figuring out what data exist in our program, and how it relates to the
(real) problem we are trying to solve is a critical part of
programming, and more concretely, designing functions, because all
functions have input and output data.
== Design Recipe ==
The first step in the _Design Recipe_ (which is what this chapter is
teaching) is to figure out what data you need to solve your
problem. You should describe what information you have, and how you
will represent it in the program. This should be in comments, as it is
crucial information both for yourself and anyone else who will see
this later.
As an example problem (which we'll follow through the whole recipe),
we want to figure out how a hypothetical bake sale is going to work.
We want to raise money by making lemonade and cookies (which we sell
at 25c/cup and 50c/cookie), but unfortunately, the only place we have
to sell them is run by a capitalist who makes us pay to use the
space. They have two options we can choose between:
- We pay them $50 to use the space, regardless of how much lemonade or
- How many cookies we sell. We pay them 10% of the total amount that
we make
The function we'll construct will take as input whether we want to pay
the flat $50 fee, and how many cups of lemonade / cookies we
sell. It'll give back our total profit.
Thinking about the type of information that we need, and how we'll
represent it, we come up with:
[source,javascript]
----
// The number of cookies, glasses of lemonade are numbers.
// Whether we will pay the flat fee is a boolean (we either
// will or won't). We'll return a number in cents, as our
// prices are in cents.
----
For this problem, it may seem pretty trivial - but in many problems it
isn't always clear how to represent a given part of the problem (and
not understanding is a good way to get lost!).
The second step is to figure out what the input and output to our
function will be. In the first step, we should have figured out all
the types of data that will appear, but we still have to define
exactly what the _signature_ of the function is, where a _signature_
is a description of the type of all the inputs and the type of the
output of a function.
You should also write this as a comment, as it is similarly very
helpful information for yourself and others who might be looking at
your program (or trying to change it). In our bake sale example, we
end up with the following _signature_:
[source,javascript]
----
// number, number, boolean -> number
----
Where that is notation for a function taking three inputs: a number
(cups of lemonade sold), another number (number of cookies sold), and
a boolean (true means we pay the flat fee) and producing a number (our
total profit) as output.
The third step is writing down the purpose of the function, in human
language. Up until now, everything we have been writing down has been
about the external attributes of the function - what it takes in and
what it produces. There may be many different functions that have
those inputs/outputs. This is the step where we describe, in the
simplest possible way (but still, precisely), what the function should
actually do. In our case, we might write:
[source,javascript]
----
// Takes the number of lemonades * 25 plus the number of
// cookies * 50 and then subtracts either 5000 (if the
// flat fee is chosen) or 10% of this total (if flat fee not
// chosen). This number, which is the number of cents, is
// what we return.
----
The fourth step is to write down the _header_ for the function, which
is the name, arguments, and some _stub_ for the return value
(where a _stub_ is a value that matches the signature, but is as
simple as you can think up). Part of this step involves coming up with
names for the function and all the arguments. In this case, we might
write:
[source,javascript]
----
function bakeSale(lemonades, cookies, useFixedCost) {
return 0;
}
----
Note that for the _stub_, I could have easily written `2`, or `1000`
(which are all numbers). The point of the _stub_ is not to have a
sensible value, just to have something that matches the right type of
data that your signature specified.
The fifth step is to write down example uses of your function, which
are usually called "tests" (as they "test" that your function behaves
properly).
In our case, we might write:
[source,javascript]
----
function bakeSale(lemonades, cookies, useFixedCost) {
return 0;
}
shouldEqual(bakeSale(0,0,true), -5000);
shouldEqual(bakeSale(0,0,false), 0);
shouldEqual(bakeSale(200, 0, true), 0);
shouldEqual(bakeSale(200, 200, true), 10000);
shouldEqual(bakeSale(200, 200, false), 13500);
----
When writing tests, it is good to think both of the common cases (for
example, selling a few hundred of each lemonade and cookies), but also
the strange cases (like selling nothing at all, or all of one
product).
When you run your program (which, at this point, you should), you'll
see a report of how many tests ran and passed (and if they failed,
you'll get a report of what went wrong). When you run them in files
that you save, you'll get the line number when they fail (but that's
not available within the in-text editors).
In this case, most of them failed (the ones that passed happened to
work because we used `0` as our stub, so like a broken clock that is
right twice a day, our always-returning-0 function is sometimes
right!). This is expected! Since you haven't written the body of the
function yet, the tests should be failing. If they weren't, they
couldn't be checking much!
The sixth step is to fill in the body of the function with a
_template_ for each of the arguments. A _template_ is a bit of code
that is how you would normally use the particular value. Each type of
data has its own template. Of the types of data that we've seen so far:
- numbers
- strings
- images
- booleans
The first three have a very simple template - it is just the
identifier of the argument. The template for booleans is an `if`
expression. So if we had a function:
[source,javascript]
----
// (1) Involves booleans.
// (2) Boolean -> Boolean
// (3) This function inverts a boolean value, turning true
// to false and vice versa.
function not(b) { // (4)
return true;
}
// (5)
shouldEqual(not(true), false);
shouldEqual(not(false), true);
----
Where I've identified each step of the design recipe completed thus
far. To example the template for `b` (the only argument), we would write:
[source,javascript]
----
// (1) Involves booleans.
// (2) Boolean -> Boolean
// (3) This function inverts a boolean value, turning true to
// false and vice versa.
function not(b) { // (4)
if (b) {
// ...
} else {
// ...
}
return true;
}
// (5)
shouldEqual(not(true), false);
shouldEqual(not(false), true);
----
The purpose of the template is to give you a place to start - if you
have an argument that is a boolean, you are probably going to have an
`if` somewhere. For our function, we have three arguments, so we have
three templates to fill in. The first two are numbers, so the template
is just the identifier, but the third one is a boolean, so it has a
template similar to `not` above.
[source,javascript]
----
// The number of cookies, glasses of lemonade are numbers.
// Whether we will pay the flat fee is a boolean (we either
// will or won't). We'll return a number in cents, as our
// prices are in cents.
// number, number, boolean -> number
// Takes the number of lemonades * 25 plus the number of
// cookies * 50 and then subtracts either 5000 (if the flat
// fee is chosen) or 10%
// of this total (if flat fee not chosen). This number,
// which is the number of cents, is what we return.
function bakeSale(lemonades, cookies, useFixedCost) {
// ...
lemonades;
// ...
cookies;
// ...
if (useFixedCost) {
// ...
} else {
// ...
}
return 0;
}
shouldEqual(bakeSale(0, 0, true), -5000);
shouldEqual(bakeSale(0, 0, false), 0);
shouldEqual(bakeSale(200, 0, true), 0);
shouldEqual(bakeSale(200, 200, true), 10000);
shouldEqual(bakeSale(200, 200, false), 13500);
----
Now that we have all the parts on the page (so to speak), we can write
the body. The templates are a guide, and can be removed if you end up
not needing them (or using them in a different way), but they are
often a useful way to start
Often, the best place to look for guidance of how to finish the
program is either the purpose statement (which states, in english,
what it should do), or the tests (which shows us some examples of what
it has done). Using the purpose statement, we know that to calculate
the total cents, we multiply the number of lemonades by 25 and the
number of cookies by 50. That gets us further (and we have used up two
of our templates):
[source,javascript]
----
// The number of cookies, glasses of lemonade are numbers.
// Whether we will pay the flat fee is a boolean (we either
// will or won't). We'll return a number in cents, as our
// prices are in cents.
// number, number, boolean -> number
// Takes the number of lemonades * 25 plus the number of
// cookies * 50 and then subtracts either 5000 (if the flat
// fee is chosen) or 10%
// of this total (if flat fee not chosen). This number,
// which is the number of cents, is what we return.
function bakeSale(lemonades, cookies, useFixedCost) {
var total = lemonades * 25 + cookies * 50;
if (useFixedCost) {
// ...
} else {
// ...
}
return 0;
}
shouldEqual(bakeSale(0,0,true), -5000);
shouldEqual(bakeSale(0,0,false), 0);
shouldEqual(bakeSale(200, 0, true), 0);
shouldEqual(bakeSale(200, 200, true), 10000);
shouldEqual(bakeSale(200, 200, false), 13500);
----
Now we want to use the `useFixedCost` template, so we have to think
what happens if we want to use the fixed cost option (which is that we
have to pay a fixed 5000 cents) and what happens if we don't (which is
that we lose 10%, or multiply the total by 0.9). This lets us complete
the function:
[source,javascript]
----
// The number of cookies, glasses of lemonade are numbers.
// Whether we will pay the flat fee is a boolean (we either will or won't).
// We'll return a number in cents, as our prices are in cents.
// number, number, boolean -> number
// Takes the number of lemonades * 25 plus the number of cookies * 50
// and then subtracts either 5000 (if the flat fee is chosen) or 10%
// of this total (if flat fee not chosen). This number, which is the
// number of cents, is what we return.
function bakeSale(lemonades, cookies, useFixedCost) {
var total = lemonades * 25 + cookies * 50;
if (useFixedCost) {
return total - 5000;
} else {
return total * 0.9;
}
return 0;
}
shouldEqual(bakeSale(0,0,true), -5000);
shouldEqual(bakeSale(0,0,false), 0);
shouldEqual(bakeSale(200, 0, true), 0);
shouldEqual(bakeSale(200, 200, true), 10000);
shouldEqual(bakeSale(200, 200, false), 13500);
----
As you are writing the function, you can continue to run your
tests. Often, if you have many tests, some of them will start to pass
as you write the function. In our case, all our tests are now passing!
== Exercises ==
- As an exercise, complete the `not` function so that all the tests pass:=
[source,javascript]
----
// Involves booleans.
// Boolean -> Boolean
// This function inverts a boolean value, turning true to false and vice versa.
function not(b) {
if (b) {
// ...
} else {
// ...
}
}
shouldEqual(not(true), false);
shouldEqual(not(false), true);
----
- Try making these tests pass as well!
[source,javascript]
----
// A person's name as a string.
// String -> String
// This function will enthusiastically greet a person by name.
// If the name is blank OR the name is undefined, just
// return "Hello!!"
function enthusiasticallyGreet(name) {
if (name) {
// ...
} else if (name) {
// ...
} else {
// ...
}
}
shouldEqual(enthusiasticallyGreet("Austin"), "Hello, Austin!!");
shouldEqual(enthusiasticallyGreet("Pea"), "Hello, Pea!!");
shouldEqual(enthusiasticallyGreet(""), "Hello!!");
shouldEqual(enthusiasticallyGreet(), "Hello!!");
----
- Use the design recipe to write a `min` function that takes as input
two numbers and returns the one that is smaller. Here's a template:
[source,javascript]
----
// Involves numbers
// Number -> Number -> Number
// This function takes two numbers as input and return the
// one that is smaller.
function min(x, y) {
//..
}
// Your tests here.
----
- There is a function `stringLength()` that takes a string and returns a
number that is the number of characters in it.
Using that function, can you use the design recipe to "center" a
string within a larger space. For example, `center(4, "hi")` would
return `" hi "`. Once you get a version working, you might work on
figuring out what to do if the space specified is shorter than the
string, or if the amount of space can't be even on both sides.
- There are two functions, `width()` and `height()` that takes as
input images and return numbers that are the width and height
respectively. For review, the `signature` of both functions are:
[source,javascript]
----
// image -> number
----
For example, we can use this to get back the width of a rectangle we
created:
[source,javascript]
----
print(width(rectangle(100,10,"red")));
----
Using this, write a function `area` that takes an image as input and
returns the total number of pixels in the image.
- One more: A B52 bomber costs about $2 billion dollars (that's nine
zeroes). That is a number that is so large, it's hard to wrap your
head around it. Often activists will try saying, "For the cost of one
B52 bomber, you could pay for this many of this thing." Create a
function that will help an activist generate statements like this by
taking the name of an item (like "year of college") and a cost per
item ($23,872), and return a statement about how many of those items
could be obtained for two billion dollars.
[source,javascript]
----
// Data
// Type
// Function description.
function b52Equivalence(item, cost) {
// ...
}
// Your tests here.
----