From c254c98f980bd903c7dcb2c923694216dfc2b578 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Thu, 31 Aug 2017 12:21:52 +0800 Subject: [PATCH 01/14] Add `Loop` runnable code as reference --- .gitignore | 268 +++++++++++++++++- intro/.gitignore | 43 ++- intro/src/main/java/Loop1.java | 12 + .../main/scala/{Main.scala => Loop2.scala} | 3 +- intro/src/main/scala/Loop3.scala | 44 +++ intro/src/main/scala/Loop4.scala | 57 ++++ intro/src/main/scala/Loop5.scala | 49 ++++ intro/src/main/scala/Loop6.scala | 42 +++ intro/src/main/scala/Loop7.scala | 50 ++++ intro/src/main/scala/Loop8.scala | 18 ++ 10 files changed, 582 insertions(+), 4 deletions(-) create mode 100644 intro/src/main/java/Loop1.java rename intro/src/main/scala/{Main.scala => Loop2.scala} (83%) create mode 100644 intro/src/main/scala/Loop3.scala create mode 100644 intro/src/main/scala/Loop4.scala create mode 100644 intro/src/main/scala/Loop5.scala create mode 100644 intro/src/main/scala/Loop6.scala create mode 100644 intro/src/main/scala/Loop7.scala create mode 100644 intro/src/main/scala/Loop8.scala diff --git a/.gitignore b/.gitignore index 9c07d4a..d323b98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,266 @@ -*.class -*.log +# Created by https://www.gitignore.io/api/osx,vim,code,linux,emacs,windows,eclipse,intellij+all + +### Code ### +# Visual Studio Code - https://code.visualstudio.com/ +.settings/ +.vscode/ +tsconfig.json +jsconfig.json + +### Eclipse ### + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile +projectile-bookmarks.eld + +# directory configuration +.dir-locals.el + +# saveplace +places + +# url cache +url/cache/ + +# cedet +ede-projects.el + +# smex +smex-items + +# company-statistics +company-statistics-cache.el + +# anaconda-mode +anaconda-mode/ + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### Intellij+all Patch ### +# Ignores the whole idea folder +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,vim,code,linux,emacs,windows,eclipse,intellij+all diff --git a/intro/.gitignore b/intro/.gitignore index 07827cc..fecd5e1 100644 --- a/intro/.gitignore +++ b/intro/.gitignore @@ -1,2 +1,43 @@ +# Created by https://www.gitignore.io/api/sbt,java,scala + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### SBT ### +# Simple Build Tool +# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control + +dist/* target/ -.idea/ \ No newline at end of file +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +.history +.cache +.lib/ + +### Scala ### + +# End of https://www.gitignore.io/api/sbt,java,scala diff --git a/intro/src/main/java/Loop1.java b/intro/src/main/java/Loop1.java new file mode 100644 index 0000000..d3146d8 --- /dev/null +++ b/intro/src/main/java/Loop1.java @@ -0,0 +1,12 @@ +public class Loop1 { + public static void main(String[] args) { + int sum = 0; + + for (int x = 1; x <= 10; x++) { + if (x % 2 == 0) + sum += x * x; + } + + System.out.println("Sum of even numbers from 1 to 10 is " + sum); + } +} diff --git a/intro/src/main/scala/Main.scala b/intro/src/main/scala/Loop2.scala similarity index 83% rename from intro/src/main/scala/Main.scala rename to intro/src/main/scala/Loop2.scala index e729297..bbd0deb 100644 --- a/intro/src/main/scala/Main.scala +++ b/intro/src/main/scala/Loop2.scala @@ -1,4 +1,4 @@ -object Main extends App { +object Loop2 extends App { var sum = 0 for (x <- 1 to 10) { @@ -7,4 +7,5 @@ object Main extends App { } println(s"Sum of even numbers from 1 to 10 is $sum") + } diff --git a/intro/src/main/scala/Loop3.scala b/intro/src/main/scala/Loop3.scala new file mode 100644 index 0000000..0bac4b7 --- /dev/null +++ b/intro/src/main/scala/Loop3.scala @@ -0,0 +1,44 @@ +object Loop3 extends App { + + def iterate(max: Int): List[Int] = { + var result = List[Int]() + + for (x <- 1 to max) + result = result :+ x + + result + } + + def filterEven(xs: List[Int]): List[Int] = { + var result = List[Int]() + + for (x <- xs) + if (x % 2 == 0) + result = result :+ x + + result + } + + def square(xs: List[Int]): List[Int] = { + var result = List[Int]() + + for (x <- xs) + result = result :+ (x * x) + + result + } + + def sum(xs: List[Int]): Int = { + var result = 0 + + for (x <- xs) + result += x + + result + } + + val result = sum(square(filterEven(iterate(10)))) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} diff --git a/intro/src/main/scala/Loop4.scala b/intro/src/main/scala/Loop4.scala new file mode 100644 index 0000000..f9e8d13 --- /dev/null +++ b/intro/src/main/scala/Loop4.scala @@ -0,0 +1,57 @@ +import scala.annotation.tailrec + +object Loop4 extends App { + + def iterate(max: Int): List[Int] = { + var result = List[Int]() + + for (x <- 1 to max) + result = result :+ x + + result + } + + def filterEven(xs: List[Int]): List[Int] = { + + @tailrec + def loop(result: List[Int], xs: List[Int]): List[Int] = xs match { + case Nil => result + case x :: tail => + val next = if (x % 2 == 0) List(x) else Nil + loop(next ++ result, tail) + } + + loop(Nil, xs) + } + + def square(xs: List[Int]): List[Int] = { + + @tailrec + def loop(result: List[Int], xs: List[Int]): List[Int] = xs match { + case Nil => result + case x :: tail => + val next = List(x * x) + loop(next ++ result, tail) + } + + loop(Nil, xs) + } + + def sum(xs: List[Int]): Int = { + + @tailrec + def loop(result: Int, xs: List[Int]): Int = xs match { + case Nil => result + case x :: tail => + val next = x + loop(next + result, tail) + } + + loop(0, xs) + } + + val result = sum(square(filterEven(iterate(10)))) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} diff --git a/intro/src/main/scala/Loop5.scala b/intro/src/main/scala/Loop5.scala new file mode 100644 index 0000000..71e869c --- /dev/null +++ b/intro/src/main/scala/Loop5.scala @@ -0,0 +1,49 @@ +import scala.annotation.tailrec + +object Loop5 extends App { + + def iterate(max: Int): List[Int] = { + var result = List[Int]() + + for (x <- 1 to max) + result = result :+ x + + result + } + + def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] = { + + @tailrec + def loop(xs: List[Int], result: List[Int]): List[Int] = xs match { + case Nil => result + case x :: tail => + loop(tail, f(x) ++ result) + } + + loop(xs, Nil) + } + + def filterEven(xs: List[Int]): List[Int] = + flatMap(xs, x => if (x % 2 == 0) List(x) else Nil) + + def square(xs: List[Int]): List[Int] = + flatMap(xs, x => List(x * x)) + + def sum(xs: List[Int]): Int = { + + @tailrec + def loop(result: Int, xs: List[Int]): Int = xs match { + case Nil => result + case x :: tail => + val next = x + loop(next + result, tail) + } + + loop(0, xs) + } + + val result = sum(square(filterEven(iterate(10)))) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} diff --git a/intro/src/main/scala/Loop6.scala b/intro/src/main/scala/Loop6.scala new file mode 100644 index 0000000..4722818 --- /dev/null +++ b/intro/src/main/scala/Loop6.scala @@ -0,0 +1,42 @@ +import scala.annotation.tailrec + +object Loop6 extends App { + + def iterate(max: Int): List[Int] = { + var result = List[Int]() + + for (x <- 1 to max) + result = result :+ x + + result + } + + def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = { + + @tailrec + def loop(xs: List[A], result: R): R = xs match { + case Nil => result + case x :: tail => + loop(tail, combine(result, x)) + } + + loop(xs, zero) + } + + def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] = + foldLeft(xs)(List[Int]())((acc, x) => f(x) ++ acc) + + def filterEven(xs: List[Int]): List[Int] = + flatMap(xs, x => if (x % 2 == 0) List(x) else Nil) + + def square(xs: List[Int]): List[Int] = + flatMap(xs, x => List(x * x)) + + def sum(xs: List[Int]): Int = + foldLeft(xs)(0)((acc, x) => acc + x) + + val result = sum(square(filterEven(iterate(10)))) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} diff --git a/intro/src/main/scala/Loop7.scala b/intro/src/main/scala/Loop7.scala new file mode 100644 index 0000000..2ee0410 --- /dev/null +++ b/intro/src/main/scala/Loop7.scala @@ -0,0 +1,50 @@ +import scala.annotation.tailrec + +object Loop7 extends App { + + def iterate(max: Int): List[Int] = { + var result = List[Int]() + + for (x <- 1 to max) + result = result :+ x + + result + } + + def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = { + + @tailrec + def loop(xs: List[A], result: R): R = xs match { + case Nil => result + case x :: tail => + loop(tail, combine(result, x)) + } + + loop(xs, zero) + } + + def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = + foldLeft(xs)(List[B]())((acc, x) => f(x) ++ acc) + + def filter[A](xs: List[A])(f: A => Boolean): List[A] = + flatMap(xs)(x => if (f(x)) List(x) else Nil) + + def map[A, B](xs: List[A])(f: A => B): List[B] = + flatMap(xs)(x => List(f(x))) + + def isEven(x: Int): Boolean = x % 2 == 0 + + def square(x: Int): Int = x * x + + def sum(xs: List[Int]): Int = + foldLeft(xs)(0)((acc, x) => acc + x) + + def sumOfEvenSquares(max: Int): Int = { + sum(map(filter(iterate(max))(isEven))(square)) + } + + val result = sumOfEvenSquares(10) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} diff --git a/intro/src/main/scala/Loop8.scala b/intro/src/main/scala/Loop8.scala new file mode 100644 index 0000000..06e14db --- /dev/null +++ b/intro/src/main/scala/Loop8.scala @@ -0,0 +1,18 @@ +object Loop8 extends App { + + def isEven(x: Int): Boolean = x % 2 == 0 + + def square(x: Int): Int = x * x + + def sumOfEvenSquares(max: Int): Int = { + (1 to max) + .filter(isEven) + .map(square) + .sum + } + + val result = sumOfEvenSquares(10) + + println(s"Sum of even numbers from 1 to 10 is $result") + +} From a960e27879b2a4ae2c1a2675688886388605d9f5 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Thu, 31 Aug 2017 12:40:31 +0800 Subject: [PATCH 02/14] Add links to source code for reference --- intro/README.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/intro/README.md b/intro/README.md index 8946ca6..174358d 100644 --- a/intro/README.md +++ b/intro/README.md @@ -8,7 +8,7 @@ expression-based programming, recursion, immutability and higher-order functions. Getting Started -------- +--------------- Open file `src/main/scala/Main.scala` in your editor of choice. To run the program start `sbt` from the terminal and type `run` in SBT prompt. @@ -50,6 +50,14 @@ public class Loop { } ``` +
+ + _(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/java/Loop1.java))_ + +
+
+ + This program is on purpose easy enough to understand. Consider however that in your real programming work you would iterate over some complex domain objects instead on numbers, maybe over two or three arrays of those @@ -98,6 +106,13 @@ object Main extends App { // 1. } ``` +
+ + _(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop2.scala))_ + +
+
+ First you notice that Scala is less verbose: no semicolons needed, no `public static void main`, no explicit reassignment of `x` in `for` loop. Here are other notable differences: @@ -170,6 +185,13 @@ def sum(xs: List[Int]): Int = { val result = sum(square(filterEven(iterate(10)))) ``` +
+ + _(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop3.scala))_ + +
+
+ Wow! The number of lines of code just exploded and we introduced a lot of duplication along the way. Let's review new Scala syntax first: @@ -309,6 +331,13 @@ def sum(xs: List[Int]): Int = { } ``` +
+ + _(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop4.scala))_ + +
+
+ Here we pattern match on a list instead of integer. Note how we used the same list construction syntax in the `case` expression to capture list's head `x` and its `tail`. `++` concatenates two lists. @@ -355,6 +384,13 @@ def square(xs: List[Int]): List[Int] = flatMap(xs, x => List(x * x)) ``` +
+ + _(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop5.scala))_ + +
+
+ 1. `flatMap` takes function `f` as its second argument; 2. `A => B` is type annotation for a function that takes an argument of type `A` and returns a value of type `B`; @@ -404,6 +440,13 @@ def sum(xs: List[Int]): Int = foldLeft(xs)(0)((acc, x) => acc + x) ``` +
+ + _(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop6.scala))_ + +
+
+ 1. `foldLeft[A, R]` is parametrized on type of the input list `A` as well as the type of its return value `R` thus `foldLeft` is a *generic* function; 2. Scala functions can have multiple parameter lists. Here it's useful for @@ -447,6 +490,13 @@ def sumOfEvenSquares(max: Int): Int = { } ``` +
+ + _(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop7.scala))_ + +
+
+ Here `square` really does only what it says and doesn't mess with lists anymore, that's responsibility of `map` now. @@ -470,6 +520,13 @@ def sumOfEvenSquares(max: Int): Int = { } ``` +
+ + _(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop8.scala))_ + +
+
+ We use `1 to 10` directly instead of our own `iterate` function. `filter`, `map`, `sum` as well as `flatMap`, `foldLeft` and many more functions are available as methods on any Scala collection. This allows using the fluid API From 4220c8327ad420cc4736c3232ecb60d8e356c0a5 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Thu, 31 Aug 2017 12:42:22 +0800 Subject: [PATCH 03/14] Fix code example to utilise `max` parameter --- intro/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intro/README.md b/intro/README.md index 174358d..890321a 100644 --- a/intro/README.md +++ b/intro/README.md @@ -486,7 +486,7 @@ def isEven(x: Int): Boolean = x % 2 == 0 def square(x: Int): Int = x * x def sumOfEvenSquares(max: Int): Int = { - sum(map(filter(iterate(10))(isEven))(square)) + sum(map(filter(iterate(max))(isEven))(square)) } ``` @@ -513,7 +513,7 @@ def isEven(x: Int): Boolean = x % 2 == 0 def square(x: Int): Int = x * x def sumOfEvenSquares(max: Int): Int = { - (1 to 10) + (1 to max) .filter(isEven) .map(square) .sum From fbfb76ff91fa100c64f8d9de384db06e2615d080 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Thu, 31 Aug 2017 13:23:13 +0800 Subject: [PATCH 04/14] Split code into `exercises` and `references` --- {intro => exercises/intro}/.gitignore | 0 {intro => exercises/intro}/README.md | 16 +++---- {intro => exercises/intro}/build.sbt | 0 .../intro}/project/build.properties | 0 exercises/intro/src/main/scala/Main.scala | 11 +++++ references/intro/.gitignore | 43 +++++++++++++++++++ references/intro/build.sbt | 5 +++ references/intro/project/build.properties | 1 + .../intro}/src/main/java/Loop1.java | 0 .../intro}/src/main/scala/Loop2.scala | 0 .../intro}/src/main/scala/Loop3.scala | 0 .../intro}/src/main/scala/Loop4.scala | 0 .../intro}/src/main/scala/Loop5.scala | 0 .../intro}/src/main/scala/Loop6.scala | 0 .../intro}/src/main/scala/Loop7.scala | 0 .../intro}/src/main/scala/Loop8.scala | 0 16 files changed, 68 insertions(+), 8 deletions(-) rename {intro => exercises/intro}/.gitignore (100%) rename {intro => exercises/intro}/README.md (96%) rename {intro => exercises/intro}/build.sbt (100%) rename {intro => exercises/intro}/project/build.properties (100%) create mode 100644 exercises/intro/src/main/scala/Main.scala create mode 100644 references/intro/.gitignore create mode 100644 references/intro/build.sbt create mode 100644 references/intro/project/build.properties rename {intro => references/intro}/src/main/java/Loop1.java (100%) rename {intro => references/intro}/src/main/scala/Loop2.scala (100%) rename {intro => references/intro}/src/main/scala/Loop3.scala (100%) rename {intro => references/intro}/src/main/scala/Loop4.scala (100%) rename {intro => references/intro}/src/main/scala/Loop5.scala (100%) rename {intro => references/intro}/src/main/scala/Loop6.scala (100%) rename {intro => references/intro}/src/main/scala/Loop7.scala (100%) rename {intro => references/intro}/src/main/scala/Loop8.scala (100%) diff --git a/intro/.gitignore b/exercises/intro/.gitignore similarity index 100% rename from intro/.gitignore rename to exercises/intro/.gitignore diff --git a/intro/README.md b/exercises/intro/README.md similarity index 96% rename from intro/README.md rename to exercises/intro/README.md index 890321a..db4fb8a 100644 --- a/intro/README.md +++ b/exercises/intro/README.md @@ -52,7 +52,7 @@ public class Loop {
- _(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/java/Loop1.java))_ + _(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/java/Loop1.java))_

