Hugo Josefson
Senior Web Consultant at www.jayway.com
let a = 1 // a == 1
let a = a + 1 // a == 2
let obj = {x: 5, y: 20} // obj == {x: 5, y: 20}
obj.x = 10 // obj == {x: 10, y: 20}
class Person {
int age = 0
function haveBirthday() {
this.age++
}
}
Person newBorn = new Person()
newBorn.age // 0
newBorn.haveBirthday()
newBorn.age // 1
string[] words = ["Good", "morning", "all"]
function changeTime(string newTime) {
words[1] = newTime
}
function addWord(string word) {
words[words.length + 1] = word
}
// ---
changeTime("afternoon") // words == ["Good", "afternoon", "all"]
addWord("the") // words == ["Good", "afternoon", "all", "the"]
addWord("time") // words == ["Good", "afternoon", "all", "the", "time"]
string hello = "HELLO WORLD!"
string hellow = hello.toLower()
hello //"HELLO WORLD!"
hellow //"hello world!"
hello === hellow //false
string hello = "HELLO WORLD!"
string hellow = hello.toLower()
hello //"HELLO WORLD!"
hellow //"hello world!"
class Person {
int age = 0
function haveBirthday() {
cloneOfMe = this.cloneAndSet({ age: this.age+1 });
return cloneOfMe
}
}
Person newBorn = new Person()
Person toddler = newBorn.haveBirthday()
newBorn.age // 0
toddler.age // 1
string[] words = ["Good", "morning", "all"]
function changeTime(string[] ws, string newTime) {
clonedWords = ws.clone()
clonedWords[1] = newTime
return clonedWords
}
function addWord(string[] ws, string word) {
clonedWords = ws.clone()
clonedWords[clonedWords.length + 1] = word
return clonedWords
}
// ---
changeTime(words, "afternoon") // returns ["Good", "afternoon", "all"]
// words == ["Good", "morning", "all"]
addWord(addWord(words, "the"), "time")
// returns ["Good", "morning", "all", "the", "time"]
// words == ["Good", "morning", "all"]
string[] words = ["Good", "morning", "all"]
function changeTime(string newTime) {
words[1] = newTime
}
function addWord(string word) {
words[words.length + 1] = word
}
// ---
changeTime("afternoon") // words == ["Good", "afternoon", "all"]
addWord("the") // words == ["Good", "afternoon", "all", "the"]
addWord("time") // words == ["Good", "afternoon", "all", "the", "time"]
input container
mapper
output container
const numbers = [1, 2, 3, 4]
const doubledNumbers = []
for (let i = 0; i < numbers.length; i++) { // imperative
doubledNumbers[i] = numbers[i] * 2 // style
}
doubledNumbers //[2, 4, 6, 8]
const numbers = [1, 2, 3, 4]
const doubledNumbers = numbers.map(number => number * 2)
doubledNumbers //[2, 4, 6, 8]
vs
const numbers = [1, 2, 3, 4]
const doubledNumbers = numbers.map(function(number) {
return number * 2
})
doubledNumbers //[2, 4, 6, 8]
const numbers = [1, 2, 3, 4]
const doubledAndIncrementedNumbers = []
for (let i = 0; i < numbers.length; i++) {
doubledAndIncrementedNumbers[i] = numbers[i] * 2 + 1
}
doubledAndIncrementedNumbers //[3, 5, 7, 9]
const numbers = [1, 2, 3, 4]
const doubledAndIncrementedNumbers = numbers
.map(number => number * 2)
.map(number => number + 1)
doubledAndIncrementedNumbers //[3, 5, 7, 9]
vs
const numbers = [1, 2, 3, 4]
const doubledAndIncrementedNumbers = numbers
.map(number => number * 2)
doubledAndIncrementedNumbers //[2, 4, 6, 8]
String[] input = new String[]{"alice", "bob", "paul", "ellie"};
String[] output = Arrays.stream(input)
.map(s -> s.toUpperCase())
.toArray(String[]::new);
output // {"ALICE", "BOB", "PAUL", "ELLIE"}
let values = [2.0, 4.0, 5.0, 7.0]
let squares = values.map {$0 * $0}
squares // [4.0, 16.0, 25.0, 49.0]
const values = [2, 4, 5, 7]
const squares = values.map(n => n * n) // ES2015
squares // [4, 16, 25, 49]
var values = [2, 4, 5, 7]
var squares = values.map(function (n) { // ES5
return n * n
})
squares // [4, 16, 25, 49]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map{|e| e*3 }
# returns [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
List<string> words = new List<string>() {
"an", "apple", "a", "day"
};
var query = from word in words
select word.Substring(0, 1);
foreach (string s in query)
Console.WriteLine(s);
/* This code produces the following output:
a
a
a
d
*/
let helloGood =
let list = ["a";"b";"c"]
list |> List.map (fun element -> "hello " + element)
// val helloGood : string list = ["hello a"; "hello b"; "hello c"]
val numbers = List(1, 2, 3, 4)
numbers.map((i: Int) => i * 2)
// List(2, 4, 6, 8)
items = [1, 2, 3, 4, 5]
squared = list( map(lambda x: x**2, items) )
// [1, 4, 9, 16, 25]
map sqrt [1, 4, 9] == [1, 2, 3]
map not [True, False, True] == [False, True, False]
plusOne = (+) 1
map plusOne [1, 4, 9] == [2, 5, 10]
input container
must be at least this big:
output container
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers.filter(isGood) //[6, 8, 10]
isMoreThanFive = n => n > 5
isEven = n => n%2 == 0
numbers
.filter(isMoreThanFive) //[6, 7, 8, 9, 10]
.filter(n => isEven(n)) //[6, 8, 10]
isGood = n => isMoreThanFive(n) && isEven(n)
String[] input = new String[]{"alice", "bob", "paul", "ellie"};
String[] output = Arrays.stream(input)
.filter(s -> s.length() > 4)
.toArray(String[]::new);
output // {"alice", "ellie"}
let digits = [1, 4, 10, 15]
let even = digits.filter { $0 % 2 == 0 }
// [4, 10]
const values = [1, 4, 10, 15]
const even = values.filter(n => n%2 === 0) // ES2015
even // [4, 10]
var values = [1, 4, 10, 15]
var even = values.filter(function (n) { // ES5
return n%2 === 0
})
even // [4, 10]
[1,2,3,4,5,6,7,8,9,10].select{|el| el%2 == 0 }
# returns [2,4,6,8,10]
string[] words = { "the", "quick", "brown", "fox", "jumps" };
IEnumerable<string> query = from word in words
where word.Length == 3
select word;
foreach (string str in query)
Console.WriteLine(str);
/* This code produces the following output:
the
fox
*/
[1..10] |> List.filter (fun i -> i%2 = 0) // even
// [2; 4; 6; 8; 10]
val numbers = List(1, 2, 3, 4)
numbers.filter((i: Int) => i % 2 == 0)
// List(2, 4, 6, 8)
number_list = range(-5, 5)
less_than_zero = list( filter(lambda x: x < 0, number_list) )
print(less_than_zero)
# Output: [-5, -4, -3, -2, -1]
filter isEven [1..6] == [2,4,6]
input container
+
output
start: 0
3
2
3
8
input container
return the largest of two planets
output
start: .
const numbers = [3, 3, 2]
const sum = numbers.reduce(
(sumSoFar, number) => sumSoFar+number, // <-- reducer fn
0 // <-- init value
)
sum // 8
first call to the reducer fn: sumSoFar = 0; number = 3; sumSoFar+number = 3
second call to the reducer fn: sumSoFar = 3; number = 3; sumSoFar+number = 6
third call to the reducer fn: sumSoFar = 6; number = 2; sumSoFar+number = 8
// numbers.reduce(...) returns 8
const numbers = [3, 3, 2]
const sum = numbers.reduce(
(sumSoFar, number) => sumSoFar+number, // <-- reducer fn
0 // <-- init value
)
sum // 8
const plus = (a, b) => a + b
const numbers = [3, 3, 2]
const sum = numbers.reduce(plus, 0)
sum // 8
String[] input = { "this", "is", "a", "sentence" };
String output = Arrays.stream(myArray)
.reduce("", (a,b) -> a + " " + b);
// "this is a sentence"
let items = [2.0, 4.0, 5.0, 7.0]
let total = items.reduce(10.0, combine: +)
// 28.0
const values = [1, 4, 10, 15]
const sum = values.reduce((sumSoFar, n) => sumSoFar + n, 0) // ES2015
sum // 30
var values = [1, 4, 10, 15]
var sum = values.reduce(function (sumSoFar, n) { // ES5
return sumSoFar + n
}, 0)
sum // 30
[1,2,3,4,5,6,7,8,9,10].inject{|sum,e| sum += e }
# returns 55
string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words.
string[] words = sentence.Split(' ');
// Prepend each word to the beginning of the
// new sentence to reverse the word order.
string reversed = words.Aggregate((workingSentence, next) =>
next + " " + workingSentence);
Console.WriteLine(reversed);
// This code produces the following output:
//
// dog lazy the over jumps fox brown quick the
["a";"b";"c"] |> List.fold (+) "hello: "
// "hello: abc"
// "hello: " + "a" + "b" + "c"
[1;2;3] |> List.fold (+) 10
// 16
// 10 + 1 + 2 + 3
val numbers = List(1, 2, 3, 4)
numbers.foldLeft(0)((m: Int, n: Int) => m + n)
// 55
from functools import reduce
product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] )
# Output: 24
foldr (+) 0 [1,2,3] == 6
www.ling.gu.se/~lager/python_exercises.html
Exercises avoid using anything python specific.
Recommended!
Most of them involve characters, words and phrases, rather than numbers, and are therefore suitable for students interested in language rather than math.
May be quite algorithmic / tricky.
Suggested days: 1, 4, 5, 12.
Day 5 hint: Regex (.)\1 matches double chars.
Well-explained exercises in JavaScript.
Recommended!
On immutability
On map etc
Syntax for map, filter, reduce
user = {
name: String,
age: Int,
birthday: Date
}
users = [
{
name: "Fjodor",
age: 31,
birthday: "2009-04-21 14:25:29.5585588 UTC"
},
{
name: "Hugo",
age: 40,
birthday: "2010-04-21 14:25:29.5585588 UTC"
}
]
map :: (a -> b) -> List a -> List b
map (\a -> a.birthday) users
user = {
name: String,
age: Int,
birthday: Date
}
users = [
{
name: "Fjodor",
age: 31,
birthday: "2009-04-21 14:25:29.5585588 UTC"
},
{
name: "Hugo",
age: 40,
birthday: "2010-04-21 14:25:29.5585588 UTC"
}
]
filter :: (a -> Bool) -> List a -> List a
filter (\ x -> x.age `mod` 2 == 0) users
function add(a, b) {
return a + b;
};
//Normal add function takes two arguments
node> const age = add(1, 3);
4
//What is Albins age?
//Albin is half the age of Hugo which is three years older than me
//Curried add
const add = a => b => a + b;
//Curried div
const div = b => a => a / b;
const myAge = 32;
const threeYearsOlder = add(3);
const halfTheAge = div(2);
const albinsAge = halfTheAge(threeYearsOlder(myAge))
17.5
//OR by using function composition
node> const R = require('ramda');
node> const findOutAlbinsAgeBasedOnMine = R.compose(halfTheAge, threeYearsOlder);
node> const albinsAge = findoutAlbinsAgeBasedOnMine(myAge)
17.5
node> const listOfNumbers = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
node> [ head, ...tail ] = listOfNumbers;
node> head
1
node> tail
[ 2, 3, 4, 5, 6, 7, 8 ]
const dinnerName = (acc, ingredientList) => {
if (!ingredientList.length) { // Exit condition
return acc;
}
const [head, ...tail] = ingredientList; // Destructuring
return dinnerName(head + acc, tail); // Recursion
};
node> const ingredients = [ 'soup', 'mushroom', 'forest' ];
node> const dinner = dinnerName('', ingredients);
node> dinner;
'forestmushroomsoup'
create a recursive function that takes a list of numbers
and returns only even numbers
(hint find the modulo operator)
Always evaluates the same result value
given the same argument values
// Idempotency requires your function to always return the same result
// given the same argument no matter how many times you invoke it
node> const R = require('ramda');
node> const composedIdentity = R.compose(R.identity, R.identity);
// OR
node> composedIdentity(32) === R.identity(32)
true
// NON-IDEMPOTENT
const nonIdempotentFn = arg => {
return arg.length;
};
node> nonIdempotentFn('hello');
5
node> nonIdempotentFn(nonIdempotentFn('hello'));
undefined
node> nonIdempotentFn(5)
undefined
By Hugo Josefson
Introduction for how all programmers can benefit from a little functional programming (fp) in their daily work.