0
votes

I am pretty much new to Racket/Scheme. I know basic syntax.
I have written a program for doing cartesian product of list1 with list2. I debugged the program using Dr Racket. It seems that when l2 becomes null, the program doesn't replace l2 with original list i.e. ol2 (which is what I am trying to do).
I am unable to figure out why this is happening.

 (define product   (lambda (ol2 l1 l2)  
     (cond  
       [(and (null? l1) (null? l2)) '()]  
       [(null? l2) (product ol2 (rest l1) ol2)]  
       [else (cons (list (first l1) (first l2)) (product ol2 l1 (rest l2)))])))  
1
Is head an alias for first (or car)?Alexis King
@AlexisKing Yes, it was an alias for head. I have edited the code for clarity.aka_007
Try using (require racket/trace) and using (trace-define (product ol2 l1 l2) ...), then running the function in the REPL. That will likely help make the problem more clear.Alexis King
What is ol2 used for?soegaard
The base case should stop when just l1 is empty? since when l1 becomes empty? l2 is reset to ol2.Sylwester

1 Answers

1
votes

In your original code:

(define product   (lambda (ol2 l1 l2)  
     (cond  
       [(and (null? l1) (null? l2)) '()]  
       [(null? l2) (product ol2 (rest l1) ol2)]  ; here l1 gets empty and l2 becomes the same as ol2
       [else (cons (list (first l1) (first l2)) (product ol2 l1 (rest l2)))])))  

The base case requires both l1 and l2 to be null?, but since l2 is replaced by ol at the same time as l1 goes from one element to zero both will never be null since l2 will be the original list.

Then the default case will try to use first on the empty list. To fix this, just change the base case to terminate when just l1 is null?:

(define product   (lambda (ol2 l1 l2)  
     (cond  
       [(null? l1) '()] ; terminate when l1 is null? 
       [(null? l2) (product ol2 (rest l1) ol2)] 
       [else (cons (list (first l1) (first l2)) (product ol2 l1 (rest l2)))])))  

The use of an extra variable that supposed to be the same wasn't apparent to me or other commenters. Using a named let or local helper procedure hides implementation details that users shouldn't have to care about:

;; with named let
(define (product l1 l2)
  (let product ((l1 l1) (tl2 l2)) 
    (cond  
      [(null? l1) '()] ; terminate when l1 is null? 
      [(null? tl2) (product (rest l1) l2)] 
      [else (cons (list (first l1) (first tl2)) (product l1 (rest tl2)))])))  

;; with local helper procedure
(define (product l1 l2)
  (define (product l1 tl2) 
    (cond  
      [(null? l1) '()] ; terminate when l1 is null? 
      [(null? tl2) (product (rest l1) l2)] 
      [else (cons (list (first l1) (first tl2)) (product l1 (rest tl2)))]))
  ;; call the helper
  (product l1 l2))