CS 245 - Programming Languages

Lab 9 and Homework 5

Functional programming in Rust

Iterators, and iterator functions, in rust allow you to program in a fairly functional style. Often this is referred to as using "higher level functions", because you put two (or more) functions together to get a result. Using a functional style allows programmers to sidestep some general annoyances in rust; things like borrowing restrictions and immutability.

The basic idea of iterators is to start with something that you can get an iterator on; an array or a slice is what we have discussed. Then, get the iterator. Finally, find an iterator function that is useful to you. Many iterator functions do not require you to do much. For example:

    fn main() {
        let v = vec![1,2,3,4,5,6,7,8,9];
        let sm :i32 = v.iter().sum();
        println!("{:?} sums to {}", v, sm);
    }    
Other iterator functions require you to provide an anonymous function to do work. For example:
    fn main() {
        let v = vec![1,2,3,4,5,6,7,8,9];
        let sm :i32 = v.iter().fold(0, |acc, e| acc+e );
        println!("{:?} sums to {}", v, sm);
        let sm :i32 = v.iter().fold(0, |acc, e| acc+e*e );
        println!("{:?} sum of squares to {}", v, sm);
    }
the fold starts with a value (0 in the examples) and passes that value into the provided anonymous function as the first parameter with the second parameter coming from the iterator. The int passes the result of the function as the value in the next iteration. So the first use of fold does exactly a sum of the items in the vector.

The iterator functions are well documented at https://doc.rust-lang.org/std/iter/trait.Iterator.html

In some cases, iterator functions themselves return iterators. In this case, you can use the "collect()" function to turn the iterator into something more usable, like a vector. For example, the following example starts with a vector of integers and results in a vector of integers in which each element of the original list is cubed.
    fn main() {
        let v = vec![1,2,3,4,5,6,7,8,9];
        let vvv:Vec = v.iter().map(|e| e*e*e).collect();
        println!("{:?} transforms to {:?}", v, vvv);
    }

Frequently you can do fairly complex operations by passing the results of one iterator function to another iterator function. In this lab/assignment you will not do anything terribly complex, just do some fairly straightforward things with iterators.

This document is the basis of BOTH lab 9 and Homework 5. The basic idea is that you do the first two "problems" as lab 9 and the last 4 as homework 5.

Problem 1

Transform a vector of string literals into a vector of lower case String. Here is a starting point
    let v = vec!["Aa", "SS", "dd", "FF", "Geoff"];
Do this in 2 steps. First transform the string literals into Strings, then lower case the String. (Yes I know you can do this in one step). (Hint, use map.)

Problem 2

Transform the set of command line parameters into a single number which is the product of the sizes of each string. Do this in two steps, first using map, then using fold. For instance, if the command line arguments are
    aa bb cc abcde cc
then the result should be 80.

Problem 3

Print each command line argument on a line by itself along with its length. For instance, if the command line arguments are
    aa bb cc abcde cc
the result should be
    aa 2
    bb 2
    cc 2
    abcde 5
    cc 2
Hint use for_each

Problem 4

With the command line arguments, create a vector of only those that start with an integer. So if the command line arguments are:
    1a a2 33 66t ttt
then you should end up with only
    1a 33 66t

Problem 5

In this exercise you will do a version of a Caesar cypher -- you may a cheap version in that it need not do the "circular" thing where you do 'z'+1 becomes 'a'. Start by creating a string of all lower case characters and spaces. Turn that into an array of characters as follows:
    let v :Vec = String::from("we the people of the united states").chars().collect();
Note that the "chars" function returns an iterator from an owned string. Now use an iterator function to put all of these characters back into a string, AFTER shifting each character by +1. Finally, print the resulting string. If you used my string, your printout should be:
    xf!uif!qfpqmf!pg!uif!vojufe!tubuft
Finally, put your encoder into a function that takes a string and a number and returns a string. In a main function, pass a string from the command line and +1. Print that string. Then pass the returned string back to the function with -1. Print the decoded string. You should get back your original string.

Problem 6

Iterators in Rust are said to be "lazy". What does this mean? Use the fact that the ".." operator in Rust generates an iterator to validate that iterators are actually lazy. For example, below is a use of ".."
    for i in 0..10 {
        println("{}", i);
    }
Explain how your program demonstrates that iterators are actually lazy.

What to hand in for Lab 9

Work no more than 80 minutes on the first two problems. If you did not complete everything, send what you have.

Send email to gtowell@brynmawr.edu with your re-implementations using reduce.

What to hand in for HW 5

Turn in solutions for problems 3-6 as Homework 5. Each problem solution should be in its own .rs file. In addition, problem 6 asks for some discussion. That discussion should either be in its own file or a part of the usual readme.

Before you submit your work, run "cargo clean" in each of the directories you created with cargo. Doing so will get rid of a bunch of stuff (intermediate compilation files, etc) and save a lot of space. Not cleaning before submitting will result in up to a 2 point penalty per uncleaned directory. If the clean is not complete (several people reported issues with clean) that is OK. The total size of each directory you turn in should be less that 200KB. (To get the total size of a directory, you can use the unix "du" command.) For instance, the space used before cleaning in one of my rust directories was 3456 KB. After cleaning it was 72KB.

How to Submit

Put everything you want to submit in a directory containing nothing else. For instance you might name the directory a4

  1. Go to the directory containing a4
  2. Enter /home/gtowell/bin/submit -c 245 -p 4 -d a4
If this worked you will get a message with the word "success" and a listing of all the files you submitted.