1
votes

I'm trying to make a function that takes in two lists of atoms as a parameter and returns them as a list of pairs.

Example Input

(combine '(1 2 3 4 5) '(a b c d e))

Example Output

'((1 a) (2 b) (3 c) (4 d) (5 e))

However, I'm new to Racket and can't seem to figure out the specific syntax to do so. Here is the program that I have so far:

(define connect
  (lambda (a b)
    (cond [(> (length(list a)) (length(list b))) (error 'connect"first list too long")]
          [(< (length(list a)) (length(list b))) (error 'connect"first list too short")]
          [else (cons (cons (car a) (car b)) (connect(cdr a) (cdr b)))]
     )))

When I run it, it gives me the error:

car: contract violation
  expected: pair?
  given: '()

Along with that, I don't believe the error checking here works either, because the program gives me the same error in the else statement when I use lists of different lengths.

Can someone please help? The syntax of cons doesn't make sense to me, and the documentation for Racket didn't help me solve this issue.

3
(define (unzip . lists) (apply map list lists))? Works with any number of arguments. If you use map from SRFI-1 it will stop at the shortest list or signal an error with the standard map - Sylwester
What's the name of the procedure? you seem to be mixing things. Is it connect, combine or zip? - Óscar López
You don't have to call list on a or b, this is wrong: (list a). This is because a is already a list, why do you want to put it inside another list? - Óscar López

3 Answers

2
votes

When you're new to Scheme, you have to learn to write code in the way recommended for the language. You'll learn this through books, tutorials, etc. In particular, most of the time you want to use built-in procedures; as mentioned in the comments this is how you'd solve the problem in "real life":

(define (zip a b)
  (apply map list (list a b)))

Having said that, if you want to solve the problem by explicitly traversing the lists, there are a couple of things to have in mind when coding in Scheme:

  • We traverse lists using recursion. A recursive procedure needs at least one base case and one or more recursive cases.
  • A recursive step involves calling the procedure itself, something that's not happening in your solution.
  • If we needed them, we create new helper procedures.
  • We never use length to test if we have processed all the elements in the list.
  • We build new lists using cons, be sure to understand how it works, because we'll recursively call cons to build the output list in our solution.
  • The syntax of cons is very simple: (cons 'x 'y) just sticks together two things, for example the symbols 'x and 'y. By convention, a list is just a series of nested cons calls where the last element is the empty list. For example: (cons 'x (cons 'y '())) produces the two-element list '(x y)

Following the above recommendations, this is how to write the solution to the problem at hand:

(define (zip a b)
  ; do all the error checking here before calling the real procedure
  (cond
    [(> (length a) (length b)) (error 'zip "first list too long")]
    [(< (length a) (length b)) (error 'zip "first list too short")]
    [else (combine a b)])) ; both lists have the same length

(define (combine a b)
  (cond
    ; base case: we've reached the end of the lists
    [(null? a) '()]
    ; recursive case
    [else (cons (list (car a) (car b)) ; zip together one element from each list
                (combine (cdr a) (cdr b)))])) ; advance the recursion

It works as expected:

(zip '(1 2 3 4 5) '(a b c d e))
=> '((1 a) (2 b) (3 c) (4 d) (5 e))
2
votes

The reason your error handling doesn't work is because you are converting your lists to a list with a single element. (list '(1 2 3 4 5)) gives '((1 2 3 4 5)) which length is 1. You need to remove the list.

This post is a good explanation of cons. You can use cons to build a list recursively in your case.

(define connect
  (lambda (a b)
    (cond [(> (length a) (length b)) (error 'zip "first list too long")]
          [(< (length a) (length b)) (error 'zip "first list too short")]
          [(empty? a) '()]
          [else (cons (list (car a) (car b)) (connect (cdr a) (cdr b)))]
               )))

However, I would prefer Sylwester's solution

(define (unzip . lists) (apply map list lists))

which uses Racket's useful apply function.

0
votes
#lang racket
(define (combine lst1 lst2)
  (map list lst1 lst2))

;;; TEST
(combine '() '())
(combine (range 10) (range 10))
(combine (range 9) (range 10))

map have buildin check mechanism. We don't need to write check again.

#lang racket

(define (combine lst1 lst2)
  (local [(define L1 (length lst1))
          (define L2 (length lst2))]
    (cond
      [(> L1 L2)
       (error 'combine "first list too long")]
      [(< L1 L2)
       (error 'combine "second list too long")]
      [else (map list lst1 lst2)])))