CSC 533: Programming Languages
Spring 2025

HW6: Clojure Structures

Note: late submissions will not be accepted for this assignment.


For this assignment, you will modify and add to functions that are provided for you in hw6.clj. Download this file, rename it YOURNAME-hw6.clj and enter your name in the comment at the top. Be careful to name your functions exactly as defined in the exercises.

PART 1: Molecular Weights

The periodic table, which identifies each element and its atomic weight, is listed below as a Clojure map:
    (def PERIODIC-TABLE
       {:H  [  1   1.0080 :Hydrogen]    :He [  2   4.0026 :Helium]
        :Li [  3   6.9410 :Lithium]     :Be [  4   9.0122 :Beryllium]
        :B  [  5  10.8100 :Boron]       :C  [  6  12.0110 :Carbon]
        :N  [  7  14.0067 :Nitrogen]    :O  [  8  15.9994 :Oxygen]
        .
        .
        .
        :Ts [117 294.0000 :Tennessine]  :Og [118 294.0000 :Oganesson]})

This map is defined in the hw6.clj file you downloaded. Add the following definitions to your copy of the file.

  1. Define a function named atomic-number that takes one input, the keyword for an element, and returns the atomic number of that element (i.e., its position in the table). For example, (atomic-number :H) should evaluate to 1 while (atomic-number :O) should evaluate to 8.
  2. Define a function named atomic-weight that takes one input, the keyword for an element, and returns the atomic weight of that element. For example, (atomic-weight :H) should evaluate to 1.008 while (atomic-weight :O) should evaluate to 15.9994.
  3. Define a function named molecular-weight1 that takes one input, a list of element keywords, and returns the molecular weight (i.e., the sum of the atomic weights) for that list. For example, (molecular-weight1 '(:H :H :O)) should evaluate to 18.0154.
  4. When writing formulas for molecules in which an element appears multiple times, scientists use subscripts to denote repetition. For example, the formula for water is written H2O instead of HHO. Copy your molecular-weight1, rename it molecular-weight2, and modify it to handle lists in which an element may be followed by a number. For example, (molecular-weight2 '(:H 2 :O)) should evaluate to 18.0154.
  5. Complex formulas can contain nested sub-formulas. For example, the formula for isopropyl alcohol is (CH3)2CHOH. Copy your molecular-weight2, rename it molecular-weight3, and modify it to handle nested lists. For example, (molecular-weight3 '((:C :H 3) 2 :C :H :O :H)) should evaluate to 60.0964.

PART 2: Binary Trees

In class, we discussed how structured lists could be used to represent binary trees. For example, the following lists represent a tree of animal names (strings) and a tree of numbers, respectively.

    (def ANIMALS                 
       '(:dog                                 
         (:bird (:horse () ()) (:cat () ()))   
         (:possum (:dog () ()) ()))) 
    
    (def NUMBERS
       '(2 (-1 () ()) (3 () ())))

Several utility functions (root, left-subtree, and right-subtree) are provided for you in hw6.clj. Recall the following definitions from Data Structures:

Add the following functions for traversing and classifying binary trees:

  1. Define a function named rightmost that takes one input, a list representing a binary tree, and returns the rightmost value in the tree. For example, (rightmost ANIMALS) should evaluate to possum. Note: since an empty tree does not have a rightmost element, the function should return nil when applied to an empty tree.
  2. Define a function named leftmost that takes one input, a list representing a binary tree, and returns the leftmost value in the tree. For example, (leftmost ANIMALS) should evaluate to horse. Note: since an empty tree does not have a leftmost element, the function should return nil when applied to an empty tree.
  3. Define a function named is-bst? that takes one input, a list representing a binary tree, and returns true if that list is a binary search tree (otherwise false). For example, (is-bst? NUMBERS) should evaluate to true, while (is-bst? ANIMALS) should evaluate to false.
  4. Define a function named is-balanced? that takes one input, a list representing a binary tree, and returns true if that list is relatively balanced (otherwise false). For example, (is-balanced? ANIMALS) and (is-balanced? NUMBERS) should both evaluate to true.
  5. Define a function named is-avl? that takes one input, a list representing a binary tree, and returns true if that list is an AVL tree (otherwise false). For example, (is-avl? NUMBERS) should evaluate to true, while (is-avl? ANIMALS) should evaluate to false.

PART 3: OOP in Clojure

In class, we discussed how Clojure allows for the equivalent of classes using protocols and type definitions. For example, we developed a Die "class" that is included in hw6.clj.

  1. Define a function named roll-until-doubles that utilizes this "class" to simulate repeated dice rolls until doubles are obtained. The function should have one input, the number of die sides, and should return the number of rolls required to achieve doubles. In addition, each pair of dice rolls should be displayed. For example, (roll-until-doubles 6) might produce the following:
  2. (roll-until-doubles 6) 1-4 4-3 5-6 2-2 => 4