0

Working on CLISP in Sublime Text.

Exp. in CLISP : less than 1 year

It's already for a while that I'm trying to solve this exercice... without success... as you might guess.

In fact I have to create a function which will modify the list and keeps only sublists which are equals or greater than the given number (watch below)

The list on which I have to work :

(setq liste '((a b) c (d) (e f) (e g x) f))

I'm supposed to find this as result :

(lenght 2 liste) => ((a b) (e f) (e g x))
liste => ((a b) (e f) (e g x))

Here my code :

(defun lenght(number liste)
    (cond
        ((atom liste) nil)
        ((listp (car liste))
            (rplacd liste (lenght number (cdr liste))) )
        ((<= (lenght number (car liste)) number)
         (I don't know what to write) )
        ((lenght number (cdr liste))) ) )

It will be very kind if you could give me only some clue so as to let me find the good result.

Thanks guys.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
fatalispo
  • 1
  • 2
  • is this homework? – sds Jun 27 '18 at 22:46
  • 1
    note that [`length`](http://clhs.lisp.se/Body/f_length.htm) is a standard lisp function - you might want to use something other than the obviously misspelled `lenght`. – sds Jun 27 '18 at 22:48
  • 2
    You cannot mutate a literal like `'((a b) ...)` . Are you sure it should mutate the list and not just create a new list with the result? You'll run into all sorts of problems if you you aretrying to remove the first element the way you do it. It is much easier to make a function that creates a new list with the elements. You are mixing your function `lenght` and the standard function `length` which is the one you should use to check the length of the elements. – Sylwester Jun 27 '18 at 22:55
  • Yes, it's an homework and I asked the teacher first for some help and he told me that he doesn't understand why I'm blocked... He said that I should add another 'test' and it will be fine. But i haven't found the test that I should do... And I called the function 'lenght' (with a mistake, sorry for this) because I've just translated the exercice from french. And yes, I have to mutate straight the list so as to not waste space address. It's the aim of the chapter that I'm doing. This is a kind of way of doing which is called in french "opération chirurgicale" (surgery operation?). Thanks a lot – fatalispo Jun 28 '18 at 14:35

3 Answers3

4

Modifying the list does not make much sense, because it gets hairy at the head of the list to retain the original reference. Return a new list.

This is a filtering operation. The usual operator in Common Lisp for that is remove-if-not (or remove-if, or remove, depending on the condition). It takes a predicate that should return whether the element should be kept. In this case, it seems to be (lambda (element) (and (listp element) (>= (length element) minlength))).

(defun filter-by-min-length (minlength list)
  (remove-if-not (lambda (element)
                   (and (listp element)
                        (>= (length element) minlength)))
                 list))

In many cases, when the condition is known at compile time, loop produces faster compiled code:

(defun filter-by-min-length (minlength list)
  (loop :for element :in list
        :when (and (listp element)
                   (>= (length element) minlength))
          :collect element))

This returns a new list that fulfills the condition. You'd call it like (let ((minlength-list (filter-by-min-length 2 raw-list))) …).

Many basic courses insist on recursively using primitive operations on cons cells for teaching purposes at first.

The first attempt usually disregards the possible stack exhaustion. At each step, you first look whether you're at the end (then return nil), whether the first element should be discarded (then return the result of recursing on the rest), or if it should be kept (then cons it to the recursion result).

If tail call optimization is available, you can refactor this to use an accumulator. At each step, instead of first recursing and then consing, you cons a kept value onto the accumulator and pass it to the recursion. At the end, you do not return nil, but reverse the accumulator and return that.

Svante
  • 50,694
  • 11
  • 78
  • 122
  • Yes, it's just an homework which is supposed to make me practice "surgery operation" = to change the way that pointer interacts with each other. The teacher doesn't want me to use : loop, if, lambda, etc.. He only wants me to use : (defun A( b c) (cond ((when to stop)) ((test) execution) ((test) execution) ((etc.)) By this way I'm blocked because I don't arrive to find the issue so as to create a function which does this : 1 - select only sublists = listp 2 - count the number of element in a sublist 3 - checked if it's equal or greater than the given number – fatalispo Jun 28 '18 at 14:50
  • Anyway I'll try to apply your advises in a certain way this evening and let you know. Thank you for your answer =) – fatalispo Jun 28 '18 at 14:52
0

Well, I have found the answer that I was looking for, after scratching my head until blood...

Seriously, here is the solution which is working (and thanks for the correction about length which helped me to find the solution ^^) :

(defun filter-by-min-length (min-length liste)
    (cond
        ((atom liste) nil)
        ((and (listp (car liste))(>= (length (car liste)) min-length))
            (rplacd liste (filter-by-min-length min-length (cdr liste))) )
        ((filter-by-min-length min-length (cdr liste))) ) )
fatalispo
  • 1
  • 2
0

A non-modifying version

(defun filter-by-min-length (min-length le)
  (cond ((atom le) nil)
        ((and (listp (car le)) (>= (length (car le)) min-length))
         (cons (car le) (filter-by-min-length min-length (cdr le))))
        (t (filter-by-min-length min-length (cdr le)))))

Test:

(defparameter *liste* '((a b) c (d) (e f) (e g x) f))
(filter-by-min-length 2 *liste*)
;; ((A B) (E F) (E G X))
*liste*
;; ((A B) C (D) (E F) (E G X) F)  ; -> *liste* not modified

For building good habits, I would recommend to use defparameter instead of setq, since the behaviour of setq might not always be defined (see here). In the link, it is said:

use defvar, defparameter, or let to introduce new variables. Use setf and setq to mutate existing variables. Using them to introduce new variables is undefined behaviour

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
  • Thank you for this advise. I'll apply this in my future works =) and the link is super useful. I will mainly use in future defvar or defparameter. – fatalispo Jun 29 '18 at 16:59