This question already has answers here:
How to add in a list in racket
(6 answers)
Closed 6 months ago.
What is an elegant or idiomatic way to get the sum of a list of lists in Racket? (I am new to Racket).
Here's what I put together but it requires two functions, ideally it would only need one:
(define (count-reps-per-round lst)
#| a partial solution with recursion,
only adds the subtotal
of each list and returns a new list
|#
(cond
[(empty? lst) empty]
[else (cons (apply + (first lst))
(count-reps-per-round (rest lst)))]))
(define (reps-per-workout lst) ; would be nice to combine with above into one function
(apply + (count-reps-per-round lst)))
(define results `(
(28 25 34 18)
(22 21 30 20)
(19 16 24 16)
(18 17 20 19)))
(display(count-reps-per-round results)) ; (105 93 75 74)
(= (reps-per-workout results) 347) ; #t
I would also be interested in any solution that doesn't require nested lists.
There's nothing wrong with splitting a complex problem in multiple helper functions, in fact this is encouraged when doing functional programming. But also we're encouraged to reuse built-in procedures whenever possible, for example we can write the solution to your problem in a single line by carefully combining map and apply:
(define (reps-per-workout lst)
(apply + (map (lambda (sl) (apply + sl)) lst)))
It works as expected:
(define results
'((28 25 34 18)
(22 21 30 20)
(19 16 24 16)
(18 17 20 19)))
(reps-per-workout results)
=> 347
Not sure this is "idiomatic" or that it's the most efficient implementation -- but seems like a simple solution:
(define (nested-sum lst)
(cond
[(empty? lst) 0 ]
[(list? lst) (+ (nested-sum (first lst)) (nested-sum (rest lst)))]
[else lst]))
(writeln (nested-sum `((1 2 3) (4 5 6)))) ; 21
(writeln (nested-sum 10)) ; 10
(writeln (nested-sum `())) ; 0
; and even deeply nested:
(writeln (nested-sum `((1 2 3) (4 5 6) ((10 20))))) ; 51
Related
I started learning Scheme today, and wanted to write a function that would reverse a list (only surface level, nested lists stay in the same order).
Heres the function I made:
(define reverse (lambda (lst)
(if (> (length lst) 0)
(cons (reverse(cdr lst)) (car lst))
'()
)))
I thought it would just continuously add the values before everything after it so in the end it's reversed, but when doing (reverse '(5 12 31 7 98 13)), I should get (13 98 7 31 12 5), but instead I get '((((((() . 13) . 98) . 7) . 31) . 12) . 5). How come it's adding periods and parenthesis instead of adding the number to the list?
(list x y ..... z) list structure is constructed like this in Scheme:
(cons x
(cons y
…
…
(cons z '())))
if you use append instead of cons and put your (car lst) into a list of its own, your code will work:
(define reverse
(lambda (lst)
(if (> (length lst) 0)
;;(cons (reverse (cdr lst)) (car lst) )
(append (reverse (cdr lst)) (list (car lst)))
'()
)))
The list (13 98 7 31 12 5) is a visualization of the pairs (13 . (98 . (7 . (31 . (12 . (5 . ())))))). If the cdr is a pair or the empty list the dot and one set of parens is omitted. The code to create this structure is (cons 13 (cons 98 (cons 7 (cons 31 (cons 12 (cons 5 '())))))) while the structure your function creates is (cons (cons (cons (cons (cons (cons 13 '()) 98) 7) 31) 12) 5).
When iterating a list you always do it in order. eg. (1 2 3) is quite difficult to do in reverse. However when creating lists you always do them from end to beginning. eg. (cons 1 (cons 2 (cons 3 '()))) will have to evaluate the cons with 3 before the one with 2 etc. This can be used for a very efficent reverse using an accumulator:
(define (reverse lst acc)
(if (null? lst)
acc
(reverse (cdr lst)
(cons (car lst) acc))))
So imagine calling this (reverse '(1 2 3) '()):
(reverse '(1 2 3) '()) ; =>
(reverse '(2 3) (cons 1 '())) ; ==>
(reverse '(3) (cons 2 '(1))) ; ==>
(reverse '() (cons 3 '(2 1))) ; ==>
'(3 2 1)
How would I write a function in Dr. Racket which consumes a list of numbers (all numbers are 25) and produces a new list of numbers with 50 added to each preceding element?
Here is my code so far:
(define (new-funct f lst)
(cond
[(empty? lst) empty]
[else (cons (f (first lst))
(new-funct f (rest lst)))]))
(define (make-addition m)
(lambda (n) (+ m n)))
(define add50 (make-addition 50))
If I type in:
(new-funct add50 (list 25 25 25 25 25))
My expected output is
(list 25 75 125 175 225)
To explain the output, the first element stays the same. Then, 50 is added to 25 to give 75. Then, 50 is added to 75 to give 125 and so on.
Instead of this output, I get:
(list 75 75 75 75 75)
How would I correct my code? Thanks.
I recommend using map to map over a range of the indices and add:
(define (agg-adder n lst)
(map
(λ (i x) (+ x (* n i)))
(range (length lst))
lst))
This will result in a new list where the elements are each x + (n * (index of x)) for each x in the original list.
I'm working with Scheme and want to do some simple programs. I created one in which the multiples of five are filtered out of a list. Now I want to write a boolean function in which it will tell me if a specific element is part of the filtered list! So if I write "8" it will say false, but "40" would say true. Thanks!
(define (multiple-of-5 some-integer)
(equal? (remainder some-integer 5) 0))
(filter multiple-of-5 '(2 5 8 20 25 27 32 40))
output so far:
(5 20 25 40)
How you produced the list (5 20 25 40) is irrelevant. Thus all your code is irrelevant to this question. Basically you want this:
(define some-list '(5 20 25 40)) ; doesn't matter how this came about
(find (lambda (e) (= e 8)) some-list) ; ==> #f
(find (lambda (e) (= e 40)) some-list) ; ==> 40 (thruthy)
Now find is defined in the SRFI-1 List library and it is voted into R7RS Large. I imagine you could make an abstraction more useful for your case like (exists? 40 some-list) that uses find or you can roll your own like in Torbio's answer.
You can implement a contains? recursive function which travels the list and checks if the input number is equal to the current list element. The condition to stop is if the list is empty or if it contains the number. Pretty self-explanatory.
(define (contains? l i)
(if (null? l) #f
(or (equal? (car l) i) (contains? (cdr l) i))))
(contains? (filter multiple-of-5 '(2 5 8 20 25 27 32 40)) 8) ; #f
(contains? (filter multiple-of-5 '(2 5 8 20 25 27 32 40)) 40) ; #t
I am trying to take a list of 16 numbers I have and make it into a list of 4, 4 element sublists to represent the game board of a magic square. I made a method that can take a list and return a single sublist, and now I am trying to recursively use this method to build the full board.
My problem however, is my initBoard returns nil no matter what and I know every other method is working as desired. Any clarification of my error would be greatly appreciated!
Also here is an example input list:
(4 5 15 10 14 11 1 8 9 16 6 3 7 2 12 13)
And what I want as the output would be:
((4 5 15 10) (14 11 1 8) (9 16 6 3) (7 2 12 13))
(defun smallList (lst cnt)
(cond ((>= cnt 4) nil)
(t (cons (car lst) (smallList (cdr lst) (+ 1 cnt))))))
(defun isEmpty (lst)
(if lst 1 -1))
(defun initBoard (lst)
(cond ((= (isEmpty lst) -1) nil)
(t (cons (smallList lst 0) (initBoard (cddddr lst))))))
Some remarks:
someList, lst, cnt is not idiomatic, use some-list, list, count
You don't need is-empty, just use endp or null, which returns a boolean (not -1 or 1). You could make an alias if you want (but why?):
(setf (symbol-function 'is-empty) #'endp)
You could use a loop for small-list:
(defun small-list (list)
(values (loop repeat 4 collect (pop list)) list))
The secondary value is the rest of the list, so that you don't need to cddddr.
But in fact, it might be better to initialize the whole board inside a single function:
(defun init-board (list)
(loop repeat 4 collect
(loop repeat 4 collect (pop list))))
The first LOOP collect lists of 4 elements, which are collected by the inner LOOP. The collected elements are popped from the input list.
Now, if I wanted to be extremely careful, I would add some checks and report errors on bad inputs:
(defun init-board (list)
(flet ((failure ()
(error "Input list should contain exactly 16 integers: ~S"
list)))
(loop
with current = list
repeat 4 collect
(loop
repeat 4
collect (if current
(let ((element (pop current)))
(check-type element integer)
element)
(failure)))
into board
finally (if list (failure) (return board)))))
Also, I would use a multi-dimensional array for boards.
(make-array '(4 4) :initial-contents (init-board list))
I just tested your three functions and it gave me the correct output, so perhaps your issue isn't where you think it is.
(initBoard '(4 5 15 10 14 11 1 8 9 16 6 3 7 2 12 13))
=> ((4 5 15 10) (14 11 1 8) (9 16 6 3) (7 2 12 13))
I would use the following recursive function:
(defun smalllist (l n)
(when l
(cons (subseq l 0 (min n (length l)))
(smalllist (nthcdr n l) n))))
problem takes a list e.g L = (4 11 16 22 75 34) and gets tested for a condition (modulus 2) then returns a list with all the items in the list that pass the test e.g newL = (4 16 34)
Here is the code:
(define clean-list
(lambda (x)
(list x (test x))))
(define test
(lambda (x)
(cond (= 0 (modulo (car x) 2))
(cons (car x) (test(cdr x)))
(else (test(cdr x))))
))
output:
((4 11 16 22 75 34) 0)
I debugged the code, it foes into (modulo (car x) 2) then returns to clean-list and exits, all after first run, please explain that and why it returns a 0 at the end of list. Also any feedback or improvement in code would be appreciated.
You're missing a set of parentheses. You're also missing a test for the recursion bottoming out.
(define test
(lambda (x)
(cond ((eq? x '()) '())
((= 0 (modulo (car x) 2))
(cons (car x) (test(cdr x))))
(else (test(cdr x))))
))
DEMO
The general syntax of cond is:
(cond (<test1> <result1>)
(<test2> <result2>)
...)
In your code <test1> was simply =, not (= 0 (modulo (car x) 2)).
Here's a tail-recursive version of the function.
(define test
(lambda (x)
(define (helper in out)
(if (null? in)
out
(if (= 0 (modulo (car in) 2))
(helper (cdr in) (append out (list (car in))))
(helper (cdr in) out))))
(helper x '())
))
(define clean-list
(lambda (x)
(list x (test x))))
(write (clean-list '(4 11 16 22 75 34)))
The output:
((4 11 16 22 75 34) (4 16 22 34))
PS. When I tested the code at repl.it, I had to change modulo to mod.