User Tools

Site Tools


cs326:lab3

This is an old revision of the document!


CSci 326 - Lab #3

The purpose of this exercise is to gain experience using Lazy Streams.

In this lab, first work through each example from the Elixir Guide regarding the pipe operator and streams.

Then, try each set of exercises in turn:

  1. This example from Chapter 10 of the text should work on Linux or MacOS. Try it and record the result:
    File.read!("/usr/share/dict/words") |> String.split |> Enum.max_by(&String.length/1)
  2. Dave Thomas makes the point that reading this file into memory takes about 2.4Mbytes of space; each processing step creates a collection of around that size to do its work. How might we, instead, work on a single line of the file at a time, and take much less memory. This example illustrates the idea:
    File.stream!("/usr/share/dict/words") |> Enum.max_by(&String.length/1)

    In this example, we turn the file contents into a stream of data. On the one hand, this way is slower. On the other, it works well when reading an indeterminate amount of data over a network connection or slow input device. We can operate on each part as it comes in, rather than waiting to receive all the data before starting the operations.

  3. Here we process 10 million integers, and just show the first 5:
    Enum.map(1..10_000_000, &(&1+1)) |> Enum.take(5)

    Compare this with using a stream:

    Stream.map(1..10_000_000, &(&1+1)) |> Enum.take(5)

    Which is faster? Give a reason why this might be.

  4. This example generates some HTML code. It uses Stream.cycle to alternate between green and white, Stream.zip to pair up these strings with the numbers 1 through 5 as tuples, and then executes code to generate some HTML table rows from these pairs:
    Stream.cycle(~w{ green white }) |> Stream.zip(1..5) |> Enum.map(fn {class, value} -> "    <tr class='#{class}'><td>#{value}</td></tr>\n" end) |> IO.puts

    This simpler example might give you a better idea of what each part of the expression is doing:

    Stream.cycle(~w{ green white }) |> Stream.zip(1..5) |> (Enum.take 3)

    Vary the argument to Enum.take between 2 and 5.

Part 2.

  1. Let's see how to use a new function Stream.unfold to generate the first n Fibonacci numbers. First, decide how many numbers you want in the sequence and assign that to n. Then run this expression:
    Stream.unfold({0,1}, fn {f1,f2} -> {f1, {f2, f1+f2}} end) |> Enum.take(n)

    Write down the first 15 numbers produced.

  2. This example is very interesting, but involved, so I won't try to go through the code; read Chapter 10, pp. 107-110 for an explanation later. It uses a timer to figure out what second has elapsed from minute to minute, and we'll make a function to print each one out a second apart.
    1. Download the file countdown.exs
    2. load it into iex and compile using the command c “countdown.exs”
    3. Call counter = Countdown.timer which creates a new timer function
    4. Call printer = counter |> Stream.each(&IO.puts/1) which creates a function to print each second the counter generates
    5. Call speaker = printer |> Stream.each(&Countdown.say/1) which is a surprise (if it works!)
    6. Now ask for a certain number of seconds, for example the next 5 seconds: speaker |> Enum.take(5)

Explain what happens.

cs326/lab3.1632863625.txt.gz · Last modified: 2021/09/28 16:13 by scarl