Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Lisp: The Remove Function, how is it used?

I have a query request-uri in the form of "/node/143" (just an example of the format).

I want to strip the first forward slash from the string, I looked up the function remove and had a try. I just can't seem to get it working (I'm using SBCL on Linux).

I've set the request-uri using this code.

(setq request-uri "/node/143")

When I check the variable I have this returned.

request-uri
"/node/143"

I now try to remove the first slash (at this point it's just anything at all to see how the function is properly used).

(remove "/" request-uri)
"/node/143"

(remove '/ request-uri)
"/node/143"

I even tried supplying a list

(remove '("/") request-uri)
"/node/143"

(remove '('/) request-uri)
"/node/143"

Even though strings are vectors of characters I thought that somehow maybe the whole string may be placed in one cell and I tried to remove the whole thing, still no luck.

(remove "/node/143" request-uri)
"/node/143"

(remove '/node143 request-uri)
"/node/143"

So I'm at a loss right now, this seemingly simple function has really eluded me, I thought I followed the documentation to the letter, but nothing is working.

Can anyone shed some light on what's happening here?

Thanks.

Edit: I found the answer to my question, which raised another question.

To remove an element from the string I used

(remove #\/ request-uri)

What about a whole string

`(remove #\node request-uri`)

Only works for the first character and throws an error, and the following all do nothing.

(remove "node" request-uri)
(remove 'node request-uri)
(remove ?\node request-uri)
(remove #\node request-uri)
(remove '("node") request-uri)

I'm not sure how else it should be addressed here.

like image 871
BlueBadger Avatar asked Mar 21 '09 14:03

BlueBadger


2 Answers

Learn to read the Common Lisp HyperSpec.

Strings are Sequences, Arrays (one-dimensional vectors of characters) and, well, Strings. This means that most of those functions are applicable.

Let's look at REMOVE. CLHS gives this signature:

remove item sequence &key from-end test test-not start end count key
    => result-sequence

Strings are sequences of characters. So a call to remove would be:

(remove #\/ "/foo/")

or (for example)

(remove #\/ "/foo/" :start 2)

Remember: #\a is a character. #\node is no character. Illegal. A string is "/foo/".

The item to REMOVE from a string has to be a character. Nothing else. Why? Because the TEST is by default EQL and EQL compares the character in the string with your item argument. Also key is by default IDENTITY and does not change the items.

What if your argument is a string? Well, then you have to do more:

 (remove "/" "/abc/" :key #'string :test #'equal)

This looks at each character of the sequence and makes it into a string. The string then will be compared with your item "/" using the function EQUAL. This works also. The cost is that it needs to generate a string from each character of "/abc/", each string is a new object.

Another way to do it is:

 (remove "/" "/abc/" :test (lambda (a b) (eql (aref a 0) b)))

Above retrieves the first character of "/" in every test and compares it with the character from the string "/abc/". Again the cost is that it needs to get the character five times (in this example).

So the best way to write it if your original object comes as a string:

(remove (aref "/" 0) "/abc/")

Above we get the character from the string "/" once and then REMOVE compares this character with the default EQL test with each character in the string - it returns a new string of those characters that are not EQL to #\/.

What do you expect ?\foo to be? In Common Lisp this is the symbol |?fOO|.

Also (remove "foo" "afoob") does not work since a string ("foo" here) is not an element of a string. Remember, characters are elements of strings. Remember also that strings with one item like "/" are still strings and not a character. Thus "/" and #\/ are of different type. The first is a string and the second is a character.

SUBSEQ extracts a sequence from a sequence. That means it also extracts a string from another string:

(subseq "0123456" 1 5)
     where 1 is the start and 5 is the end index.

CONCATENATE appends sequences. This means that it also appends strings.

(concatenate 'string "abc" "123")
  returns a new string with the strings "abc" and "123" appended.

To remove parts of a string see also the functions STRING-TRIM, STRING-LEFT-TRIM and STRING-RIGHT-TRIM.

So, as in one other answer to remove substrings from a string you need to write some code to extract some strings and then concatenate those.

SEARCH searches for strings in strings.

like image 117
Rainer Joswig Avatar answered Sep 17 '22 18:09

Rainer Joswig


To remove a whole substring, you will have to make a new function, e.g.:

(defun remove-string (rem-string full-string &key from-end (test #'eql)
                      test-not (start1 0) end1 (start2 0) end2 key)
  "returns full-string with rem-string removed"
  (let ((subst-point (search rem-string full-string 
                             :from-end from-end
                             :test test :test-not test-not
                             :start1 start1 :end1 end1
                             :start2 start2 :end2 end2 :key key)))
    (if subst-point
        (concatenate 'string
                     (subseq full-string 0 subst-point)
                     (subseq full-string (+ subst-point (length rem-string))))
        full-string)))

This is just a starting point. I replicated all options for SEARCH, but I think that some options don't make sense in this context. At least this works:

(remove-string "node" "this is a node or not")

For more elaborate cases, you should perhaps take a look at cl-ppcre, a regex library.

like image 23
Svante Avatar answered Sep 20 '22 18:09

Svante