I've just started learning Racket.
I have written this procedure:
#lang racket
(define split
(lambda (list)
(define plus-list '())
(define minus-list '())
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(define l (list-ref list i))
(define item (last-element-on-list l))
(cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
((= (cdr l '-)) (set! minus-list (cons list minus-list))))
)
(cons plus-list minus-list)
)
)
)
)
And instead of using (list-ref lst i) inside de do I have defined a variable l:
(define (list-ref lst i) l)
But it seems that I cann't do that, because I get the error:
define: not allowed in an expression context in: (define l (list-ref
lst i))
But there are a lot of define inside the do.
If I remove all the define inside the do, I have to write a lot of code and it is not easier to read and understand:
(define split
(lambda (list)
(define plus-list '())
(define minus-list '())
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(cond ((= (cdr (last-element-on-list (list-ref list i)) '+)) (set! plus-list (cons (list-ref list i) plus-list)))
((= (cdr (last-element-on-list (list-ref list i)) '-)) (set! minus-list (cons (list-ref list i) minus-list))))
)
(cons plus-list minus-list)
)
)
)
)
How can I define a variable inside a do?
Reading your other question I see why you write the bolded expressions -
…
(cond ((= (cdr (last-element-on-list (list-ref list i)) '+))
(set! plus-list
(cons (list-ref list i) plus-list)))
((= (cdr (last-element-on-list (list-ref list i)) '-))
(set! minus-list
(cons (list-ref list i) minus-list))))
…
Your input list shown there is –
(define lst
'((n 25 f +)
(s 25 m +)
(ll 20 no -)))
Your split is inspecting the contents of each element of l. split has overstepped its boundaries and now it only works for lists containing elements of this particular structure. Along with set!, lack of else in a cond is typically an indication you're doing something wrong. You also call (cdr (last-element-of-list ...)). If last-element-of-list returns an atom, cdr would throw an error here.
Consider designing split in a more generic way –
(define (split proc l)
(define (loop l true false)
(cond ((null? l)
(cons true false))
((proc (car l))
(loop (cdr l)
(cons (car l) true)
false))
(else
(loop (cdr l)
true
(cons (car l) false)))))
(loop l '() '()))
(split (lambda (x) (> x 5))
'(1 5 3 9 7 0 8 3 2 6 4))
;; '((6 8 7 9) 4 2 3 0 3 5 1)
If our list contains different elements, we can still use the same split procedure –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +)))
;; '(((4 +) (3 +) (2 +) (1 +)) (3 -) (2 -) (1 -))
I think it's never too early to start learning continuation passing style. Below, return represents our continuation and defaults to cons, the same procedure we used to return the final result in our original implementation. Intuitively, a continuation represents "the next step" of the computation –
(define (split proc l (return cons)) ;; `return` is our continuation
(if (null? l)
;; base case: list is empty, return empty result
(return '() '())
;; inductive case: at least one `x`
(let* ((x (car l))
(bool (proc x)))
(split proc ;; tail recur with our proc
(cdr l) ;; ... a smaller list
(lambda (t f) ;; ... and "the next step"
(if bool ;; if `(proc x)` returned true
(return (cons x t) ;; ... cons the `x` onto the `t` result
f) ;; ... and leave the `f` the same
(return t ;; otherwise leave `t` the same
(cons x f)))))))) ;; ... and cons the `x` onto the `f` result
If we run our split procedure, you'll notice we get the same exact output as above. At first glance it looks like we made a mess of a nice program, however there's one distinct advantage of this implementation. Because the continuation is user-configurable, instead of cons, we could decide an entirely different fate for our two lists, t and f –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(lambda (plus minus)
(printf "plus: ~a, minus: ~a\n" plus minus)))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Note how plus and minus were given the respective results. We didn't have to pick apart an intermediate cons result. More intuitively, we want printf to be "the next step", but we only need to specify the first argument –
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Now we've scratched the surface of functional style :D
do loops are not idiomatic Racket. They are inherited from Scheme, and for whatever reason, they don’t permit internal definitions. I have never once used a do loop in Racket, since the for comprehensions are more functional, and they’re just generally easier to work with. Plus, since they originate in Racket, not in Scheme, they support internal definitions as you’d expect.
You could write your split function using for/fold instead of do, which has the added advantage of not needing to use set! (and avoiding the quadratic access time of using list-ref instead of iterating through the list). I’m not completely sure what your split function is supposed to do, as even with the internal definition removed, it does not compile, but here’s my best guess at what you might be attempting to do:
(define (split lst)
(for/fold ([plus-lst '()]
[minus-lst '()])
([l (in-list lst)])
(define item (last l))
(cond
[(equal? item '+)
(values (cons l plus-lst) minus-lst)]
[(equal? item '-)
(values plus-lst (cons l minus-lst))]
[else
(values plus-lst minus-lst)])))
Aside from the obvious restructuring to use for/fold instead of do, this code also makes the following changes over your code:
It uses the built-in last function from racket/list to get the last element of a list.
It uses equal? instead of = to compare symbols, since = is specifically for comparing numbers.
It indents things properly and puts close parentheses in idiomatic locations.
I fixed your code using let, read documentation about let it's heavily used in Scheme/Racket. I haven't used Scheme lately so I couldn't explain it as well as it is in documentation.
Shortly it's local symbol definition/redefinition, and you can use symbol with value only in let body.
Short example on let
(define x 5)
(let ((x 10))
(display x)) # => 10
(display x) # => 5
(let ((y 1))
(display y)) # => 1
(display y) # = => (error) y: undefined
Your code fixed using let
(define split
(lambda (list)
(let ((plus-list '())
(minus-list '()))
(cond ((null? list) '())
(else
(do ([i (length list) (- i 1)])
((zero? i))
(let ((l (list-ref list i))
(item (last-element-on-list l)))
(cond ((= (cdr l '+)) (set! plus-list (cons list plus-list)))
((= (cdr l '-)) (set! minus-list (cons list minus-list))))))
(cons plus-list minus-list))))))
Related
Here is a task: a list is given, some of the elements are also lists. It's nescessary to replace the nested lists with the sum of the numbers in them, if all of them are even, using recursion. For example:
(1 2 NIL (2 4 6) 5 7) -> (1 2 NIL 12 5 7)
if the parent list matches the condition after the transformation:
(2 2 (4 4) (2 2)) -> (2 2 8 4) -> 16
Now i have the following code:
;; check for all list elements are even
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))
(t nil)
)
)
;; list summing
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst) (sum-list (cdr lst))))
)
)
;; main func
(defun task (lst)
(cond ((null lst) nil)
((atom (car lst)) (cons (car lst) (task (cdr lst))))
((is-even-list (car lst)) (cons (list (sum-list (car lst))) (task (cdr lst))))
(t (cons (task (car lst)) (task (cdr lst))))
)
)
But now it processes only the “lowest” level of the list if it exists:
(2 4) -> (2 4)
(2 (2 4 6) 6) -> (2 12 6)
(2 (4 (6 8) 10) 12) -> (2 (4 14 10) 12)
(2 (4 6) (8 10) 12) -> (2 10 18 12)
How can i change this code to get "full" processing?
It's definitely not the best solution but it works:
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))
(t nil)
)
)
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst) (sum-list (cdr lst))))
)
)
(defun test (lst)
(dotimes (i (list-length lst))
(cond
((not (atom (nth i lst))) (setf (nth i lst) (test (nth i lst))))
)
)
(cond
((is-even-list lst) (setf lst (sum-list lst)))
((not (is-even-list lst)) (setf lst lst))
)
)
Here's a solution which I think meets the requirements of the question: recursively sum a list each element of which is either an even number or a list meeting the same requirement. It also does this making only a single pass over the structure it is trying to sum. For large lists, it relies on tail-call elimination in the implementation which probably is always true now but is not required to be. sum-list-loop could be turned into something explicitly iterative if not.
(defun sum-list-if-even (l)
;; Sum a list if all its elements are either even numbers or lists
;; for which this function returns an even number. If that's not
;; true return the list. This assumes that the list is proper and
;; elements are numbers or lists which meet the same requirement but
;; it does not check this in cases where it gives up for other
;; reasons first: (sum-list-if-even '(2 "")) signals a type error
;; (but (sum-list-if-even '(1 "")) fails to do so)
(labels ((sum-list-loop (tail sum)
(etypecase tail
(null sum) ;all the elements of '() are even numbers
(cons
(let ((first (first tail)))
(etypecase first
(integer
;; Easy case: an integer is either an even number
;; or we give up immediately
(if (evenp first)
(sum-list-loop (rest tail) (+ sum first))
;; give up immediately
l))
(list
;; rerurse on the car ...
(let ((try (sum-list-if-even first)))
;; ... and check to see what we got to know if
;; we should recurse on the cdr
(if (not (eq try first))
(sum-list-loop (rest tail) (+ sum try))
l)))))))))
(sum-list-loop l 0)))
Allow me to show some improvements on your own answer.
First, use conventional formatting: no dangling parentheses, bodies indented two spaces, other argument forms aligned. Use appropriate line breaks.
(defun is-even-list (lst)
(cond ((null lst) t)
((and (numberp (car lst))
(evenp (car lst)))
(is-even-list (cdr lst)))
(t nil)))
(defun sum-list (lst)
(cond ((null lst) 0)
(t (+ (car lst)
(sum-list (cdr lst))))))
(defun test (lst)
(dotimes (i (list-length lst))
(cond ((not (atom (nth i lst)))
(setf (nth i lst) (test (nth i lst))))))
(cond ((is-even-list lst) (setf lst (sum-list lst)))
((not (is-even-list lst)) (setf lst lst))))
The first function checks two things: that every element is a number, and that every element is even. In this context, the first condition mainly means: no sublists.
(defun flat-all-even-p (list)
(and (every #'numberp list)
(every #'even list)))
The second function sums a list and assumes that all elements are numbers (sublists would signal an error here).
(defun sum (list)
(reduce #'+ list))
The third function does not test, it sums. Note that it only accidentally returns the answer, since setf returns the value it sets. Another problem is that you do index lookup on lists in a loop, which is very inefficient. Finally, you modify the list you were given, which will surprise your caller.
(defun sum-if-all-even (tree)
(if (listp tree)
(let ((recursed-tree (mapcar #'sum-if-all-even tree)))
(if (flat-all-even-p recursed-tree)
(sum recursed-tree)
recursed-tree))
tree)
I wish to create a function in Scheme that takes in a predicate and a list of elements, and then outputs two separate lists. One with elements of the original list that MATCH the given predicate, and one with elements that DON'T match it.
The code I have right now I believe should isolate those which match the predicate and output a list of them but the code will not work.
(define tear
(lambda (pred xs)
(cond[(null? xs) '()]
[(list? (car xs))(cons((tear (pred (car xs)))(tear (pred (cdr xs)))))]
[(pred (car xs))(cons((car xs)(tear (pred (cdr xs)))))]
[else tear (pred (cdr xs))])))
(tear number? '(1 2 3 a b c))
The resulting output on my compiler is:
tear: arity mismatch;
the expected number of arguments does not match the given number
expected: 2
given: 1
arguments...:
#f
context...:
/home/jdoodle.rkt:2:4: tear
Command exited with non-zero status 1
Any help/info that you can give would be much appreciated.
Lets fix your code step by step. Adding indentation and whitespace to make it readable:
(define tear
(lambda (pred xs)
(cond
[(null? xs)
'()]
[(list? (car xs))
(cons ((tear (pred (car xs))) (tear (pred (cdr xs)))))]
[(pred (car xs))
(cons ((car xs) (tear (pred (cdr xs)))))]
[else
tear (pred (cdr xs))])))
(tear number? '(1 2 3 a b c))
The first problem I see is a problem of putting parentheses on the inside (around the arguments) of a function call instead on the outside. You do this with cons and with the recursive calls to tear. For instance in tear (pred (cdr xs)) you should move the first paren to before the function. Remember that parentheses in an expression almost always mean a function call in the shape of (function argument ...).
(cons (A B)) should be rewritten to (cons A B)
(tear (Pred Xs)) should be rewritten to (tear Pred Xs)
tear (Pred Xs) should be rewritten to (tear Pred Xs)
With these fixes your code looks like this:
(define tear
(lambda (pred xs)
(cond
[(null? xs)
'()]
[(list? (car xs))
(cons (tear pred (car xs)) (tear pred (cdr xs)))]
[(pred (car xs))
(cons (car xs) (tear pred (cdr xs)))]
[else
(tear pred (cdr xs))])))
(tear number? '(1 2 3 a b c))
;=> (1 2 3)
(tear number? '(1 2 "not a number" 3 4))
;=> (1 2 3 4)
However, it still does something weird when there's a nested list:
(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=error> (() ())
To be consistent it should put the two lists into a list: ((1 2 3) (4 5)). To do that just remove the second cond case:
(define tear
(lambda (pred xs)
(cond
[(null? xs)
'()]
[(pred (car xs))
(cons (car xs) (tear pred (cdr xs)))]
[else
(tear pred (cdr xs))])))
(tear number? '(1 2 3 a b c))
;=> (1 2 3)
(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=> ((1 2 3) (4 5))
It now seems to do exactly half of what you want. You want it to return two lists: one for elements that passed, and one for the elements that failed. It currently is returning just the first list.
The first thing you should do is document how it returns those two lists. Since there are always exactly two, you can return them as multiple values.
;; tear returns two values:
;; - a list of the elements of `xs` that passed `pred`
;; - a list of the elements of `xs` that failed `pred`
There are two parts of using multiple values: returning them and receiving them. Use (values A B) to return them, and (let-values ([(A B) ....]) ....) to match on a result, like the result of a recursive call.
That means every recursive call like this (f .... (tear ....) ....) should become
(let-values ([(A B) (tear ....)])
(values (f .... A ....)
???))
Applying that to your code:
;; tear returns two values:
;; - a list of the elements of `xs` that passed `pred`
;; - a list of the elements of `xs` that failed `pred`
(define tear
(lambda (pred xs)
(cond
[(null? xs)
(values '()
???)]
[(pred (car xs))
(let-values ([(A B) (tear pred (cdr xs))])
(values (cons (car xs) A)
???))]
[else
(let-values ([(A B) (tear pred (cdr xs))])
(values A
???))])))
Now to fill in the ??? holes, use examples.
(tear number? '()) should return two empty lists: () ()
(tear number? '(1 2)) should return a full list and an empty list: (1 2) ()
(tear number? '(a b)) should return an empty list and a full list: () (a b)
The first example corresponds to the first ??? hole, the second example corresponds to the second hole, and so on.
This tells us that the first hole should be filled in with '(), the second hole should be filled in with B, and the third hole should be filled in with (cons (car xs) B).
(define tear
(lambda (pred xs)
(cond
[(null? xs)
(values '() '())]
[(pred (car xs))
(let-values ([(A B) (tear pred (cdr xs))])
(values (cons (car xs) A)
B))]
[else
(let-values ([(A B) (tear pred (cdr xs))])
(values A
(cons (car xs) B)))])))
(tear number? '(1 2 3 a b c))
;=> (1 2 3)
; (a b c)
(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=> ((1 2 3) (4 5))
; ("not a list")
This is a classic fold use-case. You're aggregating the list into two lists :
(define tear (lambda (pred lst)
(fold-right ; Aggregate over lst
(lambda (elem agg)
(let ((accepted (car agg))
(rejected (cadr agg)))
(if (pred elem)
; Create a new agg by adding the current element to the accepted list
`(,(cons elem accepted) ,rejected)
; Or, if the predicate rejected the element,
; Create a new agg by adding the current element to the rejected list
`(,accepted ,(cons elem rejected)))))
`(() ())
lst)))
So, if you use even? as your predicate, you can get:
> (tear even? `(1 2 3 4 5 6 7 8))
((2 4 6 8) (1 3 5 7))
Here's another way you can do it using continuation-passing style; this puts the recursive call in tail position.
(define (partition p xs (return list))
(if (null? xs)
(return null null)
(partition p
(cdr xs)
(lambda (t f)
(if (p (car xs))
(return (cons (car xs) t)
f)
(return t
(cons (car xs) f)))))))
(partition number? '())
;; => '(() ())
(partition number? '(a 1 b 2 c 3))
;; => '((1 2 3) (a b c))
(partition list? '(1 2 (3 4) (5 6) 7 8))
;; => '(((3 4) (5 6)) (1 2 7 8))
Above, we make use of Racket's default arguments. Below we show how to define partition using a helper function instead
;; procedure above, renamed to partition-helper
(define (partition-helper p xs return)
...)
;; new procedure without optional parameter
(define (partition p xs)
;; call helper with default continuation, list
(partition-helper p xs list))
Comments may help distill some of the style's mysterious nature
;; default continuation is `list`, the list constructor procedure
(define (partition p xs (return list))
(if (null? xs)
;; base case: empty list; return the empty result
(return null null)
;; inductive case: at least one x; recur on the tail...
(partition p
(cdr xs)
;; ...specifying how to continue the pending computation
(lambda (t f)
(if (p (car xs))
;; if predicate passes, cons x onto the t result
(return (cons (car xs) t)
f)
;; otherwise cons x onto the f result
(return t
(cons (car xs) f)))))))
#WillNess asks why we delay evaluating the predicate; I don't have a reason other than I think the readability above is pretty good. We can alter the implementation to check the predicate right away, if we please. The impact here is very subtle. If you don't see it, I encourage you to play pen-and-paper evaluator and compare the two processes to understand it.
;; default continuation is `list`, the list constructor procedure
(define (partition p xs (return list))
(if (null? xs)
;; base case: empty list; return the empty result
(return null null)
;; inductive case: at least one x; recur on the tail...
(partition p
(cdr xs)
;; ...specifying how to continue the pending computation
(if (p (car xs))
(lambda (t f)
;; if predicate passes, cons x onto the t result
(return (cons (car xs) t)
f))
(lambda (t f)
;; otherwise cons x onto the f result
(return t
(cons (car xs) f)))))))
Problem:
Write a function (split l) that takes a list and partitions it into two equal-sized (within one) lists, and returns a pair whose car is the first list and whose cdr is the second list.
My code:
(define split list)
(let ((half (/ (length list) 2)
(cons (car half list)
(cdr half list))))
Here's another possible implementation using the tortoise and hare algorithm:
(define (split lst)
(let loop ((tortoise lst) (hare lst) (acc '()))
(if (or (null? hare) (null? (cdr hare)))
(cons (reverse acc) tortoise)
(loop (cdr tortoise)
(cddr hare)
(cons (car tortoise) acc)))))
The above solution has the advantage of traversing the list only once, notice that we don't need to know the length of the list to make the split. It's called "tortoise and hare" because we keep two pointers over the list: one advances slowly, one element at a time (the "tortoise") and the other goes faster, two elements at a time (the "hare"). The algorithm stops when the hare reaches the end of the input list.
Alternatively, we can implement a more idiomatic (albeit slower) solution using built-in procedures. Assuming that the take and drop procedures are available in your interpreter (if not, import them from SRFI-1), this is closer to what you had in mind:
(define (split lst)
(let ((half (quotient (length lst) 2)))
(cons (take lst half)
(drop lst half))))
Either way, it works as expected:
(split '(1 2 3 4))
=> ((1 2) 3 4)
(split '(1 2 3 4 5))
=> ((1 2) 3 4 5)
Try:
(define (splitAt n lst)
(let loop ((acc '()) (n n) (lst lst))
(if (or (= n 0) (null? lst)) (cons (reverse acc) lst)
(loop (cons (car lst) acc) (- n 1) (cdr lst)))))
(define (split lst) (splitAt (quotient (length lst) 2) lst))
It works as follows:
(split '(1 2 3 4)) => ((1 2) 3 4)
(split '(1 2 3 4 5)) => ((1 2) 3 4 5)
Hope this helps.
I`m trying to implement a function that given an argument and a list, find that argument in the first element of the pair in a list
Like this:
#lang scheme
(define pairs
(list (cons 1 2) (cons 2 3) (cons 2 4) (cons 3 1) (cons 2 5) (cons 4 4)))
;This try only gets the first element, I need to runs o every pair on pairs
((lambda (lst arg)
(if (equal? (car (first lst)) arg) "DIFF" "EQ"))
pairs 2)
;This try below brings nok for every element, because Its not spliting the pairs
(define (arg) (lambda (x)2))
(map
(lambda (e)
(if (equal? arg (car e)) "ok" "nok"))
pairs)
The idea is simple, I have pair elements, and a given number. I need to see if the first element of the pairs (they are in a list) starts with that number
Thanks in advance
In Racket, this is easy to implement in terms of map. Simply do this:
(define (find-pair lst arg)
(map (lambda (e)
(if (equal? (car e) arg) "ok" "nok"))
lst))
Alternatively, you could do the same "by hand", basically reinventing map. Notice that in Scheme we use explicit recursion to implement looping:
(define (find-pair lst arg)
(cond ((null? lst) '())
((equal? (car (first lst)) arg)
(cons "ok" (find-pair (rest lst) arg)))
(else
(cons "nok" (find-pair (rest lst) arg)))))
Either way, it works as expected:
(find-pair pairs 2)
=> '("nok" "ok" "ok" "nok" "ok" "nok")
(find-pair pairs 7)
=> '("nok" "nok" "nok" "nok" "nok" "nok")
In Scheme, you should usually approach algorithms with a recursive mindset - especially when lists are involved. In your case, if you find the element in the car of the list then you are done; if not, then you've got the same problem on the cdr (rest) of the list. When the list is empty, you've not found the result.
Here is a solution:
(define (find pred list)
(and (not (null? list)) ; no list, #f result
(or (pred (car list)) ; pred on car, #t result
(find pred (cdr list))))) ; otherwise, recurse on cdr
With this your predicate function 'match if car of argument is n' is:
(define (predicate-if-car-is-n n)
(lambda (arg)
(eq? n (car arg))))
The above stretches your understanding; make sure you understand it - it returns a new function that uses n.
With everything together, some examples:
> (find (predicate-if-car-is-n 2) '((1 . 2) (2 . 3) (4 . 5)))
#t
> (find (predicate-if-car-is-n 5) '((1 . 2) (2 . 3) (4 . 5)))
#f
I'm trying to write a code (function) using Scheme that:
Takes a list of any size as a parameter
Multiplies every number of the list together
Symbols should be skipped over
Values inside pairs should be included in multiplication
In other words, results should be as follows:
> (mult '(1 2 3))
6
> (mult '(1 2 x 3 4))
24
> (mult '(1 2 z (3 y 4)))
24 (mine gives me 2)
My code allows me to skip over the symbols and multiply everything. However, once I include a pair inside the list, it acts as though it isn't a number, therefore acting like it doesn't exist. Here's my code:
(define mult
(lambda (x)
(if (null? x)
1
(if(number? (car x))
(* (car x) (mult (cdr x)))
(mult(cdr x))))))
I've tried to use append when it finds a pair, but clearly I did it wrong... Any help on how I could get it to include the values inside a pair would be much appreciated.
i.e. '(1 2 y (3 z 4) = 1 * 2 * 3 * 4
You are nearly there, just missing the list? test:
(define (mult lst)
(if (null? lst)
1
(let ((ca (car lst)))
(cond
((number? ca) (* ca (mult (cdr lst))))
((list? ca) (* (mult ca) (mult (cdr lst))))
(else (mult (cdr lst)))))))
EDIT
He're an equivalent version without let:
(define (mult lst)
(cond
((null? lst) 1)
((number? (car lst)) (* (car lst) (mult (cdr lst))))
((cons? (car lst)) (* (mult (car lst)) (mult (cdr lst))))
(else (mult (cdr lst)))))
As you see, (car lst) is likely to be evaluated more than once, so I used let in the first version to avoid this.
It is a slightly advanced technique but this problem can easily be formulated as a tail recursive algorithm.
(define (mult lst)
(let multiplying ((lst lst) (r 1)) ; r is the multiplicative identity...
(if (null? lst)
r
(let ((next (car lst))
(rest (cdr lst)))
(multiplying rest (cond ((number? next) (* next r))
((list? next) (* (mult next) r))
(else r)))))))
> (mult '(1 2 3 a b (((((10)))))))
60
Using tail recursion has performance implications but, admittedly, it is not the first thing to learn - recursion is. However, in this case, because lists are often very long, avoiding a stack frame for each list element can be a dramatic savings; using tail calls avoids the stack frame.