Home > database >  DrRacket Using accumulator as List-ref index
DrRacket Using accumulator as List-ref index

Time:02-22

I am trying to write a function that take a list and iterates over each element in the list if the number is even I want that number to be added to the previous number in the list.

I was thinking an accumulator will count up from 0 with each iteration giving a position for each element in the list.

If the number in the list is even I want to add that number to the previous number in the list. Hence why I am trying to use the accumulator as an index for list-ref. I don't know how to write it to get the accumulator value for the previous iteration ( i (list-ref a-list(- acc 1)))?

(define loopl (lambda (l)
                (for/fold
                 ([acc 0])([i l])
                  (cond
                    [(even? i)(  i (list-ref  (- acc 1) l))]

enter image description here

CodePudding user response:

The (for/fold) form requires a (values) clause, and it is in that which you would put the conditional form.

Assuming you want only the new list as the return value, you would also want a #:result clause following the iteration variables.

(define loopl
  (lambda (l)
    (for/fold
     ([index 0]
      [acc '()]
      #:result acc)
     ([i l])
      (values [  index 1]
              [append acc
                      (if (and (> index 0)
                               (even? i))
                          (list (  i (list-ref l (- index 1))))
                          (list i))]))))

This should give the correct answer.

CodePudding user response:

You almost never want to repeatedly call list-ref in a loop: that makes for horrible performance. Remember that (list-ref l i) takes time proportional to i: in your case you're going to be calling list-ref with the index being, potentially 0, 1, ..., and that going to result in quadratic performance, which is bad.

Instead there's a neat trick if you want to iterate over elements of a list offset by a fixed amount (here 1): iterate over two lists: the list itself and its tail.

In addition you need to check that the first element of the list is not even (because there is no previous element in that case, so this is an error).

Finally I'm not entirely sure what you wanted to return from the function: I'm assuming it's the sum.

(define (accum-even-previouses l)
  (unless (not (even? (first l)))
    ;; if the first elt is even this is an error
    (error 'accum-even-previouses "even first elt"))
  (for/fold ([accum 0])
            ([this (in-list (rest l))]
             [previous (in-list l)])
    (  accum (if (even? this)
                 (  this previous)
                 0))))

CodePudding user response:

The question is not quite clear about the value to be returned by this function: this answer assumes that it is a total of even elements together with their previous elements. The function is developed using the HtDF (How to Design Functions) design method with a BSL (Beginning Student language) in DrRacket.

Start with a stub, incorporating signature and purpose, and a minimal "check-expect" example:
(Note: layout differs slightly from HtDF conventions)

(define (sum-evens-with-prev xs) ;; (Listof Integer) -> Integer ; *stub define* ;; *signature*
  ;; produce total of each even x with its previous element     ; *purpose statement*
  0)                                                            ; *stub body* (a valid result)

(check-expect (sum-evens-with-prev '()) 0)                      ; *minimal example*

This can be run in DrRacket:

Welcome to DrRacket, version 8.4 [cs].
Language: Beginning Student with List Abbreviations
The test passed!
>

The next steps in HtDF are template and inventory. For a function with one list argument the "natural recursion" list template is likely to be appropriate;

(define (fn xs) ;; (Listof X) -> Y                 ; *template*
  (cond                                            ;
    [(empty? xs) ... ]  #|base case|# ;; Y         ;
    [else (...          #|something|# ;; X Y -> Y  ;
            (first xs) (fn (rest xs))) ]))         ;

With this template the function and the next tests become:

(define (sum-evens-with-prev xs) ;; (Listof Number) -> Number
  ;; produce total of each even x with its previous element (prev of first is 0)
  (cond
    [(empty? xs) 0 ]                  #|base case: from minimal example|#
    [else (error "with arguments: "   #|something: ?|#
            (first xs) (sum-evens-with-prev (rest xs))) ]))

(check-expect (sum-evens-with-prev '(1)) 0)
(check-expect (sum-evens-with-prev '(2)) 2)

These tests fail, but the error messages and purpose statement suggest what is required: the (... #|something|# from the template has to choose whether to add (first xs):

(define (sum-evens-with-prev xs) ;; (Listof Integer) -> Integer
  ;; produce total of each even x with its previous element (prev of first is 0)
  (cond
    [(empty? xs) 0 ]
    [else
     (if (even? (first xs))
        (  (first xs) (sum-evens-with-prev (rest xs)))
        (sum-evens-with-prev (rest xs))) ]))

Now all 3 tests pass! Time for more check-expects (note: careful introduction of check-expects is a way of clarifying ones understanding of the requirements, and points one to the code to be added):

(check-expect (sum-evens-with-prev '(1 1)) 0)
(check-expect (sum-evens-with-prev '(1 2)) 3)

Ran 5 tests. 1 of the 5 tests failed.

Check failures: Actual value 2 differs from 3, the expected value.

sum-evens-with-prev needs the prev value to include in the even? case: make it available by introducing it as an argument (renaming the function), add the appropriate arguments to the recursive calls, the function now just calls sum-evens-and-prev:

(define (sum-evens-and-prev xs prev) ;; (Listof Integer) Integer -> Integer
  ;; produce total of each even x and prev
  (cond
    [(empty? xs) 0 ]
    [else
     (if (even? (first xs))
         (  prev (first xs) (sum-evens-and-prev (rest xs) (first xs)))
         (sum-evens-and-prev (rest xs) (first xs))) ]))

(define (sum-evens-with-prev xs) ;; (Listof Integer) -> Integer
  ;; produce total of each even x with its previous element (prev of first is 0)
  (sum-evens-and-prev xs 0))

(just add some more tests, and all is well :)

(check-expect (sum-evens-with-prev '(0 2)) 2)
(check-expect (sum-evens-with-prev '(2 1)) 2)
(check-expect (sum-evens-with-prev '(1 3)) 0)
(check-expect (sum-evens-with-prev '(2 2)) 6)
(check-expect (sum-evens-with-prev '(1 2 3 4)) 10)
(check-expect (sum-evens-with-prev '(1 2 3 3 5 6 6)) 26)

Welcome to DrRacket, version 8.4 [cs].
Language: Beginning Student with List Abbreviations.
All 11 tests passed!
>
  • Related