1

I am currently trying to create a function called reverse-with-count that returns a list of characters in reverse order, that are repeated a number of times as specified by the corresponding element of a second list of numbers.

For example:

(reverse-with-count '(a b c) '(1 2 3)) => (c c c b b a)
(reverse-with-count '(d c b a) '(3 0 0 1)) => (a d d d)

I believe my else clause to be correct but I am getting errors in my code for the conditions I have set, where it is expecting real?

This is what I have done so far:

(define reverse-with-count
(lambda (ls1 ls2)
  (cond
    ((null? ls2) ls1)
    ((positive? ls2) (error "Please enter a positive number!"))
    ((> ls1 ls2)(error "Please ensure the lists are the same size."))
    ((< ls1 ls2)(error "Please ensure the lists are the same size."))
    (else
     (cons (reverse (make-string (car ls2)(ls1))
                    (reverse-with-count (cdr ls2)(cdr ls1))))))))

How can I fix this issue?

4
  • Why are you calling make-string? Commented Dec 28, 2018 at 17:07
  • You can't use > and < with lists. You want to compare the lengths of the lists, not the lists themselves. (not (= (length ls1) (length ls2))) Commented Dec 28, 2018 at 17:09
  • (ls1) is wrong. That tries to call ls1 as a function, but it's a list. Commented Dec 28, 2018 at 17:10
  • I guess what you meant was (make-string (car ls2) (car ls1)). Two problems: 1) the second argument to make-string must be a character, not a symbol. 2) this will create a string like "ccc" not 3 separate list elements. Commented Dec 28, 2018 at 17:13

3 Answers 3

3

You have a number of problems.

  1. You're calling numeric comparison functions positive?, <, and > with lists as the arguments. You want to compare the lengths of the lists, not the lists themselves. And you want to test if the element of the lists are positive.

  2. You shouldn't report an error when the element of the count list is positive, you should complain when it's negative.

  3. You're calling make-string. But the requirement isn't a string with multiple copies of the list element, the duplicates should be separate elements in the result.

  4. You need to reverse the final result after all the recursions, not reverse the operation on a single element.

It also helps to use more meaningful variable names than ls1 and ls2.

(define reverse-with-count
  (lambda (symbols counts)
    (let ((recurse 
           (lambda (symbols counts)
             (cond
              ((null? counts) symbols)
              ((negative? (car counts))
               (error "Please enter a positive number!"))
              ((not (= (length symbols) (length counts)))
               (error "Please ensure the lists are the same size."))
              ((= 0 (car counts))
               ;; Skip element when count is 0
               (reverse-with-count (rest symbols) (rest counts)))
              (else
               ;; Recurse with a decremented count for the first element
               (cons (car symbols)
                     (reverse-with-count
                      symbols
                      (cons (- (car counts) 1) (rest counts)))))))))
      (reverse (recurse symbols counts)))))
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your help Barmar. Where I was to use numeric/string functions seemed to be my big issue here. I had read conflicting reports online as to when to use positive? and negative?.
Isn't it obvious? You use positive? when you want to test if the number is positive, and negative? if you want to test if it's negative. Since negative is the error case, you want to use that here.
2

Here's another tail-recursive solution using match* -

#lang racket

(define (reverse-with-count xs ys (acc null))
  (match* (xs ys)

    ;; zero case
    [((list _ xs ...) (list 0 ys ...))
     (reverse-with-count xs ys acc)]

    ;; non-zero case
    [((list x _ ...) (list y ys ...))
     (reverse-with-count xs
                         (cons (- y 1) ys) ;; decrement y
                         (cons x acc))]    ;; cons onto acc

    ;; any other case
    [(_ _)
     acc]))

Works as you expected -

(reverse-with-count '(a b c) '(1 2 3))
;; '(c c c b b a)

(reverse-with-count '(d c b a) '(3 0 0 1))
;; '(a d d d)

Comments

1

Tail-recursive solution

As accumulator-based tail recursive solutions usually produce their results already in reverse by repeated use of cons, it is the natural fit to the problem here:

(define (reverse-with-count symbols counts (acc '()))
    (cond ((and (null? symbols) (null? counts)) acc)
          ((or (null? symbols) (null? counts))
           (error "Please ensure the lists are of same length."))
          ((<= (car counts) 0) ; treat negative numbers as zero
           (reverse-with-count (cdr symbols) (cdr counts) acc))
          (else
           (reverse-with-count symbols 
                               (cons (- (car counts) 1) (cdr counts)) 
                               (cons (car symbols) acc)))))

Old answer was:

(define (reverse-with-count symbols counts (acc '()))
  (let ((sym-len (length symbols)))
    (cond ((not (= sym-len (length counts)))
           (error "Please ensure the lists are the same size."))
          ((zero? sym-len) acc)
          ((< (car counts) 0)
           (error "Please enter a positive number!"))
          ((= (car counts) 0)
           (reverse-with-count (cdr symbols) (cdr counts) acc))
          (else
           (reverse-with-count symbols 
                               (cons (- (car counts) 1) (cdr counts)) 
                               (cons (car symbols) acc))))))

3 Comments

I'm not sure whether all Schemes have this optional arguments Racket syntax, it makes the code unnecessarily Racket-specific (named let would be the usual solution to use). More importantly, repeated length measuring is an anti-pattern, would you agree? Its effect can be achieved by few checks at the base case, and besides, if counts end prematurely, assume all the rest are zeroes, and if symbols end prematurely, so what, we're done anyway. :)
@Will Ness: Thanks! I agree - and improved the answer (the old answer is below it).
as for the Racket-specific code - I don't care about the other Schemes. Since as far I understood, Racket is the "official Scheme" now ... and my personal opinion is, that lispers should unite and use ONE scheme and ONE common-lisp - we are anyway already a too small community ... it annoys me already in common-lisp that there are so many implementations with their specific code ... it very unnecessarily complicates programming.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.