How to write your own "list?" procedure in Scheme - list

I am trying to write my own 'list?' predicate in scheme. I know that definition of a list: 1. en empty list 2. pair, whose cdr is a pair
I know my code is wrong because it is returning true value on every pair, but every pair is not a list. I do not know how to implement the condition that cdr of the list must be also a pair.
(define (my-list? x)
(if (equal? x ()) #t
(pair? x)))

The list? procedure checks if a list is proper (nested cons-cells ending in a null value), hence we have to consider three cases:
(define (my-list? x)
(cond ((null? x) #t) ; empty list
((pair? x) (my-list? (cdr x))) ; pair, advance recursion
(else #f))) ; atom
A null value is considered an empty list. If we're in a pair, then we advance on its cdr by calling the procedure recursively. If it's not null nor a pair, it's an atom. This will cover edge cases, such as this:
(my-list? 42)
=> #f
(my-list? (cons 1 2))
=> #f
And will return true for proper lists:
(my-list? '())
=> #t
(my-list? (cons 1 (cons 2 '())))
=> #t

Related

Scheme function to reverse a list

For my programming languages class I'm supposed to write a function in Scheme to reverse a list without using the pre-made reverse function. So far what I got was
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (CONS (reverseList(CDR lst)) (CAR lst)))
))
The problem I'm having is that if I input a list, lets say (a b c) it gives me (((() . c) . b) . a).
How am I supposed to get a clean list without multiple sets of parenthesis and the .'s?
The problem with your implementation is that cons isn't receiving a list as its second parameter, so the answer you're building isn't a proper list, remember: a proper list is constructed by consing an element with a list, and the last list is empty.
One possible workaround for this is to use a helper function that builds the answer in an accumulator parameter, consing the elements in reverse - incidentally, this solution is tail recursive:
(define (reverse lst)
(reverse-helper lst '()))
(define (reverse-helper lst acc)
(if (null? lst)
acc
(reverse-helper (cdr lst) (cons (car lst) acc))))
(reverse '(1 2 3 4 5))
=> '(5 4 3 2 1)
You are half way there. The order of the elements in your result is correct, only the structure needs fixing.
What you want is to perform this transformation:
(((() . c) . b) . a) ; input
--------------------
(((() . c) . b) . a) () ; trans-
((() . c) . b) (a) ; for-
(() . c) (b a) ; mation
() (c b a) ; steps
--------------------
(c b a) ; result
This is easy to code. The car and cdr of the interim value are immediately available to us. At each step, the next interim-result is constructed by (cons (cdr interim-value) interim-result), and interim-result starts up as an empty list, because this is what we construct here - a list:
(define (transform-rev input)
(let step ( (interim-value input) ; initial set-up of
(interim-result '() ) ) ; the two loop variables
(if (null? interim-value)
interim-result ; return it in the end, or else
(step (car interim-value) ; go on with the next interim value
(cons ; and the next interim result
(... what goes here? ...)
interim-result )))))
interim-result serves as an accumulator. This is what's known as "accumulator technique". step represents a loop's step coded with "named-let" syntax.
So overall reverse is
(define (my-reverse lst)
(transform-rev
(reverseList lst)))
Can you tweak transform-rev so that it is able to accept the original list as an input, and thus skip the reverseList call? You only need to change the data-access parts, i.e. how you get the next interim value, and what you add into the interim result.
(define (my-reverse L)
(fold cons '() L)) ;;left fold
Step through the list and keep appending the car of the list to the recursive call.
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (APPEND (reverseList(CDR lst)) (LIST (CAR lst))))
))
Instead of using cons, try append
(define (reverseList lst)
(if (null? lst)
'()
(append (reverseList (cdr lst)) (list (car lst)) )
)
)
a sample run would be:
1]=> (reverseList '(a b c 1 2 + -))
>>> (- + 2 1 c b a)
car will give you just one symbol but cdr a list
Always make sure that you provide append with two lists.
If you don't give two lists to the cons it will give you dotted pair (a . b) rather than a list.
See Pairs and Lists for more information.

Search in a list with argument

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

LISP - get last list of a list

I'm trying to figure out how to obtain the last (non-empty) list from within another list, or return nil if there is no such list (recursively). This is an homework assignment, and as such I am looking for help on the method, not necessarily the code for it. Example:
(lastele '(1 (2 3) 4 5)) ;=> (2 3)
(lastele '(1 (2 3) (4 5)) ;=> (4 5)
(lastele '(1 2 3 4 5)) ;=> NIL
I was trying to run through the list, and if I encountered a sublist, I would check to see if the rest of the list contained any more non-empty sublists, if it did, continue with setting the list to that, and repeating until we had a null list.
(defun lastele2 (L)
(if (null L)
'()
(if (hasMoreLists (rest L))
(lastele2 (rest L))
(first L))))
It seems as if I can't get hasMoreLists to work, though. Returning t or f within is just erroring. Is this the best way to go about this?
First of all, note that you're implicitly assuming that none of the sublists are the empty list; if they could be the empty list, then nil is an ambiguous result, because you can't tell whether your function returned nil because there were no sublists, or because there were, and the last one was empty. E.g.,
(fn '(1 2 3 4 5)) ;=> nil because there are no sublists
(fn '(1 2 3 () 5)) ;=> nil because there are sublists, and the last one is nil
So, under the assumption that there are no non-null sublists in the toplevel list, we can continue.
A non-homework solution using standard functions
You don't need to write this. You can just use find-if with the predicate listp and specify that you want to search from the end by using the keyword argument :from-end t:
CL-USER> (find-if 'listp '(1 (2 3) 4 5) :from-end t)
(2 3)
CL-USER> (find-if 'listp '(1 (2 3) (4 5)) :from-end t)
(4 5)
CL-USER> (find-if 'listp '(1 2 3 4 5) :from-end t)
NIL
Writing your own
If you need to write something like this, your best bet is to use a recursive function that searches a list and keeps track of the most recent list element that you've seen as the result (the starting value would be nil) and when you finally reach the end of the list, you'd return that result. E.g.,
(defun last-list (list)
(labels ((ll (list result) ; ll takes a list and a "current result"
(if (endp list) ; if list is empty
result ; then return the result
(ll (cdr list) ; else continue on the rest of list
(if (listp (car list)) ; but with a "current result" that is
(car list) ; (car list) [if it's a list]
result))))) ; and the same result if it's not
(ll list nil))) ; start with list and nil
The local function ll here is tail recursive, and some implementations will optimize it into a loop, but would be more idiomatic to use a genuine looping construct. E.g., with do, you'd write:
(defun last-list (list)
(do ((result nil (if (listp (car list)) (car list) result))
(list list (cdr list)))
((endp list) result)))
If you don't want to use labels, you can define this as two functions:
(defun ll (list result)
(if (endp list)
result
(ll (cdr list)
(if (listp (car list))
(car list)
result))))
(defun last-list (list)
(ll list nil))
Alternatively, you could make last-list and ll be the same functions by having last-list take the result as an optional parameter:
(defun last-list (list &optional result)
(if (endp list)
result
(last-list (cdr list)
(if (listp (car list))
(car list)
result))))
In all of these cases, the algorithm that you're implementing is essentially iterative. It's
Input: list
result ← nil
while ( list is not empty )
  if ( first element of list is a list )
    result ← first element of list
  end if
  list ← rest of list
end while
return result
Something based on the code in the question
We can still find something that's a bit closer to your original approach (which will use more stack space), though. First, your original code with proper indentation (and some newlines, but there's more flexible in coding styles there):
(defun lastele2 (L)
(if (null L)
'()
(if (hasMoreLists (rest L))
(lastele2 (rest L))
(first L))))
The approach it looks like you're trying to use is to define the last sublist of a list L as:
nil, if L is empty;
if (rest L) has some sublists, whatever the last sublist of (rest L) is; and
if (rest L) doesn't have some sublists, then (first L).
That last line isn't quite right, though. It needs to be
if (rest L) doesn't have some sublists, then (first L) if (first L) is a list, and nil otherwise.
Now, you've already got a way to check whether (rest L) has any (non-null) sublists; you just check whether (lastele2 (rest L)) returns you nil or not. If it returns nil, then it didn't contain any (non-null) sublists. Otherwise it returned one of the lists. This means that you can write:
(defun last-list (list)
(if (endp list) ; if list is empty
nil ; then return nil
(let ((result (last-list (rest list)))) ; otherwise, see what (last-list (rest list)) returns
(if (not (null result)) ; if it's not null, then there were more sublists, and
result ; last-list returned the result that you wantso return it
(if (listp (first list)) ; otherwise, if (first list) is a list
(first list) ; return it
nil))))) ; otherwise return nil
This is implementing the an essentially recursive algorithm; the value of the subproblem is returned, and then lastList returns a value after examining it that result:
Function: lastList(list)
if ( list is empty )
   return nil
else
   result ← lastList(list)
  if ( result is not nil )
     return result
  else if ( first element of list is a list )
     return first element of list
  else
     return nil
  end if
end if
No, it's not the best way to go about this. To find whether the rest of list has more lists, you need to search it - and if it has, you restart scanning over the rest of your list.
I.e. you do a lot of back and forth.
Instead, just search along, and update a side variable to point to any list you find along the way.
(defun lastele (lst &aux a) ; a is NIL initially
(dolist (e lst a) ; return a in the end
(if (consp e) (setq a e))))

Procedures on lists (scheme)

I am writing a procedure which returns a list with all of the negative odd and positive
even integers removed (strings can stay), by using lambda in the primitive filter procedure. I also am avoiding using recursion, but that is what's stumping me.
What I have so far is:
(define (f2b lst)
(cond ((null? lst)'()) ; if the list is empty, return the empty list
((pair? (car lst)) ; if the current element isn't a list
(filter (lambda (x) (or (even? x) (positive? x))) (car lst))
(filter (lambda (x) (or (odd? x) (negative? x))) (car lst)))
(else (string? (car lst)) ;otherwise, if the current element is a string,
(car lst) ; then return that element
(f2b (cdr lst)))))
I'm also not sure how I can apply both of the filter procedures at the same time.
It's way simpler than that. All you have to do is filter the list. You just need the appropriate predicate.
When do you want to keep an element? You phrased it in terms of what you want to remove, so let's start with that. You want to remove if it's a negative odd or a positive even integer, and leave everything else in. It's easier to break it down into smaller functions.
(define (positive-even? x) (and (positive? x) (even? x)))
(define (negative-odd? x) (and (negative? x) (odd? x)))
(define (remove-num? x) (or (positive-even? x) (negative-odd? x)))
This defines whether to keep a number. But the list element might not be a number. So we
keep it if it's not a number, or if it doesn't match remove-num?:
(define (keep-element? x) (or (not (number? x)) (not (remove-num? x))
Then your function just has to call filter:
(define (f2b lst) (filter keep-element? lst))
Seems to work:
(f2b '(-4 -3 -2 -1 0 1 2 3 4 "a string" "another"))
=> (-4 -2 0 1 3 "a string" "another")
Here's how it would look as one big honkin' function:
(define (f2b lst)
(filter
(lambda (x)
(or (not (number? x))
(not (or (and (positive? x) (even? x))
(and (negative? x) (odd? x))))))
lst)
Personally, the nested or not or and gets a bit hard to read for my taste...
Ok, apparently you have nested lists. All you have to do here is map the result of the filter with a function which:
when given a list, returns (f2b lst)
otherwise, returns the element unchanged.
I will leave it as an exercise for you since, if you thought my function could possibly work on a nested list, clearly you have a lot of learning to do...

Scheme: Combining lists at a certain point

I am trying to define a procedure that takes an argument of 2 lists and a non-negative integer. Assume perfect input. I want it to construct a list of the elements in the first list UNTIL it reaches the given integer of elements at which point I want to finish it with the remainder of elements from the second list. Let me demonstrate.
~(combine '(a b c d e) '(w x y z) 2)
(a b y z)
Notice that the second list continued from the next index as if it were being made to cdr the whole time.
This is what I have. It doesn't work, of course and I think I may be using the wrong logic.
(define (combine seq1 seq2 point)
(if (null? (or seq1 seq2))
'()
(if (equal? point 0)
(cons seq2 '())
(cons (car seq1) (combine (cdr seq1) (cdr seq2) (- point 1) )))))
All help would be appreciated!
Thank you!
Well, this line doesn't do what you want:
(if (null? (or seq1 seq2))
In Scheme, the empty list is a true value; the only false value in Scheme is #f. That means that
(or '() '(a))
returns '() but
(or '(a) '())
returns '(a)
So your check for null will return #t only half of the time. (And if '() were a false value, as it is in regular Lisp, then it would never work, so I'm not sure what you are thinking).
If you want that line to return true if either of the lists is null, you will have to rethink it.
Then there's the line
(cons seq2 '())
Try testing that function on its own and see what happens - it's not what you need. If you think about it, that line should be rather simpler. What do you actually need to return at that point?