1
votes

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
i fixed it, now i get nothingJulie Rosen
In the innermost if expression, if (null? (cdr mylist)) is true, you should returns (car mylist) instead of an empty list '().Pie 'Oh' Pah

5 Answers

2
votes

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.
2
votes

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.

1
votes

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.

1
votes

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.

0
votes

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.