@@ -108,7 +108,7 @@ object Main extends App { // 1.
- _(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop2.scala))_ + _(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala))_

@@ -187,7 +187,7 @@ val result = sum(square(filterEven(iterate(10))))
- _(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop3.scala))_ + _(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala))_

@@ -333,7 +333,7 @@ def sum(xs: List[Int]): Int = {
- _(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop4.scala))_ + _(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala))_

@@ -386,7 +386,7 @@ def square(xs: List[Int]): List[Int] =
- _(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop5.scala))_ + _(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala))_

@@ -442,7 +442,7 @@ def sum(xs: List[Int]): Int =
- _(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop6.scala))_ + _(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala))_

@@ -492,7 +492,7 @@ def sumOfEvenSquares(max: Int): Int = {
- _(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop7.scala))_ + _(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala))_

@@ -522,7 +522,7 @@ def sumOfEvenSquares(max: Int): Int = {
- _(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/intro/src/main/scala/Loop8.scala))_ + _(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala))_

diff --git a/intro/build.sbt b/exercises/intro/build.sbt similarity index 100% rename from intro/build.sbt rename to exercises/intro/build.sbt diff --git a/intro/project/build.properties b/exercises/intro/project/build.properties similarity index 100% rename from intro/project/build.properties rename to exercises/intro/project/build.properties diff --git a/exercises/intro/src/main/scala/Main.scala b/exercises/intro/src/main/scala/Main.scala new file mode 100644 index 0000000..ece9140 --- /dev/null +++ b/exercises/intro/src/main/scala/Main.scala @@ -0,0 +1,11 @@ +object Main extends App { + + var sum = 0 + for (x <- 1 to 10) { + if (x % 2 == 0) + sum += x * x + } + + println(s"Sum of even numbers from 1 to 10 is $sum") + +} diff --git a/references/intro/.gitignore b/references/intro/.gitignore new file mode 100644 index 0000000..fecd5e1 --- /dev/null +++ b/references/intro/.gitignore @@ -0,0 +1,43 @@ +# Created by https://www.gitignore.io/api/sbt,java,scala + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### SBT ### +# Simple Build Tool +# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control + +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +.history +.cache +.lib/ + +### Scala ### + +# End of https://www.gitignore.io/api/sbt,java,scala diff --git a/references/intro/build.sbt b/references/intro/build.sbt new file mode 100644 index 0000000..9631802 --- /dev/null +++ b/references/intro/build.sbt @@ -0,0 +1,5 @@ +name := "intro" + +version := "1.0" + +scalaVersion := "2.12.3" diff --git a/references/intro/project/build.properties b/references/intro/project/build.properties new file mode 100644 index 0000000..826c0bd --- /dev/null +++ b/references/intro/project/build.properties @@ -0,0 +1 @@ +sbt.version = 0.13.16 \ No newline at end of file diff --git a/intro/src/main/java/Loop1.java b/references/intro/src/main/java/Loop1.java similarity index 100% rename from intro/src/main/java/Loop1.java rename to references/intro/src/main/java/Loop1.java diff --git a/intro/src/main/scala/Loop2.scala b/references/intro/src/main/scala/Loop2.scala similarity index 100% rename from intro/src/main/scala/Loop2.scala rename to references/intro/src/main/scala/Loop2.scala diff --git a/intro/src/main/scala/Loop3.scala b/references/intro/src/main/scala/Loop3.scala similarity index 100% rename from intro/src/main/scala/Loop3.scala rename to references/intro/src/main/scala/Loop3.scala diff --git a/intro/src/main/scala/Loop4.scala b/references/intro/src/main/scala/Loop4.scala similarity index 100% rename from intro/src/main/scala/Loop4.scala rename to references/intro/src/main/scala/Loop4.scala diff --git a/intro/src/main/scala/Loop5.scala b/references/intro/src/main/scala/Loop5.scala similarity index 100% rename from intro/src/main/scala/Loop5.scala rename to references/intro/src/main/scala/Loop5.scala diff --git a/intro/src/main/scala/Loop6.scala b/references/intro/src/main/scala/Loop6.scala similarity index 100% rename from intro/src/main/scala/Loop6.scala rename to references/intro/src/main/scala/Loop6.scala diff --git a/intro/src/main/scala/Loop7.scala b/references/intro/src/main/scala/Loop7.scala similarity index 100% rename from intro/src/main/scala/Loop7.scala rename to references/intro/src/main/scala/Loop7.scala diff --git a/intro/src/main/scala/Loop8.scala b/references/intro/src/main/scala/Loop8.scala similarity index 100% rename from intro/src/main/scala/Loop8.scala rename to references/intro/src/main/scala/Loop8.scala From a5773757e08d4a1de72ce544d59927766104ca9e Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Tue, 5 Sep 2017 11:18:46 +0800 Subject: [PATCH 05/14] Break long paragraph in intro to make it easier to read --- exercises/intro/README.md | 112 ++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 42 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index db4fb8a..11ecefc 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -1,17 +1,21 @@ -Introduction to Functional Programming in Scala +Introduction to Functional Programming (FP) in Scala ============ In this tutorial we will start with a trivial program, written in imperative style, and through a series of refactorings transform it into a functional -program while learning the core FP concepts like referential transparency, -expression-based programming, recursion, immutability and higher-order -functions. +program while learning core FP concepts such as: + +* referential transparency, +* expression-based programming, +* recursion, +* immutability, and +* higher-order functions. Getting Started --------------- -Open file `src/main/scala/Main.scala` in your editor of choice. To run the -program start `sbt` from the terminal and type `run` in SBT prompt. +Open file `src/main/scala/Main.scala` in your editor of choice. +To run the program start `sbt` from the terminal and type `run` in SBT prompt. The Challenge ------------- @@ -31,7 +35,7 @@ The "Real" Program ------------------ We'll start with almost a schoolbook solution in Java that will have loops, -conditionals and variables. This first example is in Java instead of Scala +conditionals, and variables. This first example is in Java instead of Scala so that it's immediately familiar to anyone who did some programming in procedural imperative language before: @@ -57,16 +61,16 @@ public class Loop {
+This program is, on purpose, easy enough to understand. Consider however, that +in your real programming work you might deal with several complexities at the same +time and thus a simple `for` loop with a variable can quickly get out of hand: -This program is on purpose easy enough to understand. Consider however that -in your real programming work you would iterate over some complex domain -objects instead on numbers, maybe over two or three arrays of those -simultaneously, there would be many more conditions and they would be nested, -there would be multiple variables to keep track of running totals and they -would all change independently of each other based on complex conditional -logic. And what about running this loop on multiple CPU cores in parallel so -that it can finish faster? You see how a simple `for` loop with a variable can -quickly get out of hand? +* iterating over some complex domain objects instead on numbers, +* iterating over two or three arrays of those domain objects simultaneously, +* multiple and / or nested conditions, +* multiple variables to keep track of running totals, +* multiple variables that all change independently based on complex conditional logic, and +* parallel loop execution (i.e. utilising multiple CPU cores). Two cornerstones of imperative programming languages contribute to the problem here: @@ -119,13 +123,13 @@ First you notice that Scala is less verbose: no semicolons needed, no `public 1. `object` keyword defines a singleton value initialized by the block of code inside it. Extending `App` makes it an entrypoint of an application; -2. `var` keyword defines a variable that can be reassigned later. Type of the +1. `var` keyword defines a variable that can be reassigned later. Type of the variable is inferred from the right-hand side of the assignment, `Int` in this case; -3. `1 to 10` defines a inclusive `Range` – a sequence that can be enumerated; -4. `for (x <- seq) { ...x... }` enumerates elements of `seq` executing the +1. `1 to 10` defines a inclusive `Range` – a sequence that can be enumerated; +1. `for (x <- seq) { ...x... }` enumerates elements of `seq` executing the code block in curly braces with `x` representing each element in scope; -5. `s"...$x..."` does string interpolation of value `x`. +1. `s"...$x..."` does string interpolation of value `x`. Decomplecting Functions ----------------------- @@ -144,6 +148,27 @@ for (x <- 1 to 10) { // iterate There are 4 distinct functions that are entangled in this statement-based program. No single function can be tested in isolation. Let's pull them apart: +```scala +def iterate(max: Int): List[Int] = ??? +def filterEven(xs: List[Int]): List[Int] = ??? +def square(xs: List[Int]): List[Int] = ??? +def sum(xs: List[Int]): Int = ??? + +val result = sum(square(filterEven(iterate(10)))) +``` + +Let's review the function and variable assignment Scala syntax first: + +1. `def f(x: Int): List[Int]` defines function `f` that takes an +argument `x` of type `Int` and returns a `List` of `Int`s; +1. `???` is a way to define a method stub (like a TODO), +which will allow the program to compile but throws `NotImplementedError` +when the function is called; +1. `val x = ...` defines an immutable value, once assigned the value +cannot be changed (unlike a `var`). + +So how would the function implementation look? + ```scala def iterate(max: Int): List[Int] = { var result = List[Int]() @@ -193,14 +218,12 @@ val result = sum(square(filterEven(iterate(10))))
Wow! The number of lines of code just exploded and we introduced a lot of -duplication along the way. Let's review new Scala syntax first: +duplication along the way. Let's review the function body implementation: -1. `def f(x: Int): List[Int] = {...}` defines function `f` that takes an -argument `x` of type `Int` and returns a `List` of `Int`s; -2. The last expression of a function body is its return value; -3. `List[Int]()` creates an empty list of integers; -4. `list :+ element` returns a new list made of appending `element` to `list`; -5. `val x = ...` defines an immutable value that cannot be changed. +1. The last expression of a function body is its return value; +1. `List[Int]()` creates an empty list of integers, one alternative is to use +the equivalent factory method `List.empty[Int]` (a matter of preference); +1. `list :+ element` returns a new list made of appending `element` to `list`; Despite explosion of code and duplication we've made our program composable: every function can be tested in isolation and functions can be @@ -212,15 +235,18 @@ universal building blocks. Consider the expression `sum(square(filterEven(iterate(10))))`. Unlike a program made of statements, every sub-expression is an expression on its own: - `10` is an expression which evaluates to 10, `iterate(10)` is an expression -which evaluates to `List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`, `filterEven -(iterate(10))` is an expression which evaluates to `List(2, 4, 6, 8, 10)` -and so on. Every expression can be replaced with the value it evaluates to as + +* `10` is an expression which evaluates to 10, +* `iterate(10)` is an expression which evaluates to `List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`, +* `filterEven (iterate(10))` is an expression which evaluates to `List(2, 4, 6, 8, 10)`, +* and so on. + +Every expression can be replaced with the value it evaluates to as long as functions involved in the expression don't have side effects – e.g. launch the proverbial missiles or modify variables in other parts of the program. In functional programming such functions are called *pure* and the property allowing substitution of values for expressions is called -*referential transparency* meaning that referring to a value via an +*referential transparency*, meaning that referring to a value via an indirection of a function call is transparent and doesn't change program execution. @@ -273,14 +299,14 @@ First, let's clear the new Scala syntax out of the way: 1. Functions can be defined inside the body of other functions, this is not recursion though; -2. `@tailrec` annotation checks at compile time that our recursive function +1. `@tailrec` annotation checks at compile time that our recursive function `loop` will not blow the call stack with large inputs; -3. We use *pattern matching* to match on integer `max`. It looks like `switch` +1. We use *pattern matching* to match on integer `max`. It looks like `switch` statement in other programming languages, but is an expression instead of a statement because every `case` branch has to evaluate to the same type; -4. `case _` matches any value; -5. `element :: list` creates a new list with `element` prepended to `list`; -6. `Nil` is an empty list. +1. `case _` matches any value (i.e. catchall); +1. `element :: list` creates a new list with `element` prepended to `list`; +1. `Nil` is an empty list. Recursive pattern is to pass the intermediary result along with the reduced input to the `loop` function itself. The recursive function breaks out of @@ -497,15 +523,17 @@ def sumOfEvenSquares(max: Int): Int = {
-Here `square` really does only what it says and doesn't mess with lists -anymore, that's responsibility of `map` now. +Here, `square` really does only what it says and doesn't mess with lists +anymore, which is now the responsibility of `map`. Collection API -------------- -By a lucky coincidence Scala standard library collection API already provides -most of the functions that we've just discovered. It would be a shame not to -use them: +We have gone to some length to describe some basic collection operations such +as `fold` and `map`. Because these operations are very commonly used, Scala +provided them in the standard library as part of the collections API. So let's +perform one final refactoring to swap our own implementation with ones from the +standard library: ```scala def isEven(x: Int): Boolean = x % 2 == 0 From 375b0cf5a5c102f63942cf54e1f0c9daac4f5cea Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:06:21 +0800 Subject: [PATCH 06/14] Replace ordered list with regular numbering instead of MD auto-parsed one --- exercises/intro/README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index 11ecefc..faa4f58 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -123,13 +123,13 @@ First you notice that Scala is less verbose: no semicolons needed, no `public 1. `object` keyword defines a singleton value initialized by the block of code inside it. Extending `App` makes it an entrypoint of an application; -1. `var` keyword defines a variable that can be reassigned later. Type of the +2. `var` keyword defines a variable that can be reassigned later. Type of the variable is inferred from the right-hand side of the assignment, `Int` in this case; -1. `1 to 10` defines a inclusive `Range` – a sequence that can be enumerated; -1. `for (x <- seq) { ...x... }` enumerates elements of `seq` executing the +3. `1 to 10` defines a inclusive `Range` – a sequence that can be enumerated; +4. `for (x <- seq) { ...x... }` enumerates elements of `seq` executing the code block in curly braces with `x` representing each element in scope; -1. `s"...$x..."` does string interpolation of value `x`. +5. `s"...$x..."` does string interpolation of value `x`. Decomplecting Functions ----------------------- @@ -161,10 +161,10 @@ Let's review the function and variable assignment Scala syntax first: 1. `def f(x: Int): List[Int]` defines function `f` that takes an argument `x` of type `Int` and returns a `List` of `Int`s; -1. `???` is a way to define a method stub (like a TODO), +2. `???` is a way to define a method stub (like a TODO), which will allow the program to compile but throws `NotImplementedError` when the function is called; -1. `val x = ...` defines an immutable value, once assigned the value +3. `val x = ...` defines an immutable value, once assigned the value cannot be changed (unlike a `var`). So how would the function implementation look? @@ -221,9 +221,9 @@ Wow! The number of lines of code just exploded and we introduced a lot of duplication along the way. Let's review the function body implementation: 1. The last expression of a function body is its return value; -1. `List[Int]()` creates an empty list of integers, one alternative is to use +2. `List[Int]()` creates an empty list of integers, one alternative is to use the equivalent factory method `List.empty[Int]` (a matter of preference); -1. `list :+ element` returns a new list made of appending `element` to `list`; +3. `list :+ element` returns a new list made of appending `element` to `list`; Despite explosion of code and duplication we've made our program composable: every function can be tested in isolation and functions can be @@ -299,14 +299,14 @@ First, let's clear the new Scala syntax out of the way: 1. Functions can be defined inside the body of other functions, this is not recursion though; -1. `@tailrec` annotation checks at compile time that our recursive function +2. `@tailrec` annotation checks at compile time that our recursive function `loop` will not blow the call stack with large inputs; -1. We use *pattern matching* to match on integer `max`. It looks like `switch` +3. We use *pattern matching* to match on integer `max`. It looks like `switch` statement in other programming languages, but is an expression instead of a statement because every `case` branch has to evaluate to the same type; -1. `case _` matches any value (i.e. catchall); -1. `element :: list` creates a new list with `element` prepended to `list`; -1. `Nil` is an empty list. +4. `case _` matches any value (i.e. catchall); +5. `element :: list` creates a new list with `element` prepended to `list`; +6. `Nil` is an empty list. Recursive pattern is to pass the intermediary result along with the reduced input to the `loop` function itself. The recursive function breaks out of From 8017189649bd774757e4642fc2dd44c34b8d374a Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:07:34 +0800 Subject: [PATCH 07/14] Replace unordered list symbol to `-` for consistency --- exercises/intro/README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index faa4f58..ac9c9d0 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -5,11 +5,11 @@ In this tutorial we will start with a trivial program, written in imperative style, and through a series of refactorings transform it into a functional program while learning core FP concepts such as: -* referential transparency, -* expression-based programming, -* recursion, -* immutability, and -* higher-order functions. +- referential transparency, +- expression-based programming, +- recursion, +- immutability, and +- higher-order functions. Getting Started --------------- @@ -65,12 +65,12 @@ This program is, on purpose, easy enough to understand. Consider however, that in your real programming work you might deal with several complexities at the same time and thus a simple `for` loop with a variable can quickly get out of hand: -* iterating over some complex domain objects instead on numbers, -* iterating over two or three arrays of those domain objects simultaneously, -* multiple and / or nested conditions, -* multiple variables to keep track of running totals, -* multiple variables that all change independently based on complex conditional logic, and -* parallel loop execution (i.e. utilising multiple CPU cores). +- iterating over some complex domain objects instead on numbers, +- iterating over two or three arrays of those domain objects simultaneously, +- multiple and / or nested conditions, +- multiple variables to keep track of running totals, +- multiple variables that all change independently based on complex conditional logic, and +- parallel loop execution (i.e. utilising multiple CPU cores). Two cornerstones of imperative programming languages contribute to the problem here: @@ -236,10 +236,10 @@ universal building blocks. Consider the expression `sum(square(filterEven(iterate(10))))`. Unlike a program made of statements, every sub-expression is an expression on its own: -* `10` is an expression which evaluates to 10, -* `iterate(10)` is an expression which evaluates to `List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`, -* `filterEven (iterate(10))` is an expression which evaluates to `List(2, 4, 6, 8, 10)`, -* and so on. +- `10` is an expression which evaluates to 10, +- `iterate(10)` is an expression which evaluates to `List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`, +- `filterEven (iterate(10))` is an expression which evaluates to `List(2, 4, 6, 8, 10)`, +- and so on. Every expression can be replaced with the value it evaluates to as long as functions involved in the expression don't have side effects – e.g. From 37e0f479795ea0a05471f728d3a723ddbb7a77c6 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:17:45 +0800 Subject: [PATCH 08/14] Fix MD rendering for source code links --- exercises/intro/README.md | 48 +++++++-------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index ac9c9d0..f50a764 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -54,11 +54,7 @@ public class Loop { } ``` -
- - _(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/java/Loop1.java))_ - -
+_
(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/java/Loop1.java))
_
This program is, on purpose, easy enough to understand. Consider however, that @@ -110,11 +106,7 @@ object Main extends App { // 1. } ``` -
- - _(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala))_ - -
+_
(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala))
_
First you notice that Scala is less verbose: no semicolons needed, no `public @@ -210,11 +202,7 @@ def sum(xs: List[Int]): Int = { val result = sum(square(filterEven(iterate(10)))) ``` -
- - _(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala))_ - -
+_
(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala))
_
Wow! The number of lines of code just exploded and we introduced a lot of @@ -357,11 +345,7 @@ def sum(xs: List[Int]): Int = { } ``` -
- - _(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala))_ - -
+_
(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala))
_
Here we pattern match on a list instead of integer. Note how we used the same @@ -410,11 +394,7 @@ def square(xs: List[Int]): List[Int] = flatMap(xs, x => List(x * x)) ``` -
- - _(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala))_ - -
+_
(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala))_

