Search code examples
ruby

Use Ruby to make a DrRacket `append` function


I am trying to use Ruby to emulate an append function from DrRacket. However, when I try it, I get ((1 2 3) . 99) instead of (1 2 3 . 99). Is it possible to correctly emulated append with a preexisting list, or do I need to make a copy of it first? Please reply at your earliest convenience.


Solution

  • There are two points to your problem:

    1. Data Structure

    append is basically like cons, in the sense of it should also add items to the underlying list. However in this example, it actually returns a string, without altering the state of the Pair object.

    What you actually need to do, is to append the new item to the end of the pair/list structure. To achieve this you need to walk through the pairs (assuming it is a valid list) until you find the last item, which should be null.

     def append(other)
      return false unless list?
      
      if cdr.nil?
        @value2 = other
      else
        cdr.append(other)
      end
    
      self
    end
    

    It is also important that you return the "existing" object with self if you want to puts inline, like in your example.

    Take for example the following code:

    b = cons(1, Pair.null)
    c = cons(4, Pair.null)
    
    puts b.append(c)
    

    append here will make sure that the following structure is created:

    <Pair @value1=1, @value2=<Pair @value1=4, @value2=nil>>
    
    1. Printing the list

    Now, in order to print the list it is yet another recursive problem (like the first one). You need to walk through the list of pairs but also account for the the different cases. That's why keeping a valid data structure in place is so important.

    def to_s
      helper_to_s(true)
    end
    
    def helper_to_s(started = false)
      str = ""
      str += "(" if started
      str += "(" if started && car.is_a?(Pair)
    
      if car.is_a?(Pair)
          str += car.helper_to_s
      else
        str += car.to_s
      end
    
      if cdr.nil?
        str += ")"
      elsif cdr.is_a?(Pair)
        str += " "
        str += cdr.helper_to_s()
      else
        str += " . #{cdr})"
      end
    
      str
    end
    

    We need a helper function so that we can tell the recursion call when to open the (. Additionally we also need to account for the case when we have nested pair right away. Finally, thanks to our data structure we can tell when the cdr is not null, meaning it's not a pure list, in this case we can simply concatenate a . to the final string and it will show it properly in the to_s call.

    If you have further questions please make sure to check the documentation for Pairs and List in the racket documentation: https://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html it is really helpful.