CS 245 - Programming Languages

Homework 4

Elixir Programs

In this lab you will write four small programs in Elixir. Each of your programs should be it its own module and that module in its own file. (Yes, you could put them all in the same module, or all of the modules into a single file.)

Each module and public function should be documented using @moduledoc and @doc. (See code posted on the class website for examples.) Documentation does not need to be extensive, but should be sufficient to help the grader read and understand your code.

Each program should be accompanied by a set of tests sufficient to demonstrate that your program works. You may assume that someone else is checking for valid input. So the tests need only demonstrate that your program works on "good" input. For instance, in the first program, you may assume that the input is actually a list rather than a string or a tuple, ... However, you should have several list structures.

Part 1

Write a program which flattens lists of unlimited depth. Specifically, consider a list. The elements in the list are items which may be either something or lists. The contained lists abide by the same rule as the top level list. For example, the following is a legal list:
        [12, 6, [1, [2]],[3,4]]
        
Your program should "flatten" the provided list and return the flattened list. Items in the flattened list my be in any order. For instance, for the first example above, the following is an acceptable result:
        [6,12,3,4,1,2]
    
I have no idea why you program would produce such a result, but it would be OK. (One version of my implementation preserves order.)

Some thoughts:

Part 2

Write a module named Student that contains a struct with two properties: name and id_number. Within that module, add a function named register_students that takes a list of names and, optionally, a starting number. If no starting number is supplied, use 0. The function should return a list of Student structs. You function(s) should NOT contain any if (or case or cond). Do everything using matching.

For example:

IO.inspect Student.register_students(["mary", "jane", "grace"])
[
  %Student{id_number: 0, name: "mary"},
  %Student{id_number: 1, name: "jane"},
  %Student{id_number: 2, name: "grace"}
]


IO.inspect Student.register_students(["lily", "alana", "quinn"], 5501)
[
  %Student{id_number: 5501, name: "lily"},
  %Student{id_number: 5502, name: "alana"},
  %Student{id_number: 5503, name: "quinn"}
]
        

Implement two versions of register_students. In the first, write you own recursive function. In the second, use Enum.map rather than recursion. Only one version is required. Implement both a recursive and Enum.map version for a 5 point bonus. (Do everything else before doing both.)

Finally, suppose you are given two lists of registered students. Write a function to return a single list, without duplications (where a duplication is defined as two students having the same ID number). Note that there may be duplications within each list and well as between the two lists. Hint: one fairly efficient approach is to first sort the combined lists and then remove duplicate neighbors (you can use Enum.sort and Enum.dedup to do so).

Part 3 -- Using Enum

Write a program using function(s) in Enum to do the following. Given a list of strings, return only those strings that are
  1. in odd numbered positions in the list. That is if the list is [a,b,c,d,e] then you should only get [b, d] as they are in positions 1 and 3 (when you start counting from 0).
  2. are longer than 5 characters
In your program:

Part 4 -- Indices

Almost totally missing from the Enum package are functions that are dependent on position in the list. So write two.

Start by creating a module named EnumIndex. Within this module you will create two functions, as described below:

map_index

The map_index function is very similar to Enum.map. It takes two arguments: a list and a function. Unlike map, the input function should take two parameters: a value from the list and the index of the index of the item in the list. Here is an example of the usage of map_index
    iex(1)> EnumIndex.map_index(["a", "s", "d", "f", "g"], fn (val, idx) -> {val, idx} end)

    [{"a", 0}, {"s", 1}, {"d", 2}, {"f", 3}, {"g", 4}]

filter_index

The filter_index function is very similar to Enum.filter. It takes two arguments: a list and a function. Unlike filter, the input function should take two parameters: a value from the list and the index of the index of the item in the list. Here is an example of the usage of filter_index
    iex(1)> EnumIndex.filter_index(["a", "s", "d", "f", "g"], fn (val, idx) -> idx>1 and val>"e" end)
    ["f", "g"]

Questions:

What to hand in

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.