{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# For Comprehensions\n", "\n", "Scala offers a lightweight notation for expressing *sequence comprehensions*. Comprehensions have the form `for (enumerators) yield e`, where `enumerators` refers to a semicolon-separated list of enumerators. An *enumerator* is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body `e` for each binding generated by the enumerators and returns a sequence of these values.\n", "\n", "Here's an example:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Travis\n", "Dennis\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mUser\u001b[39m\n", "\u001b[36muserBase\u001b[39m: \u001b[32mList\u001b[39m[\u001b[32mUser\u001b[39m] = \u001b[33mList\u001b[39m(\n", " \u001b[33mUser\u001b[39m(\u001b[32m\"Travis\"\u001b[39m, \u001b[32m28\u001b[39m),\n", " \u001b[33mUser\u001b[39m(\u001b[32m\"Kelly\"\u001b[39m, \u001b[32m33\u001b[39m),\n", " \u001b[33mUser\u001b[39m(\u001b[32m\"Jennifer\"\u001b[39m, \u001b[32m44\u001b[39m),\n", " \u001b[33mUser\u001b[39m(\u001b[32m\"Dennis\"\u001b[39m, \u001b[32m23\u001b[39m)\n", ")\n", "\u001b[36mtwentySomethings\u001b[39m: \u001b[32mList\u001b[39m[\u001b[32mString\u001b[39m] = \u001b[33mList\u001b[39m(\u001b[32m\"Travis\"\u001b[39m, \u001b[32m\"Dennis\"\u001b[39m)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "case class User(name: String, age: Int)\n", "\n", "val userBase = List(User(\"Travis\", 28),\n", " User(\"Kelly\", 33),\n", " User(\"Jennifer\", 44),\n", " User(\"Dennis\", 23))\n", "\n", "val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30))\n", " yield user.name // i.e. add this to a list\n", "\n", "twentySomethings.foreach(name => println(name)) // prints Travis Dennis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `for` loop used with a `yield` statement actually creates a `List`. Because we said `yield user.name`, it's a `List[String]`. `user <- userBase` is our generator and `if (user.age >=20 && user.age < 30)` is a guard that filters out users who are not in their 20s.\n", "\n", "Here is a more complicated example using two generators. It computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 9) \n", "(2, 8) \n", "(3, 7) \n", "(4, 6) \n", "(5, 5) \n" ] }, { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mfoo\u001b[39m" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def foo(n: Int, v: Int) =\n", " for (i <- 0 until n;\n", " j <- i until n if i + j == v)\n", " yield (i, j)\n", "\n", "foo(10, 10) foreach {\n", " case (i, j) =>\n", " println(s\"($i, $j) \") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5)\n", "}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here `n == 10` and `v == 10`. On the first iteration, `i == 0` and `j == 0` so `i + j != v` and therefore nothing is yielded. `j` gets incremented 9 more times before `i` gets incremented to `1`. Without the `if` guard, this would simply print the following:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "\n", "(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ...\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that comprehensions are not restricted to lists. Every datatype that supports the operations `withFilter`, `map`, and `flatMap` (with the proper types) can be used in sequence comprehensions.\n", "\n", "You can omit `yield` in a comprehension. In that case, comprehension will return `Unit`. This can be useful in case you need to perform side-effects. Here's a program equivalent to the previous one, but without using `yield`:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 9)\n", "(2, 8)\n", "(3, 7)\n", "(4, 6)\n", "(5, 5)\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mfoo\u001b[39m" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def foo(n: Int, v: Int) =\n", " for (i <- 0 until n;\n", " j <- i until n if i + j == v)\n", " println(s\"($i, $j)\")\n", "\n", "foo(10, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Scala (2.13)", "language": "scala", "name": "scala213" }, "language_info": { "codemirror_mode": "text/x-scala", "file_extension": ".scala", "mimetype": "text/x-scala", "name": "scala", "nbconvert_exporter": "script", "version": "2.13.1" } }, "nbformat": 4, "nbformat_minor": 4 }