CS 245 - Programming Languages

Lab 7 and 8

Little Elixir Programs

In this lab you will write a series of small programs in Elixir. The intent here is simply to practice with Elixir before the first Elixir-based programming assignment.

For lab 8, continue where you left off from lab 7.

Fizz Buzz

Write a program that prints the numbers 1 .. 100 except: if the number is divisible by 3 print "Fizz", if the number is divisible by 5 print "Buzz", if the number is divisible by 3 and 5 print "FizzBuzz".

K to M conversion

Write a program that takes command line input from a user to convert miles to kilometers or kilometers to miles. (1 mile equals 1.609km) For instance, if your elixir program is in k2m.ex
        UNIX> elixir k2m.ex 20 k
    
You can get the command line parameters in elixir by "System.argv". For example:
    defmodule CR do
      def main2() do
        IO.inspect(System.argv())
      end
    end
    
    CR.main2
    
Then usage would be
UNIX> elixir commread.ex
    []
UNIX> elixir commread.ex 1 2 3 4 5
    ["1", "2", "3", "4", "5"]
    

There are two ways to get the first element in a list; using the hd (head) function which can be combined with tl (tail). Both can be used with the pipe operator (or not) as below. Or you can use matching. All of this is illustrated below:

    iex(16)> a=[1,2,3,4]
    [1, 2, 3, 4]
    iex(17)> hd(a)
    1
    iex(18)> tl(a)
    [2, 3, 4]
    iex(19)> hd(tl(a))
    2
    iex(20)> a |> hd
    1
    iex(21)> a |> tl |> hd
    2
    iex(22)>
    nil
    iex(23)> [b|c]=a
    [1, 2, 3, 4]
    iex(24)> b
    1
    iex(25)> c
    [2, 3, 4]    

To convert a string to an integer use the "parse" function of the integer package as illustrated here:

    iex(9)> q=Float.parse("11.3 abc")
    {11.3, " abc"}
    iex(10)> {ff, _} = q
    {11.3, " abc"}
    iex(11)> ff
    11.3
    iex(12)> elem(q, 0)
    11.3
Note that parse returns a tuple. The example shows two different ways of getting the number out of the tuple.

Having completed this program, now rewrite it to prompt the user for input in a loop (rather than taking arguments from the command line). Allow the user to break out of the loop by entering a "q". For instance

UNIX> elixir cl2.ex
Enter units (q to quit): k
Enter distance: 20
20 kilometers is 12.42 miles
Enter units (q to quit): m
Enter distance: 20
20 miles is 32.2061191626409 kilometers
Enter units (q to quit): 20
Enter distance: 20
Got 20 which is neither k nor m
Enter units (q to quit): w
Enter distance: 20
Got w which is neither k nor m
Enter units (q to quit): q
Enter distance: q
Goodbye
    
To get input from a user use IO.gets as below:
    iex(14)> aa = IO.gets("Enter a number: ")
    Enter a number: 65
    "65\n"
    iex(15)> aa
    "65\n"    

Finally, rewrite this program using no "if" statements or "=". You should write this using only matching in function calls.

Times Table

Write a program to produce a times table. You should write this in a function so that the size of the table produced is a parameter of the function. For example a times table for 1..3 looks like the sample below. Getting precise columnar printing in Elixir is possible, but do not worry about it for this program. To print an item without a carriage return / line feed use IO.write rather than IO.puts. You can write this program without using IO.write, it is just a little easier if you use IO.write.):
           1 2 3
        1  1 2 3
        2  2 4 6
        3  3 6 9
    
Once you have your times table printing, also create a lists of lists containing the factors (without headers) of the times table. Hence your list for the above table would be
        ((1,2,3), (2,4,6), (3,6,9))
    
As ever, use only tail recursion. Again, try to write this without "if" or "=".

Going Higher

For this use Enum.map and Enum.filter to do the following. Given a list of numbers, multiply each by 7.2. Then take the square root of the number (use :math.sqrt(x)). Then add that number to its successor in the list (for the last item in the list, just return the item itself). Finally, return a list containing only those items that are less than 25 after all of these transformations. Do not write this all in a single function. Rather first use Enum.map to do the multiplication. Then use Enum.map to do the square root. Then write your own recursive function to do the successor addition. (Ideally this would use only matching. Tail recursion would be nice, too.) If you used tail recursion in your successor adder, un-reverse the list use Enum.reverse. Finally, use Enum.filter to get a list of those greater than 10. Each step should be independently testable. Put all of these together in two ways. Once using the pipe operator, and once without using the pipe operator.

For example, I named a module Steps and a function to do all of this stepper. Here is a call to my function and the result.
IO.inspect Steps.stepper([1,2,3,4,5,6,7,8,9])
[10.392304845413264, 11.063453348751466]

What to hand in

Work no more than 80 minutes on the above programs. If you did not complete everything, send what you have.

Send email to gtowell@brynmawr.edu with all three programs. (Or as far as you got.)