I am attempting to accept a list, have it count the positive, negative, and zeros, and return a new list.
The only thing I notice as I'm debugging is that the list is iterating through, but it is not utilizing any of the conditionals. So its successfully recursively calling itself, and then it just errors once its empty.
(define (mydisplay value)
(display value)
(newline)
#t
)
(define neg 0)
(define z 0)
(define pos 0)
(define (posneg lst)
(cond
((NULL? lst))
(NEGATIVE? (car lst) (+ 1 neg))
(ZERO? (car (lst)) (+ 1 z))
(else (+ 1 pos))
)
(posneg (cdr lst))
)
(mydisplay (posneg '(1 2 3 4 2 0 -2 3 23 -3)))
(mydisplay (posneg '(-1 2 -3 4 2 0 -2 3 -23 -3 0 0)))
(mydisplay (posneg '()))
A better way to keep values while computing are by making a helper that has the data you want to keep as arguments. Updating the value is the same as recursing by providing a new value:
I like @naomik's abstraction but unboxing/boxing for each iteration within the helper is perhaps overkill. Though having a contract is nice and both Racket and R6RS supports making your own types:
An alternative would be it returned the values as separate results with values or it could take a continuation:
#!racket
has optional arguments so the prototype could be just without having to have the first expression to check if there was passed an extra argument or not:OK, my favorite technique to apply here is wishful thinking as I learned it from Gerald Jay Sussman and Hal Abelson in the Structure and Interpretation of Computer Programs (SICP) course. Particularly, video lecture 2B. Compound Data will be of interest to you.
Let's start by just pretending (wishing) with have this data container available to us that holds 3 values: one for positives, one for negatives, and one for zeros. We'll call it
pnz
.The way to create one of these is simple
To select the positives value
To select a negatives value
To select the zeros value
Forget for a moment that these procedures don't exist (yet). Instead, just wish that they did and begin writing the procedure to solve your problem.
We'll start with some pseudocode
OK, that's pretty straight forward actually. Well, with one little gotcha. Notice I'm saying "update the count by one"? Well we need somewhere to store that count as the procedure is iterating. Let's make a slight adjustment to the pseudocode, this time including a
pnz
parameter to keep track of our current countNow this procedure makes sense to me. In the simplest case where
xs
is an empty list, it will simply return apnz
of(0 0 0)
. Ifxs
has any number of values, it will iterate through the list, one-by-one, and increment the corresponding value in thepnz
container.Translating this into scheme is a breeze
You'll notice I used a
let
here to make it easier to reference the individualp
,n
,z
values of the current iteration. That way, when we detect a positive, negative, or zero, we can easily increment the appropriate value by simply doing(+ 1 p)
,(+ 1 n)
, or(+ 1 z)
accordingly. Values that are not meant to be incremented can simply be passed on untouched.We're getting extremely close. Our procedure make logical sense but we need to define
make-pnz
,positives
,negatives
, andzeros
before it can work. By the way, this methodology of defining data objects by creating constructors and selectors to isolate use from representation is called data abstraction. You'll learn more about that in the video I linked, if you're interested.So here's the contract that we need to fulfill
Let's implement it !
Pff, well that was easy as can be ! Using a
list
,car
,cadr
, andcaddr
made our job simple and it's easy to understand howpnz
behaves.Without further ado, let's see this thing in action now
And there you have it. Wishful thinking and a dash of data abstraction to the rescue.
Data abstraction is a very powerful concept. You might be thinking, "Why didn't we just use
list
in thecount-pnz
procedure instead of all of this constructor/selector ceremony?" The answer may not be readily apparent, but it is a bit too much for me to get into with this post. Instead, I sincerely do hope you check out the learning resources I linked as I'm certain they will be of great benefit to you.Yep, that's totally true. Let's see
make-pnz
,positives
,negatives
, andzero
implemented in a different way. Remember, the contract still has to be fulfilled in order for this implementation to be considered valid.Pretty cool. This demonstrates the beauty of data abstraction. We were able to completely re-implement
make-pnz
,positives
,negatives
, andzeros
in a different way, but because we still fulfilled the original contract, ourcount-pnz
function does not need to change at all.First, let me say that @naomik's answer is awesome. This is the way to deconstruct the problem and build it up step by step.
When I first read the question, what I ended up thinking was:
So
reduce
means perhaps usingfoldl
orfoldr
.Here is example with
foldr
(returning a list in the format'(p n z)
):The body of the solution is the
lambda
we are using to reduce. Essentially, this just adds 1 to thep
,n
orz
position of the list (usingcar
,cadr
andcaddr
respectively).*note, this solution is not optimized.