1. `flatMap` takes function `f` as its second argument; @@ -466,11 +446,7 @@ def sum(xs: List[Int]): Int = foldLeft(xs)(0)((acc, x) => acc + x) ``` -
- - _(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala))_ - -
+_
(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala))
_
1. `foldLeft[A, R]` is parametrized on type of the input list `A` as well as @@ -516,11 +492,7 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -
- - _(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala))_ - -
+_
(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala))
_
Here, `square` really does only what it says and doesn't mess with lists @@ -548,11 +520,7 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -
- - _(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala))_ - -
+_
(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala))
_
We use `1 to 10` directly instead of our own `iterate` function. `filter`, From b0d129008f638d4370c517c804411616291f3760 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:24:20 +0800 Subject: [PATCH 09/14] Simplify and replace link into footnote-style --- exercises/intro/README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index f50a764..8e60873 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -54,8 +54,7 @@ public class Loop { } ``` -_
(source: [Loop1.java](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/java/Loop1.java))
_ -
+Source: [Loop1.java] This program is, on purpose, easy enough to understand. Consider however, that in your real programming work you might deal with several complexities at the same @@ -106,8 +105,7 @@ object Main extends App { // 1. } ``` -_
(source: [Loop2.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala))
_ -
+Source: [Loop2.scala] First you notice that Scala is less verbose: no semicolons needed, no `public static void main`, no explicit reassignment of `x` in `for` loop. Here are @@ -202,8 +200,7 @@ def sum(xs: List[Int]): Int = { val result = sum(square(filterEven(iterate(10)))) ``` -_
(source: [Loop3.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala))
_ -
+Source: [Loop3.scala] Wow! The number of lines of code just exploded and we introduced a lot of duplication along the way. Let's review the function body implementation: @@ -345,8 +342,7 @@ def sum(xs: List[Int]): Int = { } ``` -_
(source: [Loop4.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala))
_ -
+Source: [Loop4.scala] Here we pattern match on a list instead of integer. Note how we used the same list construction syntax in the `case` expression to capture list's head `x` @@ -394,8 +390,7 @@ def square(xs: List[Int]): List[Int] = flatMap(xs, x => List(x * x)) ``` -_
(source: [Loop5.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala))_
-
+Source: [Loop5.scala] 1. `flatMap` takes function `f` as its second argument; 2. `A => B` is type annotation for a function that takes an argument of type @@ -446,8 +441,7 @@ def sum(xs: List[Int]): Int = foldLeft(xs)(0)((acc, x) => acc + x) ``` -_
(source: [Loop6.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala))
_ -
+Source: [Loop6.scala] 1. `foldLeft[A, R]` is parametrized on type of the input list `A` as well as the type of its return value `R` thus `foldLeft` is a *generic* function; @@ -492,8 +486,7 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -_
(source: [Loop7.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala))
_ -
+Source: [Loop7.scala] Here, `square` really does only what it says and doesn't mess with lists anymore, which is now the responsibility of `map`. @@ -520,8 +513,7 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -_
(source: [Loop8.scala](https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala))
_ -
+Source: [Loop8.scala] We use `1 to 10` directly instead of our own `iterate` function. `filter`, `map`, `sum` as well as `flatMap`, `foldLeft` and many more functions are @@ -537,3 +529,13 @@ readable and easy to maintain and change because it doesn't contain any mutable variables and imperative statements. It's very modular – we can change the order of operations in predictable ways and we can add more transformations without changing the existing ones. + + +[Loop1.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop1.scala +[Loop2.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala +[Loop3.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala +[Loop4.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala +[Loop5.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala +[Loop6.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala +[Loop7.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala +[Loop8.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala From 9803b0943d9028751d649d74426f0c4c9ce7a8a1 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:26:31 +0800 Subject: [PATCH 10/14] Revert and add air quotes ;) --- exercises/intro/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index 8e60873..f56c0a1 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -494,11 +494,9 @@ anymore, which is now the responsibility of `map`. Collection API -------------- -We have gone to some length to describe some basic collection operations such -as `fold` and `map`. Because these operations are very commonly used, Scala -provided them in the standard library as part of the collections API. So let's -perform one final refactoring to swap our own implementation with ones from the -standard library: +"By a lucky coincidence", Scala standard library collection API already provides +most of the functions that we've just discovered. It would be a shame not to +use them: ```scala def isEven(x: Int): Boolean = x % 2 == 0 From f70c2f7f26f35fcc16c7e3cc6bf3c34340fba6f0 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:37:53 +0800 Subject: [PATCH 11/14] Add generated diff output --- references/intro/diff/Loop3-Loop4.html | 260 +++++++++++++++++++++++++ references/intro/diff/Loop4-Loop5.html | 230 ++++++++++++++++++++++ references/intro/diff/Loop5-Loop6.html | 212 ++++++++++++++++++++ references/intro/diff/Loop6-Loop7.html | 206 ++++++++++++++++++++ references/intro/diff/Loop7-Loop8.html | 215 ++++++++++++++++++++ 5 files changed, 1123 insertions(+) create mode 100644 references/intro/diff/Loop3-Loop4.html create mode 100644 references/intro/diff/Loop4-Loop5.html create mode 100644 references/intro/diff/Loop5-Loop6.html create mode 100644 references/intro/diff/Loop6-Loop7.html create mode 100644 references/intro/diff/Loop7-Loop8.html diff --git a/references/intro/diff/Loop3-Loop4.html b/references/intro/diff/Loop3-Loop4.html new file mode 100644 index 0000000..b632c7f --- /dev/null +++ b/references/intro/diff/Loop3-Loop4.html @@ -0,0 +1,260 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop3.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop4.scala
1import scala.annotation.tailrec
2
1object Loop3 extends App {3object Loop4 extends App {
24
3  def iterate(max: Int): List[Int] = {5  def iterate(max: Int): List[Int] = {
4    var result = List[Int]()6    var result = List[Int]()
57
6    for (x <- 1 to max)8    for (x <- 1 to max)
7      result = result :+ x9      result = result :+ x
810
9    result11    result
10  }12  }
1113
12  def filterEven(xs: List[Int]): List[Int] = {14  def filterEven(xs: List[Int]): List[Int] = {
13    var result = List[Int]()
1415
15    for (x <- xs)16    @tailrec
16      if (x % 2 == 0)17    def loop(result: List[Int], xs: List[Int]): List[Int] = xs match {
17        result = result :+ x
18
19    result18      case Nil => result
19      case x :: tail =>
20        val next = if (x % 2 == 0) List(x) else Nil
21        loop(next ++ result, tail)
22    }
23
24    loop(Nil, xs)
20  }25  }
2126
22  def square(xs: List[Int]): List[Int] = {27  def square(xs: List[Int]): List[Int] = {
23    var result = List[Int]()
2428
29    @tailrec
30    def loop(result: List[Int], xs: List[Int]): List[Int] = xs match {
31      case Nil => result
32      case x :: tail =>
33        val next = List(x * x)
34        loop(next ++ result, tail)
35    }
36
25    for (x <- xs)37    loop(Nil, xs)
26      result = result :+ (x * x)
27
28    result
29  }38  }
3039
31  def sum(xs: List[Int]): Int = {40  def sum(xs: List[Int]): Int = {
32    var result = 0
3341
42    @tailrec
43    def loop(result: Int, xs: List[Int]): Int = xs match {
44      case Nil => result
45      case x :: tail =>
46        val next = x
47        loop(next + result, tail)
48    }
49
34    for (x <- xs)50    loop(0, xs)
35      result += x
36
37    result
38  }51  }
3952
40  val result = sum(square(filterEven(iterate(10))))53  val result = sum(square(filterEven(iterate(10))))
4154
42  println(s"Sum of even numbers from 1 to 10 is $result")55  println(s"Sum of even numbers from 1 to 10 is $result")
4356
44}57}
4558
+ diff --git a/references/intro/diff/Loop4-Loop5.html b/references/intro/diff/Loop4-Loop5.html new file mode 100644 index 0000000..6a84a59 --- /dev/null +++ b/references/intro/diff/Loop4-Loop5.html @@ -0,0 +1,230 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop4.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop5.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
22
3object Loop4 extends App {3object Loop5 extends App {
44
5  def iterate(max: Int): List[Int] = {5  def iterate(max: Int): List[Int] = {
6    var result = List[Int]()6    var result = List[Int]()
77
8    for (x <- 1 to max)8    for (x <- 1 to max)
9      result = result :+ x9      result = result :+ x
1010
11    result11    result
12  }12  }
1313
14  def filterEven(xs: List[Int]): List[Int] = {14  def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] = {
1515
16    @tailrec16    @tailrec
17    def loop(result: List[Int], xs: List[Int]): List[Int] = xs match {17    def loop(xs: List[Int], result: List[Int]): List[Int] = xs match {
18      case Nil => result18      case Nil => result
19      case x :: tail =>19      case x :: tail =>
20        val next = if (x % 2 == 0) List(x) else Nil
21        loop(next ++ result, tail)20        loop(tail, f(x) ++ result)
22    }21    }
2322
24    loop(Nil, xs)23    loop(xs, Nil)
25  }24  }
2625
27  def square(xs: List[Int]): List[Int] = {26  def filterEven(xs: List[Int]): List[Int] =
27    flatMap(xs, x => if (x % 2 == 0) List(x) else Nil)
2828
29    @tailrec
30    def loop(result: List[Int], xs: List[Int]): List[Int] = xs match {29  def square(xs: List[Int]): List[Int] =
31      case Nil => result
32      case x :: tail =>
33        val next = List(x * x)30    flatMap(xs, x => List(x * x))
34        loop(next ++ result, tail)
35    }
36
37    loop(Nil, xs)
38  }
3931
40  def sum(xs: List[Int]): Int = {32  def sum(xs: List[Int]): Int = {
4133
42    @tailrec34    @tailrec
43    def loop(result: Int, xs: List[Int]): Int = xs match {35    def loop(result: Int, xs: List[Int]): Int = xs match {
44      case Nil => result36      case Nil => result
45      case x :: tail =>37      case x :: tail =>
46        val next = x38        val next = x
47        loop(next + result, tail)39        loop(next + result, tail)
48    }40    }
4941
50    loop(0, xs)42    loop(0, xs)
51  }43  }
5244
53  val result = sum(square(filterEven(iterate(10))))45  val result = sum(square(filterEven(iterate(10))))
5446
55  println(s"Sum of even numbers from 1 to 10 is $result")47  println(s"Sum of even numbers from 1 to 10 is $result")
5648
57}49}
5850
+ diff --git a/references/intro/diff/Loop5-Loop6.html b/references/intro/diff/Loop5-Loop6.html new file mode 100644 index 0000000..e7fada9 --- /dev/null +++ b/references/intro/diff/Loop5-Loop6.html @@ -0,0 +1,212 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop5.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop6.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
22
3object Loop5 extends App {3object Loop6 extends App {
44
5  def iterate(max: Int): List[Int] = {5  def iterate(max: Int): List[Int] = {
6    var result = List[Int]()6    var result = List[Int]()
77
8    for (x <- 1 to max)8    for (x <- 1 to max)
9      result = result :+ x9      result = result :+ x
1010
11    result11    result
12  }12  }
1313
14  def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] = {14  def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = {
1515
16    @tailrec16    @tailrec
17    def loop(xs: List[Int], result: List[Int]): List[Int] = xs match {17    def loop(xs: List[A], result: R): R = xs match {
18      case Nil => result18      case Nil => result
19      case x :: tail =>19      case x :: tail =>
20        loop(tail, f(x) ++ result)20        loop(tail, combine(result, x))
21    }21    }
2222
23    loop(xs, Nil)23    loop(xs, zero)
24  }24  }
2525
26  def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] =
27    foldLeft(xs)(List[Int]())((acc, x) => f(x) ++ acc)
28
26  def filterEven(xs: List[Int]): List[Int] =29  def filterEven(xs: List[Int]): List[Int] =
27    flatMap(xs, x => if (x % 2 == 0) List(x) else Nil)30    flatMap(xs, x => if (x % 2 == 0) List(x) else Nil)
2831
29  def square(xs: List[Int]): List[Int] =32  def square(xs: List[Int]): List[Int] =
30    flatMap(xs, x => List(x * x))33    flatMap(xs, x => List(x * x))
3134
32  def sum(xs: List[Int]): Int = {35  def sum(xs: List[Int]): Int =
3336    foldLeft(xs)(0)((acc, x) => acc + x)
34    @tailrec
35    def loop(result: Int, xs: List[Int]): Int = xs match {
36      case Nil => result
37      case x :: tail =>
38        val next = x
39        loop(next + result, tail)
40    }
41
42    loop(0, xs)
43  }
4437
45  val result = sum(square(filterEven(iterate(10))))38  val result = sum(square(filterEven(iterate(10))))
4639
47  println(s"Sum of even numbers from 1 to 10 is $result")40  println(s"Sum of even numbers from 1 to 10 is $result")
4841
49}42}
5043
+ diff --git a/references/intro/diff/Loop6-Loop7.html b/references/intro/diff/Loop6-Loop7.html new file mode 100644 index 0000000..016c0a8 --- /dev/null +++ b/references/intro/diff/Loop6-Loop7.html @@ -0,0 +1,206 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop6.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop7.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
22
3object Loop6 extends App {3object Loop7 extends App {
44
5  def iterate(max: Int): List[Int] = {5  def iterate(max: Int): List[Int] = {
6    var result = List[Int]()6    var result = List[Int]()
77
8    for (x <- 1 to max)8    for (x <- 1 to max)
9      result = result :+ x9      result = result :+ x
1010
11    result11    result
12  }12  }
1313
14  def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = {14  def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = {
1515
16    @tailrec16    @tailrec
17    def loop(xs: List[A], result: R): R = xs match {17    def loop(xs: List[A], result: R): R = xs match {
18      case Nil => result18      case Nil => result
19      case x :: tail =>19      case x :: tail =>
20        loop(tail, combine(result, x))20        loop(tail, combine(result, x))
21    }21    }
2222
23    loop(xs, zero)23    loop(xs, zero)
24  }24  }
2525
26  def flatMap(xs: List[Int], f: Int => List[Int]): List[Int] =26  def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] =
27    foldLeft(xs)(List[Int]())((acc, x) => f(x) ++ acc)27    foldLeft(xs)(List[B]())((acc, x) => f(x) ++ acc)
2828
29  def filterEven(xs: List[Int]): List[Int] =29  def filter[A](xs: List[A])(f: A => Boolean): List[A] =
30    flatMap(xs, x => if (x % 2 == 0) List(x) else Nil)30    flatMap(xs)(x => if (f(x)) List(x) else Nil)
3131
32  def square(xs: List[Int]): List[Int] =32  def map[A, B](xs: List[A])(f: A => B): List[B] =
33    flatMap(xs, x => List(x * x))33    flatMap(xs)(x => List(f(x)))
34
35  def isEven(x: Int): Boolean = x % 2 == 0
36
37  def square(x: Int): Int = x * x
3438
35  def sum(xs: List[Int]): Int =39  def sum(xs: List[Int]): Int =
36    foldLeft(xs)(0)((acc, x) => acc + x)40    foldLeft(xs)(0)((acc, x) => acc + x)
3741
42  def sumOfEvenSquares(max: Int): Int = {
43    sum(map(filter(iterate(max))(isEven))(square))
44  }
45
38  val result = sum(square(filterEven(iterate(10))))46  val result = sumOfEvenSquares(10)
3947
40  println(s"Sum of even numbers from 1 to 10 is $result")48  println(s"Sum of even numbers from 1 to 10 is $result")
4149
42}50}
4351
+ diff --git a/references/intro/diff/Loop7-Loop8.html b/references/intro/diff/Loop7-Loop8.html new file mode 100644 index 0000000..9172d68 --- /dev/null +++ b/references/intro/diff/Loop7-Loop8.html @@ -0,0 +1,215 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop7.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop8.scala
1import scala.annotation.tailrec
2
3object Loop7 extends App {1object Loop8 extends App {
4
5  def iterate(max: Int): List[Int] = {
6    var result = List[Int]()
7
8    for (x <- 1 to max)
9      result = result :+ x
10
11    result
12  }
13
14  def foldLeft[A, R](xs: List[A])(zero: R)(combine: (R, A) => R): R = {
15
16    @tailrec
17    def loop(xs: List[A], result: R): R = xs match {
18      case Nil => result
19      case x :: tail =>
20        loop(tail, combine(result, x))
21    }
22
23    loop(xs, zero)
24  }
25
26  def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] =
27    foldLeft(xs)(List[B]())((acc, x) => f(x) ++ acc)
28
29  def filter[A](xs: List[A])(f: A => Boolean): List[A] =
30    flatMap(xs)(x => if (f(x)) List(x) else Nil)
31
32  def map[A, B](xs: List[A])(f: A => B): List[B] =
33    flatMap(xs)(x => List(f(x)))
342
35  def isEven(x: Int): Boolean = x % 2 == 03  def isEven(x: Int): Boolean = x % 2 == 0
364
37  def square(x: Int): Int = x * x5  def square(x: Int): Int = x * x
386
39  def sum(xs: List[Int]): Int =
40    foldLeft(xs)(0)((acc, x) => acc + x)
41
42  def sumOfEvenSquares(max: Int): Int = {7  def sumOfEvenSquares(max: Int): Int = {
43    sum(map(filter(iterate(max))(isEven))(square))8    (1 to max)
9      .filter(isEven)
10      .map(square)
11      .sum
44  }12  }
4513
46  val result = sumOfEvenSquares(10)14  val result = sumOfEvenSquares(10)
4715
48  println(s"Sum of even numbers from 1 to 10 is $result")16  println(s"Sum of even numbers from 1 to 10 is $result")
4917
50}18}
5119
+ From 9e8a620e743b353bce851a1600f273d0bf4a8fa6 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:43:04 +0800 Subject: [PATCH 12/14] Scrub filesystem path --- references/intro/diff/Loop3-Loop4.html | 4 ++-- references/intro/diff/Loop4-Loop5.html | 4 ++-- references/intro/diff/Loop5-Loop6.html | 4 ++-- references/intro/diff/Loop6-Loop7.html | 4 ++-- references/intro/diff/Loop7-Loop8.html | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/references/intro/diff/Loop3-Loop4.html b/references/intro/diff/Loop3-Loop4.html index b632c7f..82ecaee 100644 --- a/references/intro/diff/Loop3-Loop4.html +++ b/references/intro/diff/Loop3-Loop4.html @@ -46,8 +46,8 @@ - - + + diff --git a/references/intro/diff/Loop4-Loop5.html b/references/intro/diff/Loop4-Loop5.html index 6a84a59..7c8f24e 100644 --- a/references/intro/diff/Loop4-Loop5.html +++ b/references/intro/diff/Loop4-Loop5.html @@ -46,8 +46,8 @@
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop3.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop4.scala~/scala-workshop/references/intro/src/main/scala/Loop3.scala~/scala-workshop/references/intro/src/main/scala/Loop4.scala
1import scala.annotation.tailrec
- - + + diff --git a/references/intro/diff/Loop5-Loop6.html b/references/intro/diff/Loop5-Loop6.html index e7fada9..e8f49d8 100644 --- a/references/intro/diff/Loop5-Loop6.html +++ b/references/intro/diff/Loop5-Loop6.html @@ -46,8 +46,8 @@
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop4.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop5.scala~/scala-workshop/references/intro/src/main/scala/Loop4.scala~/scala-workshop/references/intro/src/main/scala/Loop5.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
- - + + diff --git a/references/intro/diff/Loop6-Loop7.html b/references/intro/diff/Loop6-Loop7.html index 016c0a8..2649858 100644 --- a/references/intro/diff/Loop6-Loop7.html +++ b/references/intro/diff/Loop6-Loop7.html @@ -46,8 +46,8 @@
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop5.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop6.scala~/scala-workshop/references/intro/src/main/scala/Loop5.scala~/scala-workshop/references/intro/src/main/scala/Loop6.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
- - + + diff --git a/references/intro/diff/Loop7-Loop8.html b/references/intro/diff/Loop7-Loop8.html index 9172d68..9574fd4 100644 --- a/references/intro/diff/Loop7-Loop8.html +++ b/references/intro/diff/Loop7-Loop8.html @@ -46,8 +46,8 @@
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop6.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop7.scala~/scala-workshop/references/intro/src/main/scala/Loop6.scala~/scala-workshop/references/intro/src/main/scala/Loop7.scala
1import scala.annotation.tailrec1import scala.annotation.tailrec
- - + + From e776285b44fbb5e0085fc82d1b1ae40f9336ac49 Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:53:54 +0800 Subject: [PATCH 13/14] Add links to diff preview --- exercises/intro/README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index f56c0a1..3aaf122 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -54,7 +54,7 @@ public class Loop { } ``` -Source: [Loop1.java] +> Source: [Loop1.java] This program is, on purpose, easy enough to understand. Consider however, that in your real programming work you might deal with several complexities at the same @@ -105,7 +105,7 @@ object Main extends App { // 1. } ``` -Source: [Loop2.scala] +> Source: [Loop2.scala] First you notice that Scala is less verbose: no semicolons needed, no `public static void main`, no explicit reassignment of `x` in `for` loop. Here are @@ -200,7 +200,7 @@ def sum(xs: List[Int]): Int = { val result = sum(square(filterEven(iterate(10)))) ``` -Source: [Loop3.scala] +> Source: [Loop3.scala] Wow! The number of lines of code just exploded and we introduced a lot of duplication along the way. Let's review the function body implementation: @@ -342,7 +342,8 @@ def sum(xs: List[Int]): Int = { } ``` -Source: [Loop4.scala] +> Source: [Loop4.scala]
+> Diff: [Loop3.scala => Loop4.scala] Here we pattern match on a list instead of integer. Note how we used the same list construction syntax in the `case` expression to capture list's head `x` @@ -390,7 +391,8 @@ def square(xs: List[Int]): List[Int] = flatMap(xs, x => List(x * x)) ``` -Source: [Loop5.scala] +> Source: [Loop5.scala]
+> Diff: [Loop4.scala => Loop5.scala] 1. `flatMap` takes function `f` as its second argument; 2. `A => B` is type annotation for a function that takes an argument of type @@ -441,7 +443,8 @@ def sum(xs: List[Int]): Int = foldLeft(xs)(0)((acc, x) => acc + x) ``` -Source: [Loop6.scala] +> Source: [Loop6.scala]
+> Diff: [Loop5.scala => Loop6.scala] 1. `foldLeft[A, R]` is parametrized on type of the input list `A` as well as the type of its return value `R` thus `foldLeft` is a *generic* function; @@ -486,7 +489,8 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -Source: [Loop7.scala] +> Source: [Loop7.scala]
+> Diff: [Loop6.scala => Loop7.scala] Here, `square` really does only what it says and doesn't mess with lists anymore, which is now the responsibility of `map`. @@ -511,7 +515,8 @@ def sumOfEvenSquares(max: Int): Int = { } ``` -Source: [Loop8.scala] +> Source: [Loop8.scala]
+> Diff: [Loop7.scala => Loop8.scala] We use `1 to 10` directly instead of our own `iterate` function. `filter`, `map`, `sum` as well as `flatMap`, `foldLeft` and many more functions are @@ -532,8 +537,13 @@ transformations without changing the existing ones. [Loop1.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop1.scala [Loop2.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala [Loop3.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala +[Loop3.scala => Loop4.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop3-Loop4.html [Loop4.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop4.scala +[Loop4.scala => Loop5.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop4-Loop5.html [Loop5.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop5.scala +[Loop5.scala => Loop6.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop5-Loop6.html [Loop6.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop6.scala +[Loop6.scala => Loop7.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop6-Loop7.html [Loop7.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop7.scala +[Loop7.scala => Loop8.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop7-Loop8.html [Loop8.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop8.scala From 74e41e2d7dcdbd3b679495414815c420f19d339c Mon Sep 17 00:00:00 2001 From: Herdy Handoko Date: Wed, 13 Sep 2017 09:55:46 +0800 Subject: [PATCH 14/14] Fix link to `Loop1.java` file --- exercises/intro/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/intro/README.md b/exercises/intro/README.md index 3aaf122..095e8d0 100644 --- a/exercises/intro/README.md +++ b/exercises/intro/README.md @@ -534,7 +534,7 @@ change the order of operations in predictable ways and we can add more transformations without changing the existing ones. -[Loop1.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop1.scala +[Loop1.java]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/java/Loop1.java [Loop2.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop2.scala [Loop3.scala]: https://github.com/SingaporeScalaProgrammers/scala-workshop/blob/master/references/intro/src/main/scala/Loop3.scala [Loop3.scala => Loop4.scala]: https://rawgit.com/SingaporeScalaProgrammers/scala-workshop/master/references/intro/diff/Loop3-Loop4.html
/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop7.scala/Users/hhandoko/Workspace/Repositories/scala-workshop/references/intro/src/main/scala/Loop8.scala~/scala-workshop/references/intro/src/main/scala/Loop7.scala~/scala-workshop/references/intro/src/main/scala/Loop8.scala
1import scala.annotation.tailrec