-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFunctionalProgramming.js
More file actions
253 lines (179 loc) · 10.1 KB
/
FunctionalProgramming.js
File metadata and controls
253 lines (179 loc) · 10.1 KB
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
/* Learn About Functional Programming
Functional programming is a style of programming where solutions are simple, isolated functions, without any side effects outside of the function scope: INPUT -> PROCESS -> OUTPUT
Functional programming is about:
Isolated functions - there is no dependence on the state of the program, which includes global variables that are subject to change
Pure functions - the same input always gives the same output
Functions with limited side effects - any changes, or mutations, to the state of the program outside the function are carefully controlled
The members of freeCodeCamp happen to love tea.
In the code editor, the prepareTea and getTea functions are already defined for you. Call the getTea function to get 40 cups of tea for the team, and store them in the tea4TeamFCC variable.
*/
// Function that returns a string representing a cup of green tea
const prepareTea = () => 'greenTea';
/*
Given a function (representing the tea type) and number of cups needed, the
following function returns an array of strings (each representing a cup of
a specific type of tea).
*/
const getTea = (numOfCups) => {
const teaCups = [];
for(let cups = 1; cups <= numOfCups; cups += 1) {
const teaCup = prepareTea();
teaCups.push(teaCup);
}
return teaCups;
};
// Only change code below this line
const tea4TeamFCC = null;
// Only change code above this line
// SOLUTION
const prepareTea = () => 'greenTea';
const getTea = (numOfCups) => {
const teaCups = [];
for(let cups = 1; cups <= numOfcups; cups += 1) {
const teaCup = prepareTea();
teaCups.push(teaCups);
}
return teaCups;
};
const tea4TeamFCC = getTea(40);
/* Should :
The tea4TeamFCC variable should hold 40 cups of tea for the team.
Passed
The tea4TeamFCC variable should hold cups of green tea.
END*/
/* Understand Functional Programming Terminology
The FCC Team had a mood swing and now wants two types of tea: green tea and black tea. General Fact: Client mood swings are pretty common.
With that information, we'll need to revisit the getTea function from last challenge to handle various tea requests. We can modify getTea to accept a function as a parameter to be able to change the type of tea it prepares. This makes getTea more flexible, and gives the programmer more control when client requests change.
But first, let's cover some functional terminology:
Callbacks are the functions that are slipped or passed into another function to decide the invocation of that function. You may have seen them passed to other methods, for example in filter, the callback function tells JavaScript the criteria for how to filter an array.
Functions that can be assigned to a variable, passed into another function, or returned from another function just like any other normal value, are called first class functions. In JavaScript, all functions are first class functions.
The functions that take a function as an argument, or return a function as a return value are called higher order functions.
When functions are passed in to or returned from another function, then those functions which were passed in or returned can be called a lambda.
Prepare 27 cups of green tea and 13 cups of black tea and store them in tea4GreenTeamFCC and tea4BlackTeamFCC variables, respectively. Note that the getTea function has been modified so it now takes a function as the first argument.
Note: The data (the number of cups of tea) is supplied as the last argument. We'll discuss this more in later lessons.
*/
// Function that returns a string representing a cup of green tea
const prepareGreenTea = () => 'greenTea';
// Function that returns a string representing a cup of black tea
const prepareBlackTea = () => 'blackTea';
/*
Given a function (representing the tea type) and number of cups needed, the
following function returns an array of strings (each representing a cup of
a specific type of tea).
*/
const getTea = (prepareTea, numOfCups) => {
const teaCups = [];
for(let cups = 1; cups <= numOfCups; cups += 1) {
const teaCup = prepareTea();
teaCups.push(teaCup);
}
return teaCups;
};
// Only change code below this line
const tea4GreenTeamFCC = null;
const tea4BlackTeamFCC = null;
// Only change code above this line
console.log(
tea4GreenTeamFCC,
tea4BlackTeamFCC
);
// SOLUTION
const prepareGreenTea = () => 'greenTea';
const prepareBlackTea = () => 'blackTea';
const gerTea = (prepareTea, numOfCups) => {
const teaCups = [];
for(let cups = 1; cups <= numOfCups; cups += 1) {
const teaCups = prepareTea();
teaCups.push(teaCups);
}
return teaCups;
}
const tea4GreenTeamFCC = getTea(prepareGreenTea, 27);
const tea4BlackTeamFCC = getTea(prepareBlackTea, 13);
console.log(
tea4GreenTeamFCC,
tea4BlackTeamFCC
);
/* Should :
The tea4GreenTeamFCC variable should hold 27 cups of green tea for the team.
Passed
The tea4GreenTeamFCC variable should hold cups of green tea.
Passed
The tea4BlackTeamFCC variable should hold 13 cups of black tea.
Passed
The tea4BlackTeamFCC variable should hold cups of black tea.
END*/
/* Understand the Hazards of Using Imperative Code
Functional programming is a good habit. It keeps your code easy to manage, and saves you from sneaky bugs. But before we get there, let's look at an imperative approach to programming to highlight where you may have issues.
In English (and many other languages), the imperative tense is used to give commands. Similarly, an imperative style in programming is one that gives the computer a set of statements to perform a task.
Often the statements change the state of the program, like updating global variables. A classic example is writing a for loop that gives exact directions to iterate over the indices of an array.
In contrast, functional programming is a form of declarative programming. You tell the computer what you want done by calling a method or function.
JavaScript offers many predefined methods that handle common tasks so you don't need to write out how the computer should perform them. For example, instead of using the for loop mentioned above, you could call the map method which handles the details of iterating over an array. This helps to avoid semantic errors, like the "Off By One Errors" that were covered in the Debugging section.
Consider the scenario: you are browsing the web in your browser, and want to track the tabs you have opened. Let's try to model this using some simple object-oriented code.
A Window object is made up of tabs, and you usually have more than one Window open. The titles of each open site in each Window object is held in an array. After working in the browser (opening new tabs, merging windows, and closing tabs), you want to print the tabs that are still open. Closed tabs are removed from the array and new tabs (for simplicity) get added to the end of it.
The code editor shows an implementation of this functionality with functions for tabOpen(), tabClose(), and join(). The array tabs is part of the Window object that stores the name of the open pages.
Examine the code in the editor. It's using a method that has side effects in the program, causing incorrect behaviour. The final list of open tabs, stored in finalTabs.tabs, should be ['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium', 'new tab', 'Netflix', 'YouTube', 'Vine', 'GMail', 'Work mail', 'Docs', 'freeCodeCamp', 'new tab'] but the list produced by the code is slightly different.
Change Window.prototype.tabClose so that it removes the correct tab.
*/
// tabs is an array of titles of each site open within the window
var Window = function(tabs) {
this.tabs = tabs; // We keep a record of the array inside the object
};
// When you join two windows into one window
Window.prototype.join = function (otherWindow) {
this.tabs = this.tabs.concat(otherWindow.tabs);
return this;
};
// When you open a new tab at the end
Window.prototype.tabOpen = function (tab) {
this.tabs.push('new tab'); // Let's open a new tab for now
return this;
};
// When you close a tab
Window.prototype.tabClose = function (index) {
// Only change code below this line
var tabsBeforeIndex = this.tabs.splice(0, index); // Get the tabs before the tab
var tabsAfterIndex = this.tabs.splice(index + 1); // Get the tabs after the tab
this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // Join them together
// Only change code above this line
return this;
};
// Let's create three browser windows
var workWindow = new Window(['GMail', 'Inbox', 'Work mail', 'Docs', 'freeCodeCamp']); // Your mailbox, drive, and other work sites
var socialWindow = new Window(['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium']); // Social sites
var videoWindow = new Window(['Netflix', 'YouTube', 'Vimeo', 'Vine']); // Entertainment sites
// Now perform the tab opening, closing, and other operations
var finalTabs = socialWindow
.tabOpen() // Open a new tab for cat memes
.join(videoWindow.tabClose(2)) // Close third tab in video window, and join
.join(workWindow.tabClose(1).tabOpen());
console.log(finalTabs.tabs);
// SOLUTION
var Window = function(tabs) {
this.tabs = tabs;
};
Window.prototype.join = function (otherWindow) {
this.tabs = this.tabs.concat(otherWindow.tabs);
return this;
};
Window.prototype.tabOpen = function (tab) {
this.tabs.push('new tab');
return this;
};
Window.prototype.tabClose = function (index) {
var tabsBeforeIndex = this.tabs.splice(0, index);
var tabsAfterIndex = this.tabs.splice(1);
this.tabs = tabsBeforeIndex.concat(tabsAfterIndex);
return this;
};
var workWindow = new Window(['GMail', 'Inbox', 'Work mail', 'Docs', 'freeCodeCamp']);
var socialWindow = new Window(['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium']);
var videoWindow = new Window(['Netflix', 'YouTube', 'Vimeo', 'Vine']);
var finalTabs = socialWindow
.tabOpen()
.join(videoWindow.tabClose(2))
.join(workWindow.tabClose(1).tabOpen());
console.log(finalTabs.tabs);
/* Should :
finalTabs.tabs should be ['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium', 'new tab', 'Netflix', 'YouTube', 'Vine', 'GMail', 'Work mail', 'Docs', 'freeCodeCamp', 'new tab']
END*/