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/exercises/intro/.gitignore b/exercises/intro/.gitignore new file mode 100644 index 0000000..fecd5e1 --- /dev/null +++ b/exercises/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/intro/README.md b/exercises/intro/README.md similarity index 75% rename from intro/README.md rename to exercises/intro/README.md index 8946ca6..095e8d0 100644 --- a/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: @@ -50,15 +54,18 @@ public class Loop { } ``` -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? +> 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 +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). Two cornerstones of imperative programming languages contribute to the problem here: @@ -98,6 +105,8 @@ object Main extends App { // 1. } ``` +> 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 other notable differences: @@ -129,6 +138,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; +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; +3. `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]() @@ -170,15 +200,15 @@ def sum(xs: List[Int]): Int = { val result = sum(square(filterEven(iterate(10)))) ``` +> 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 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; +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); +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 @@ -190,15 +220,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. @@ -256,7 +289,7 @@ recursion though; 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; -4. `case _` matches any value; +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. @@ -309,6 +342,9 @@ def sum(xs: List[Int]): Int = { } ``` +> 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` and its `tail`. `++` concatenates two lists. @@ -355,6 +391,9 @@ def square(xs: List[Int]): List[Int] = flatMap(xs, x => List(x * x)) ``` +> 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 `A` and returns a value of type `B`; @@ -404,6 +443,9 @@ def sum(xs: List[Int]): Int = foldLeft(xs)(0)((acc, x) => acc + x) ``` +> 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; 2. Scala functions can have multiple parameter lists. Here it's useful for @@ -443,18 +485,21 @@ 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)) } ``` -Here `square` really does only what it says and doesn't mess with lists -anymore, that's responsibility of `map` now. +> 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`. 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 +"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 @@ -463,13 +508,16 @@ 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 } ``` +> 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 available as methods on any Scala collection. This allows using the fluid API @@ -484,3 +532,18 @@ 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.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 +[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 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/intro/src/main/scala/Main.scala b/exercises/intro/src/main/scala/Main.scala similarity index 99% rename from intro/src/main/scala/Main.scala rename to exercises/intro/src/main/scala/Main.scala index e729297..ece9140 100644 --- a/intro/src/main/scala/Main.scala +++ b/exercises/intro/src/main/scala/Main.scala @@ -7,4 +7,5 @@ object Main extends App { } println(s"Sum of even numbers from 1 to 10 is $sum") + } diff --git a/intro/.gitignore b/intro/.gitignore deleted file mode 100644 index 07827cc..0000000 --- a/intro/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target/ -.idea/ \ No newline at end of file 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/diff/Loop3-Loop4.html b/references/intro/diff/Loop3-Loop4.html new file mode 100644 index 0000000..82ecaee --- /dev/null +++ b/references/intro/diff/Loop3-Loop4.html @@ -0,0 +1,260 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~/scala-workshop/references/intro/src/main/scala/Loop3.scala~/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..7c8f24e --- /dev/null +++ b/references/intro/diff/Loop4-Loop5.html @@ -0,0 +1,230 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~/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
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..e8f49d8 --- /dev/null +++ b/references/intro/diff/Loop5-Loop6.html @@ -0,0 +1,212 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~/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
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..2649858 --- /dev/null +++ b/references/intro/diff/Loop6-Loop7.html @@ -0,0 +1,206 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~/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
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..9574fd4 --- /dev/null +++ b/references/intro/diff/Loop7-Loop8.html @@ -0,0 +1,215 @@ + + + + + File Compare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~/scala-workshop/references/intro/src/main/scala/Loop7.scala~/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
+ 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/references/intro/src/main/java/Loop1.java b/references/intro/src/main/java/Loop1.java new file mode 100644 index 0000000..d3146d8 --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop2.scala b/references/intro/src/main/scala/Loop2.scala new file mode 100644 index 0000000..bbd0deb --- /dev/null +++ b/references/intro/src/main/scala/Loop2.scala @@ -0,0 +1,11 @@ +object Loop2 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/src/main/scala/Loop3.scala b/references/intro/src/main/scala/Loop3.scala new file mode 100644 index 0000000..0bac4b7 --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop4.scala b/references/intro/src/main/scala/Loop4.scala new file mode 100644 index 0000000..f9e8d13 --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop5.scala b/references/intro/src/main/scala/Loop5.scala new file mode 100644 index 0000000..71e869c --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop6.scala b/references/intro/src/main/scala/Loop6.scala new file mode 100644 index 0000000..4722818 --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop7.scala b/references/intro/src/main/scala/Loop7.scala new file mode 100644 index 0000000..2ee0410 --- /dev/null +++ b/references/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/references/intro/src/main/scala/Loop8.scala b/references/intro/src/main/scala/Loop8.scala new file mode 100644 index 0000000..06e14db --- /dev/null +++ b/references/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") + +}