CS 315 Assignment 9: Memos and Drums

Due: November 17, 2009.

Files: Cons.java   Cons.jfugue.java   Event.java   patmatch.lsp   patterns.lsp   patm.lsp

Some functions that you may need are provided in the file Cons.java, and you will need some of your functions from the last assignment. Question 1 may be done in Java or in Lisp; the rest should be done in Java.

  1. Write a recursive function Object solve(Object e, String v) that attempts to solve the equation e for variable v, which we assume occurs at most once in e.

    This function will do the same thing as the earlier version of solve; in this version, use patterns to rewrite the given expression. The three base cases are the same as before, and you can copy your earlier code for those cases.

    1. If the left-hand side (lhs) of e is v, return e.
         (solve '(= f (* m a)) 'f)  =>  (= f (* m a))
      
    2. If the right-hand side (rhs) of e is v, return e with its arguments reversed.
         (solve '(= (* m a) f) 'f)  =>  (= f (* m a))
      
    3. If the rhs is not v and not a Cons, return null.

    4. If the rhs of e is a Cons (i.e. an operation), try to solve the equation by applying algebraic transformations from a list of patterns, algpats. For each pattern in the list, try to transform the expression e using the pattern. If the transformation works (is not null), call solve recursively on the transformed version of e; if the result of solve is not null, return that result. Otherwise, continue through the list of patterns. If all patterns have been tried, return null.

    An initial list of patterns is provided; add patterns to complete the set.

  2. Write a method hashCode() for the class Cons. The hashCode of a Cons should be the XOR of the hashCode of the first times 17 and the hashCode of the rest times 127. The hashCode of null should be 0; use an auxiliary function to make sure that you do not try to call hashCode() on null, which would cause a runtime exception.

  3. Write a class Memoizer (in a file Memoizer.java) that implements memoization of a function call. Memoization is a technique for wrapping an expensive function with a memory structure that remembers previously computed values of the function. If the function is called again with the same argument value, it will be cheaper to look up the saved value than to recompute it.

    The class should be created using new Memoizer(Functor f) where f is a functor that wraps the function associated with the memoizer.

    Use a HashMap within the Memoizer to associate function values with argument values. You can Google "java hashmap" to get documentation of a HashMap.

    Memoizer should have a method Object call(Object x) to perform the wrapped function call. The call method should operate as follows:

    1. First, call should look up the parameter x in the HashMap; if it is present, return the value associated with x (without calling the Functor).

    2. Otherwise,
      1. Call the Functor to compute the value of fn(x).
      2. Add the value fn(x) to the HashMap for the key x.
      3. Return the value fn(x).

    Reference: http://en.wikipedia.org/wiki/Memoization

  4. An Austin company is making a digital piano, drum and percussion synthesizer, and they have hired you to write the software. Since musicians are not always good programmers, the input language that is used to specify a drum pattern needs to be simple. The software needs to convert the specified drum pattern into a time sequence of I/O commands to drive the drum synthesizer units. Since music is a language, we expect that a drum program will be structured as a tree.

    The drum synthesizer driver program is implemented as a discrete-event simulator, based on a PriorityQueue. You can Google "java priorityqueue" to get documentation of a PriorityQueue.

    A discrete-event simulator processes a sequence of events which are scheduled at particular clock times. The PriorityQueue is used to hold the pending events, with the priority being the time at which an event is scheduled to occur. In operation, the simulator removes the highest-priority (smallest time value) event from the queue, sets the current time to the time of the event, and executes it; the event will typically perform some action, and it may schedule other events for the future. The simulation stops if the queue becomes empty.

    We will assume that time values are integers. Events that occur at the same integer time are considered to be simultaneous, regardless of their order, since the computer is much faster than the drum units. A time duration of 1 represents a 1/16 note duration.

    An Event has a scheduled .time() and an .action(); this class is provided. An action is a list, consisting of a command followed by parameters; usually there is only one parameter, the length of time the command lasts. An event is added to the queue by the call addevent(PriorityQueue pq, Cons action, int time) .

    Execution of a command may emit an I/O command to the drum unit. Write a function execute(PriorityQueue pq, Cons act, int time) that executes an action by emitting appropriate commands.

    The function emit(instrument, time, duration) sends a command to a drum instrument; the arguments are all int. The instrument values are as follows:
    4[bass_drum]boom
    5[acoustic_snare]snare
    6[pedal_hi_hat]hat
    7[crash_cymbal_1]cymbal
    8[cowbell]cowbell
    9[ride_bell]bell
    10[hand_clap]clap
    11[tambourine]tambourine

    Commands are as follows, with d representing the duration of the action; usually a duration should be a power of 2, except for rest.

    Examples:

    (seq (boom 4) (bell 4))
    
    (seq (repeat 2 (kaboom 4)) (repeat 3 (cymbal 2)))
    
    (repeat 2 (seq (piano 0 C5 4) (piano 0 D5 4) (piano 0 E5 4)))
    

    Write a function int totaltime(Cons action) that calculates the total time required for an action. For most actions, the time is simply the duration of the action. A repeat action has a time that is the repeat count times the time of the sub-action. A seq action takes the sum of the times of its sub-actions. A sync action takes the maximum time of its sub-actions.

    The jFugue system, http://jfugue.org, allows sounds to be produced from Java. An interface has been written to allow the output of your program to produce sounds through jFugue. If you would like to use jFugue, download it from the jFugue site and install it in the same directory that you use for your program. Rename the file Cons.jfugue.java to be Cons.java .

    Reference: http://en.wikipedia.org/wiki/Discrete_event_simulation