1

Im very new to scheme and the ideas of car, cdr, etc. I have this function to return the last element of a list, but right now it just returns an empty list.

 (define (last mylist)
      (if (list? mylist)
              (if (null? mylist)
                  (if (null? (cdr mylist))
                      '()
                      (last (cdr mylist)) 
                   )
              )        
      )
)

5 Answers5

2

The book How To Design Programs helps you answer this problem by giving you a specific and detailed design recipe. This particular problem is covered in section 9.2, "Non-empty Lists". Broadly, here are the steps you need to follow:

  • formulate a data definition for non-empty lists (or take it from the book)
  • write the purpose statement, signature, and header for your function
  • WRITE TEST CASES (your test cases are going to help a lot here. (keep in mind that you don't need to test inputs that aren't allowed by your data definition)
  • add the template associated with your data definition (also appears in the book)
  • fill in the blank places in the template to complete your definition
  • debug.
John Clements
  • 16,895
  • 3
  • 37
  • 52
2

By your indentation alone, it's very evident that you're coming to Scheme from another programming language

But you're also using if incorrectly – in Scheme, you cannot have a single-branch if statement. Well, there's no statements in Scheme at all, only expressions, and if expressions will always take 3 operands (arguments)

  1. the predicate (condition)
  2. the consequent (what happens if the predicate is true)
  3. the alternative (what happens when the predicate is false)

Your program is close tho. Just a little adjustment and you're right where you need to be – take note of how the indentation makes it easy to see if's 3 operands.

(define (last mylist)
  (if (null? mylist)
      #f
      (if (null? (cdr mylist))
          (car mylist)
          (last (cdr mylist)))))

Lastly, Scheme offers cond that helps prevent unnecessary code nesting for sequences of conditions

(define (last mylist)
  (cond ((null? mylist)
         #f)
        ((null? (cdr mylist))
         (car mylist))
        (else
         (last (cdr mylist)))))

(last '())
;; #f

(last '(1))
;; 1

(last '(1 2))
;; 2

(last '(1 2 3))
;; 3

Beyond the scope of this answer is the return value #f for (last '()) – I would argue that calling last on an empty list should have the same effect of calling car on an empty list. But I'll leave it up to you.

Mulan
  • 129,518
  • 31
  • 228
  • 259
1

If (null? mylist) does not hold, what is it? A non-empty list.

Non-empty lists may have one, or more, elements.

How many elements have such lists that their last element is their first?

What can be said about such lists' cdr? You should use this to stop your recursion earlier. Right now it continues until the list is empty, but you need to stop earlier than that.

(define (last mylist)
      (if (list? mylist)
              (if (null? mylist)
                  '()
                  ;; {1}
                  (last (cdr mylist))
                  ;;
                  )))

at {1} you call (last (cdr mylist)) unconditionally. But what if you've reached the end of your list? What if only one element is left? You'll need to return it as the result, in such case. So, replace the unconditional code with an if expression to accomplish this.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • I understand why I'm getting the empty list, I'm just not sure how to fix it. We were given a very small subset of the language to work with. – Julie Rosen Nov 02 '17 at 20:47
  • it will return the first and only element of the list – Julie Rosen Nov 02 '17 at 20:48
  • good, and that will be your answer - the last element. just write down an example: given `(3 2 1)`, what will `(null? lst)` return? what will `(cdr lst)` return? do we want to proceed with that result? same for `(2 1)`. then for `(1)`. which will give you the result. – Will Ness Nov 02 '17 at 20:49
  • I would need a car of the list with only one element, but I'm just not sure syntax-wise where that goes – Julie Rosen Nov 02 '17 at 20:56
  • you need to add another condition that checks the cdr of your list. – Will Ness Nov 02 '17 at 20:57
1

When you asked the question (null? (cdr mylist)), you should have returned (car mylist) if that's true instead of '(). Since at that point it means that mylist is a single-atom list.

(define (last mylist)
  (cond ((null? mylist) '())
        ((null? (cdr mylist)) (car mylist))
        (else (last (cdr mylist)))))

You could use cond instead of if to avoid nested conditions, since cond handle many arms while if is often used for when you have only two options for a condition.

This book the Little Schemer did best at helping me visualize what's going on in a Scheme program.

Pandemonium
  • 7,724
  • 3
  • 32
  • 51
0

I think this comes closest to your initial code:

(define (last mylist)
  (if (list? mylist)
      (if (null? mylist)
          '() ; input list is empty
          (if (null? (cdr mylist))
              (car mylist) ; list only has one remaining element so this is it
              (last (cdr mylist)))) ; otherwise, recurse
      #f)) ; input is not a list

When using if, be sure to always fill out both the true and the false branches.

uselpa
  • 18,732
  • 2
  • 34
  • 52