Homework 6:
Recursive Functions and Language Processing

Due: Monday, March 4, at 11:59 PM

Introduction

This assignment asks you to write even more recursive functions in Racket. The goals of this assignment are to gain more experience with common recursion patterns and to begin applying them to programming language topics.

Template Source File

Download the zipped directory hw06.zip. It contains templates for your solutions and two files of helper functions.

Please use the given names as the names of the files you submit, and do not modify the provide or require clauses in any of the files.

With provide, you must define all five functions. If you don't have time to solve a problem, define a function that takes the correct number of arguments and returns a legal default value, such as 0 or '().

The zip file also includes two files that contain helper functions we saw in class. Keep them in the folder with your solutions and tests. You will not submit these files.

Do Not Use...

To solve these problems, you do not need any Racket features beyond the things we have learned in class and the things discussed in this assignment. In order to practice the new skills we are learning, do not use...

Organizing Code

As before, put your solutions in homework06.rkt. Put any helper functions you write for a problem after the main function of your solution.

As before, put your tests in homework06-tests.rkt. For each problem, write at least three Rackunit expressions to test your solution. Depending on the type of value that the function produces, use check-equal? or check-true/check-false. You may use my examples as one of your tests. Be sure that you test other key cases, too.

Test only the public functions. Helper functions are not visible outside your solution module.

Problems

  1. Write a structurally recursive function named (tails lst) that takes a list as an argument. tails returns a list containing all of the sublists lst, including lst. For example:
    > (tails '(1 2 3))
    '((1 2 3) (2 3) (3) ())
    
    > (tails '(a (1 2) c 3))
    '((a (1 2) c 3)
      ((1 2) c 3)
      (c 3)
      (3)
      ())
    
    Think carefully about what the function should return if it receives the empty list as an argument. This is also the base case for the recursion.

  2. Write a mutually recursive function named (n-list? obj) that takes one argument, which can be any Racket value. n-list? returns true if and only if obj is an n-list. For example:
    > (n-list? '(1 (2 (3 4) 5) 6))
    #t
    > (n-list? '(1 (2 (3 a) 5) 6))      ; oops, there's an 'a
    #f
    
    n-list? should be mutually recursive with the function num-expr?, which returns true if its argument is a number expression and false if not.

    Even though the argument to this function can be any Racket object, you should still use the definition of n-list to design your function. An n-list must either be an empty list or a pair whose car is a number expression and whose cdr is an n-list. Similarly, use the definition of number expression to design num-expr?. Your functions must follow the definitions in order to tell if their arguments do!

  3. Write a structurally recursive function named (tree-min bin-tree) that takes as an argument a binary tree bin-tree of this form:
    <binary-tree> ::= <number>
                    |  (<number> <binary-tree> <binary-tree>)
    
    tree-min returns the smallest value in the tree. For example:
    > (tree-min '(8 (13 11 (5 24 6)) (15 (12 10 14) 20)))
    5
    
    You will find Racket's primitive function min useful here. Note: You do not need an accumulator variable for this problem! You can determine the answer for each case directly from its parts.

  4. Write a structurally recursive function named (declared-vars exp) that takes as input an expression in the little language from class:
    <exp> ::= <varref>
            | (lambda (<var>) <exp>)
            | (<exp> <exp>)
    
    declared-vars returns a list of all the variables declared in exp. Recall that only lambda expressions can create a variable. For example:
    > (declared-vars '(lambda (y) (x y)))
    (y)
    > (declared-vars '((lambda (x)
                            (lambda (z) z))
                          (lambda (x) y) ))
    (x z x)     ;; duplicates are allowed
    
    As discussed in class, use the syntax procedures for the little language defined in syntax-procs.rkt:
    exp?
    varref?
    lambda?   lambda->param   lambda->body
    app?      app->proc       app->arg
    
    Each case of declared-vars will return a list of symbols. In the app case, you will need to combine two lists into one. For this, you will want to use Racket's primitive function append.

  5. Note: This is an extra credit problem. I encourage you to do it for practice, but it is not required.

    Write a structurally recursive function (prefix->postfix exp) that takes one argument, an expression in the same little language as Problem 4. prefix->postfix returns an expression of the same form as its argument, except that all function applications have been reversed. For example:
    > (prefix->postfix '(square x))
    '(x square)
    
    > (prefix->postfix '(lambda (y) (x y)))
    '(lambda (y) (y x))
    
    Use the same syntax procedures for the little language described in Problem 4. For this problem, you will also need the constructor functions:
    • make-varref
    • make-lambda
    • make-app

Deliverables

By the due time and date, use the course submission system to submit the following files electronically:

Be sure that your submission follows the submission requirements. Be sure to use the specified name for your file. This enables the autograder to find and run your code.