Using recursion to getNth position for Racket - list

I have to write a recursive call in Racket that allows us to get the Nth position of a given list. for example (getNth 3 '(1 2 3 4)) should return back 4 and if the list is empty or if the position is non existent then return and empty list.
this is my code:
(define getNth
(lambda (ls)
(if (= ls null?) 1
(getNth ls(append '(car ls)(cdr ls))))))
getting this error:
getnth: arity mismatch;
the expected number of arguments does not match the given number
expected: 1
given: 2
arguments...:
4
()
if you need the recursive statement here it is:
base case-getNth N LS) = (), if LS = null
recursive call -(getNth N LS) = (getNth N (append '(car LS)(cdr LS))), if N >= 1
im getting stumped I dont know how to implement this one.

Assuming your getNth can revieve a "N" from a list (rather than a fixed N i.e. get3rd), your are indeed missing an argument:
(define getNth
(lambda (n list)
(cond ((null? list) '())
((= n 0) (car list))
(else (getNth (- n 1) (cdr list))))))
Which basically is until I get to the number I want, decrement the it and "pop" the first element out until the first element of the list is the one I want i.e. the rest of the list.
Your lambda only takes one argument the way you defined it which is exactly the error message you're having ;)
I think I appropriately balanced my parenthesis :)
A trick with recursion: it's generic concept is easy:
A recusrion is "if a terminal condition occurs, I'm done, return appropriate value. Else, increment the argument toward termination condition and call myself with these new arguments"
It gets complicated when the termination condition can have exceptions. Because if your can't trivially expressed it as "if this done else redo with increment" you could make mistakes...

There are several issues with you code..
You say you need a procedure that takes two arguments, but yours take only ls.
When ls is the empty list the result is 1
if list is not empty you recurse with two arguments, both lists. The first is the same as the original argument which would make a infinite recursion. The second is an append between '(car lst) which evaluates to the list (car lst) and (cdr ls) (the the list ls except the first pair)
(define get-nth
(lambda (index lst)
(if (= index 0) ; when index is zero
(car lst) ; return the first element
(get-nth (- index 1) ; else recurse with the decrement of index
(cdr lst))))) ; and all but the first element (the rest) of lst
;; test
(get-nth 0 '(a)) ; ==> a
(get-nth 1 '(a b)) ; ==> b
;; However, this won't work:
(get-nth 0 '()) ; will fail
(get-nth 10 '(a b c)) ; will fail
I kind of like the fact that it doesn't work when given wrong arguments. list-ref and car fails just as much if gived wrong parameters, but that is easy to fix:
(define get-nth
(lambda (index lst)
(cond ((not (pair? lst)) '()) ; it should always be a pair
((= index 0) (car lst)) ; when index is zero, return the first element
(else (get-nth (- index 1) ; else recurse with the decrement of index
(cdr lst)))))) ; and all but the first element (the rest) of lst
Instead of just th empty list you can have a default value or perhaps throw an error with (raise 'get-nth-error) (R6RS+)

Related

Why list-ref works with pair

In the following code:
(define l (cons 1 2))
l
(list? l)
(pair? l)
(list-ref l 0) ; works;
(list-ref l 1) ; DOES NOT WORK;
Output:
'(1 . 2)
#f
#t
1
Error: list-ref: index reaches a non-pair
index: 1
in: '(1 . 2)
Why (list-ref l 0) works if l is not a list. Otherwise, why (list-ref l 1) does not work. This behavior seems to be inconsistent and confusion creating.
The functions car and cdr also work, returning 1 and 2, respectively. Why should cdr work if this is not a list. One would expect car to return 1.2 and not just 1.
From the documentation:
The lst argument need not actually be a list; lst must merely start with a chain of at least (add1 pos) pairs.
This means the lst argument is allowed to be an improper list as long as the index is smaller than the index of the first pair whose cdr is a non-pair.
A list is simply a lot of pairs linked together terminating in an empty list. For example, (list 1 2 3) is equivalent to (cons 1 (cons 2 (cons 3 '()))).
There's no reason to loop through the entire list to check if l is a list, so as long as it looks like a list, scheme doesn't care if it terminates in an empty list. list-ref is basically defined as so:
(define (list-ref lyst index)
(cond ((= index 0) (car index))
(else (list-ref (cdr lyst) (- index 1)))))
So of course it will encounter an error when it tries to run (car 2)
As for the other question, car and cdr simply return the first element in a pair and the second element in a pair respectively.

New to Racket: why is the if statement affecting the return?

I just started learning Racket so I am still trying to figure out the intricacies of the language. I am trying to implement my own search function in a list. If the function finds it, it returns the index, otherwise it returns -1.
(define (find-index list item)
(if (equal? (length list) 1)
(if (equal? (first list) item) 0 1)
(if (equal? (first list) item)
0
(+ 1 (my-search (rest list) item)))))
So the find-index function is a recursive function that walks through the list looking for an item that is equivalent to "item." I have written it so that, if there are 4 elements in a list, the function can return any number between 0-4.
(define (my-search list item)
(define length (my-length list))
(define index (find-index list item))
(if (= index length) -1 index))
My idea is that if the find-index function returns a number equal to the list's length, it means the function did not find the item, therefore my-search function is supposed to return -1.
However, when I put in
(my-search (list "apple" "barbecue" "child" "demon" "enter") "fire")
the result I get is 3, instead of -1. If I print index before the if statement the index is 3 instead of 5. If
(if (= index length) -1 index))
is not part of the my-search function then everything is fine.
What I think is going on is that index is the id for the function itself, not the result of the function. However, I don't see why that effects the returning result of my-search. Is anyone willing to shed some light on this question?
Also, any style critique is welcome. I would like to know if I am not following conventions.
The weird behavior is caused by the fact that find-index is calling my-search which is calling find-index (a mutual recursion!). At some point, that extra if is causing the recursion to end prematurely. Solution: replace the call to my-search with find-index in the find-index procedure.
Now that that was settled, we can write a single procedure for finding an element's index in a list or signaling that it wasn't found, like this:
(define (find-index lst item)
(cond ((empty? lst) #f)
((equal? (first lst) item) 0)
(else
(let ((result (find-index (rest lst) item)))
(if result (add1 result) #f)))))
Let's see how the above improves your procedure:
The preferred way to structure a procedure with multiple conditions is to use a cond
You should not use list as a parameter name, it clashes with a built-in procedure with the same name
For the same reason, you should not call length a local definition
It's not a good idea to use length for checking if we stepped outside of a list, a well-built recursion will take care of that, without having to iterate over the list all over again
It's common to use #f to indicate that a search procedure didn't find what it was looking for
In a well-structured recursion over lists, you should check if a list is empty, normally this is the first base case we write - your procedure will fail if an empty list is passed
We use let to declare local variables, in this case it makes sense, to avoid calling the recursion twice
Use (add1 x), it's more idiomatic than (+ 1 x)
But wait, we can do better! The above solution can be rewritten in a tail-recursive style; by ensuring that the recursive call is the last thing we do, our procedure will use constant space, and it'll be as efficient as a loop in a conventional programming language. The trick is to pass an extra parameter with the value to return (in this case, the index). I'll use a named let for brevity:
(define (find-index lst item)
(let loop ((lst lst) (idx 0))
(cond ((empty? lst) #f)
((equal? (first lst) item) idx)
(else (loop (rest lst) (add1 idx))))))
You can verify that both procedures work as advertised:
(find-index (list "apple" "barbecue" "child" "demon" "enter") "fire")
=> #f
(find-index (list "apple" "barbecue" "child" "demon" "enter") "child")
=> 2
That's how I would attack the problem.
(define (find-index L item) ; L your list. item the item for which you want the index
(define (aux L res) ; Auxiliary function. L your list. item the item for which you want the index
(cond ((null? L) -1) ; No thing was found, return -1.
((eq? (car L) item) res) ; If the item is equal to the current item then return the position.
(else (aux (cdr L) (add1 res))))) ; Move on to the next item in the list and increment the position.
(aux L 0)) ; Call of the auxiliary function that will be doing the job
Test run...
(define L '(a b c d))
Element not in the list
(find-index L 'e)
Output : -1
Element "d"
(find-index L 'd)
Output : 3
Here is a version of find-index that attempts to use the same style as your original example. Instead of list I use xs (which is short for "list of xes").
Note that it would be better to use the false value #f to indicate "not found".
(define (find-index xs item)
(if (empty? xs)
-1 ; not found
(if (equal? (first xs) item)
0 ; found at beginning
(let ([index-in-rest (find-index (rest xs) item)]) ; index in rest of list
(if (= index-in-rest -1)
-1 ; not found in rest of list
(+ 1 index-in-rest)))))) ; add 1 because we skipped
the first element

Scheme - Recursion : Sum consecutive elements of a list

I'm trying to write a function using Scheme that :
take a list of integers with more than two elements as a parameter
sum the n-th-element and (n+1)-th-element
return this list
Result should be as follows :
> (SumNeighbors (list 1 2 3 4))
(3 5 7)
I think I get the way to add elements but my recursion is totally wrong...
(define (SumNeighbors lst)
(if (not (null? (cdr lst)))
(append (list (+ (car lst) (car (cdr lst)))) (SumNeighbors (cdr lst)))))
Any help would be appreciated.
The solution to this problem follows a well-known pattern. I'll give you some hints, it'll be more fun if you find the answer by your own means:
(define (SumNeighbors lst)
(if <???> ; if there's only one element left
<???> ; we're done, return the empty list
(cons ; otherwise call `cons`
(+ <???> <???>) ; add first and second elements
(SumNeighbors <???>)))) ; and advance recursion
Notice the following:
Your solution is lacking the base case - what happens when the list we're traversing only has one element left? it's time to finish the recursion! and because we're building a list as the output, what should be the value returned?
We normally use cons to build an output list, not append. That's the natural way to build a list
The part of this procedure that falls outside the solution template is the fact that we stop when there's a single elment left in the list, not when the list is empty (as is the usual case)
You'll see that many procedures that iterate over an input list and return a list as output follow the same solution template, it's very important that you learn how and why this works, it's the foundation for writing solutions to other similar problems.
#!r6rs
(import (except (rnrs base) map)
(only (srfi :1) map))
(define (sum-neighbors lst)
(map + lst (cdr lst)))
The higher order function map as defined in SRFI-1 supports uneven lenght arguments. It will stop at the shortest list.
If you call (sum-neighbors '(1 2 3 4)) it will become (map + (1 2 3 4) (2 3 4)) which is the same as (cons (+ 1 2) (cons (+ 2 3) (cons (+ 3 4) '())))

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))))

Return all elements on nesting depth n of a nested list

I'm really puzzled on how to do this... I can't even figure out how to start, I know how to do it for a binary tree but I want to be able to do it with any form of nested list, can anyone help me out please?
For this one, you need to use the template for traversing an arbitrarily nested list of elements. For example, study this procedure that copies an arbitrarily nested list:
(define (copy lst)
(cond ((null? lst) ; if list is empty
'()) ; return the empty list
((not (list? (car lst))) ; if current element is not a list
(cons (car lst) ; cons current element
(copy (cdr lst)))) ; with the rest of the list
(else ; if current element is a list
(cons (copy (car lst)) ; cons recursive call over current element
(copy (cdr lst)))))) ; with recursive call over rest of the list
A little convention first. Let's say that 1 is the base level, and that all the elements returned will be in a flat output list (without preserving the original structure of the input list). For example:
(elements-level '(1 2 3) 1)
; => '(1 2 3)
(elements-level '(1 (2) 3) 2)
; => '(2)
With the above template in mind, let's see how we can modify it for solving the problem at hand. I'll let you fill-in the blanks, because the question looks like homework:
(define (elements-level lst lvl)
(cond ((or (null? lst) (< lvl 1)) ; if list is empty or below level
'()) ; return the empty list
((not (list? (car lst))) ; if current element is not a list
(if (= lvl <???>) ; if `lvl` is the base level
(cons <???> ; cons current element in list
(elements-level <???> lvl)) ; and advance recursion over cdr part
(elements-level <???> lvl))) ; else advance recursion over cdr part
(else ; if current element is a list
(append ; use `append` for flattening the list
(elements-level <???> <???>) ; recur over car, decrease one level
(elements-level <???> <???>))))) ; recur over cdr, keep the same level
Test the procedure with this list, it must return '(1) for level 1, '(2) for level 2, and so on:
(elements-level '(1 (2 (3 (4 (5))))) 1)
; => '(1)
I think you can use recursion with steps as below:
Define a list to hold all elements at nth depth.
create a recursion function, which takes nested list, new list and n as argument
For each element of the nested loop, call the recursion function itself by passing the child list and depth as n-1.
Add all elements of the nested list to new list when n = 0.
Once this method is done, you will all elements of depth n in the new list.
Possible Enhancement:
If it is possible the some of the list elements don't extend up to nth level, then you may want to check the type of elements before calling the recursion method. If it's of type leaf, then simply add the element in the new list.