Loop Though Strings - lisp

I seem to be stuck trying to loop through strings to find characters that are not in the other string. The goal of the program is to loop though string one and document the characters that are not in the other string. The characters that are not in the other string will be printed out after all the checking is finished. They may not be repeated, hence I attempt to use three loops.
I am trying to debug the code below, since I have to eventually check both strings against each other, and I want to do this manually for the know how.
CG-USER(258): (defun stringprod (string1 string2)
(let ((newString nil))
(let ((letterSearchOn nil))
(loop for i from 0 below (length string1)
always
(setf (letterSearchOn (char string1 i))
(loop for j from 0 below (length string2)
(for ch = (char string2 j)
(/when (find ch letterSearchOn :test #'equal)
(append newString ch)))))))))
STRINGPROD
CG-USER(260): (stringprod "abc" "abc")
Error: (FOR CH = (CHAR STRING2 J)
(/WHEN (FIND CH LETTERSEARCHON :TEST #'EQUAL)
(APPEND NEWSTRING CH))) found where LOOP keyword expected.
Current LOOP context: FOR J FROM 0 BELOW (LENGTH STRING2)
(FOR CH = (CHAR STRING2 J)
(/WHEN (FIND CH LETTERSEARCHON :TEST #'EQUAL) (APPEND NEWSTRING CH))).
[condition type: PROGRAM-ERROR]
CG-USER(261):

How about something like this?
(defun remove-unsafe (str unsafe)
(remove-duplicates
(remove-if #'(lambda (c) (find c unsafe)) str)))

You need to check the syntax of LOOP: http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm
(loop for i from start1
for j from start2
do ...)
Your code has added parentheses, missing do words and weird characters like /.
If your question is homework, you should tag it as such. If not you can use SET-DIFFERENCE.
(set-difference (coerce "abc" 'list) (coerce "bcd" 'list))
-> (#\a)

Related

Alternating upcase/downcase for a string in Common Lisp

I want to write a function that will return a string formatted with alternative upcase/downcase in Common Lisp. For example, entering "stackoverflow" should return the string "StAcKoVeRfLoW". Here's my attempt, but it just returns a list of cons pairs. Am I on the right track?
(defun mockify (chars)
(let ((lst (coerce chars 'list)))
(if (equal lst nil) nil
(coerce (cons
(cons (char-upcase (car lst)) (char-downcase (cadr lst)))
(mockify (cddr lst)))
'string))))
CL-USER> (mockify "meow")
((#\M . #\e) (#\O . #\w))
Using MAP: we are creating a new string, moving over the original string and upcase/downcase based on an alternating boolean variable.
CL-USER 353 > (let ((string "stackoverflow")
(upcase t))
(map (type-of string)
(lambda (element)
(prog1 (if upcase
(char-upcase element)
(char-downcase element))
(setf upcase (not upcase))))
string))
"StAcKoVeRfLoW"
(defun mockify (chars)
(let ((lst (coerce chars 'list)))
(if (equal lst nil)
;; return nil
nil
;; return a string (coerce)
(coerce
;; a list whose elements are cons-cells, but ...
(cons (cons (char-upcase (car lst))
(char-downcase (cadr lst)))
;; ... the rest is computed by calling mockify,
;; which returns either an empty list or a string
(mockify (cddr lst)))
'string))))
The types of your expressions are confusing, and in fact your example leads to an error when using SBCL:
> (mockify "meow")
The value
(#\O . #\w)
is not of type
CHARACTER
when setting an element of (ARRAY CHARACTER)
[Condition of type TYPE-ERROR]
Also, you are going to have to handle corner cases in your code, because as is, it is possible that (cadr list), i.e. (second list), is called on a list that has only one element. Then, the result would be NIL and char-downcase would fail with an error.
Using only strings
I'd suggest writing a version of the function that does not use intermediate lists:
let R be the string-downcase of the whole string
then modify every other character of R by upcasing it
So for example, one way to do it (among others) would be:
(defun mockify (chars)
(let ((chars (string-downcase chars)))
(prog1 chars
(upcasify chars 0))))
(defun upcasify (string index)
(when (< index (length string))
(setf (char string index) (char-upcase (char string index)))
(upcasify string (+ index 2))))
Using only lists
If you prefer having a recursive function that processes lists, I'd rather define it in layers:
coerce string to list
process the list recursively
eventually, coerce the resulting list back to a string
This will avoid doing conversions from strings to lists at every step, and make the code simpler at each level.
(defun mockify (chars)
(coerce (mockify-list (coerce chars 'list)) 'string))
(defun mockify-list (chars)
...)
The list version is recursive and look like what you tried to do, but take care of corner cases.
There is more than one way to do it. Here is a loop based solution:
(let ((string "StackOverflow"))
(with-output-to-string (s)
(loop :for c :across string
:for up := t :then (not up)
:do (princ (if up
(char-upcase c)
(char-downcase c))
s))))
Fun thing - I actually wrote a similar thing some time ago.
https://github.com/phoe/string-pokemonize

Lisp - Keep words finishing by given letter

I am trying to modify this function in a way that when given a list it will only keep the words ending with a given letter. I have few restriction on what I am allowed to use and needs to keep char,rplacd and length to do it. I'm now having difficulties with the 'length ' part. I initially manage to do it in a way that it would keep all words starting with given letter but I am having trouble doing the opposite in line 5.
(setq liste '(have read nose art silence))
I would get the following result
(endingwith 'e liste) => (have nose silence)
(defun endingwith (x liste)
(cond
((not liste) nil)
((equal
(char (string (length (car liste))) 0)
(char (string x) 0) )
(rplacd liste (endingwith x (cdr liste))) )
(t (endingwith x (cdr liste))) ) )
Note that the task you have been given teaches a style of Lisp programming which is in the real world not used.
we need to operate of strings, which are vectors of characters
we can use the standard function remove
destructively changing a list is sometimes useful but can be avoided. See delete for a destructive version of remove
Example:
(defun keep-symbols-ending-with-char (char symbols)
"returns a sequence, where all symbols end with the given char"
(when (symbolp char)
(setf char (char (symbol-name char) 0)))
(remove char
symbols
:test-not #'eql
:key (lambda (item &aux (string (symbol-name item)))
(char string (1- (length string))))))
CL-USER> (keep-symbols-ending-with-char 'e '(have read nose art silence))
(HAVE NOSE SILENCE)
Given the limited resources you are given, this calls for a recursive solution. The value of (endingwith 'e liste) should be defined in terms of the value of calling endingwith with the rest of the list, and adding or not the first element if it matches 'e.
Further notice that in your case, length should be used with a string, so use (length (string (car liste))) instead of (string (length (car liste))).
The function would look like this:
(defun endingwith (x liste)
(cond
((not liste) nil)
((eql (char (string x) 0) (char (string (car liste)) (- (length (string (car liste))) 1)))
(cons (car liste) (endingwith x (cdr liste))) )
(t (endingwith x (cdr liste))) ))
Some points of style: don't use (not liste); instead use either (null liste) or (endp liste) which emphasize that liste is either an empty list, or that processing has reached the end of liste, respectively. Also, use '() when the intention is to represent an empty list; use nil when the intention is to represent boolean False.
The elements of liste are symbols, and x itself is a symbol; these symbols need to be converted to sequences so that the final character of the symbol can be assessed. string will do the job. But OP code has two problems here: length takes a sequence argument, so the value of (car liste) must also be converted using string; and sequences are zero-indexed in Common Lisp, so the last index of a sequence is one less than its length.
(defun endingwith (x liste)
(cond
((null liste) '())
((equal (char (string (car liste))
(- (length (string (car liste))) 1))
(char (string x) 0))
(rplacd liste (endingwith x (cdr liste))))
(t
(endingwith x (cdr liste)))))
One way to debug programs like this in Common Lisp is to get into the REPL and experiment. When you use a function and it sends you to the debugger, look for lines in that function that may have problems.
In the posted code, (char (string (length (car liste))) 0) is the first likely candidate. Try (car liste) at the REPL and see if that evaluates to 'HAVE as expected. When it does, try (length (car liste)). That will send you to the debugger again with a type error and a message like
LENGTH: HAVE is not a SEQUENCE.
This suggests that you need to use (string (car liste)) in the same way that (string x) is used in the next line of the original function definition. So, try (length (string (car liste))) at the REPL. Now you should see the expected value of 4, but it becomes apparent that the original line of code was a bit jumbled up, because char wants the first argument to be a string, and the second argument to be an index. So try again at the REPL (char (string (car liste)) (length (string (car liste)))). This again lands us in the debugger with a message like:
CHAR: index 4 should be less than the length of the string.
But that message reminds us that sequences are zero-indexed in Common Lisp, and that the last index of a string of length 4 is 3. So, once again at the REPL: (char (string (car liste)) (- (length (string (car liste))) 1)). Now we have success, with the REPL returning the expected #\E. Having worked through this problematic line at the REPL, we can now replace the line in the original function definition and see if that works. It does.
(defun ends-with-p (end s)
(string= end (subseq s (- (length s) (length end)))))
(defun keep-ending-with (end strings)
(remove-if-not #'(lambda (x) (ends-with-p end x)) strings))

return a line of text if match found

I am having some trouble working out how to return a line of text if a match is found.
(set 'wireshark "http://anonsvn.wireshark.org/wireshark/trunk/manuf")
(set 'arptable (map (fn (x) (parse x " ")) (exec "arp -a")))
(define (cleanIPaddress x)
(slice x 1 -1))
(define (cleanMACaddress x)
(upper-case (join (slice (parse x ":") 0 3) ":")))
(define (addIPandMACaddress x)
(list (cleanIPaddress (nth 1 x)) (cleanMACaddress (nth 3 x))))
(set 'arplist (map addIPandMACaddress arptable))
(set 'routerMAC (last (assoc (exec "ipconfig getoption en1 router") arplist)))
(find-all routerMAC (get-url wireshark))
returns
("20:AA:4B")
so I know that the code "works"
but I would like to retrieve the full line of text
"20:AA:4B Cisco-Li # Cisco-Linksys, LLC"
This can be performed simply by using a string-split procedure that allows us to use remove-if (the Common Lisp version of filter) to search through a string split by newlines removing any lines that do not contain the string we are searching for. That would result in a list of every line containing the string. The functions we will define here are already available via various Common Lisp libraries, but for the education purposes, we will define them all ourselves. The code you need works like so:
; First we need a function to split a string by character
(defun string-split (split-string string)
(loop with l = (length split-string)
for n = 0 then (+ pos l)
for pos = (search split-string string :start2 n)
if pos collect (subseq string n pos)
else collect (subseq string n)
while pos))
; Now we will make a function based on string-split to split by newlines
(defun newline-split (string)
(string-split "
" string))
; Finally, we go through our text searching for lines that match our string.
; Make sure to replace 'needle' with the string you wish to search for.
(remove-if #'(lambda (x)
(equal 'nil (search (string-upcase "needle")
(string-upcase x))))
(newline-split haystack))
You should be able to apply this strategy to the code you posted with a few small modifications. This code was tested on SBCL 1.0.55.0-abb03f9, an implementation of ANSI Common Lisp, on Mac OS X 10.7.5.
In the end I used:
(find-all (string routerMAC ".*") (get-url wireshark))

Removing characters from a string in Nyquist

How can I remove a certain character from a string in Nyquist (which is very similar to xlisp) and have the result returned?
I want to count how many "A" there are in a string like "ABBAAAABBBAABAAAB". (Yes, there are only 'A's and 'B's in the string.)
Since there is no (count) function in Nyquist I tried something like
(length (remove #\B mystring))
or
(length (remove #\B mystring :test equal))
But it doesn't work.
Forgetting the character count for a moment, how can I remove the 'B's from the string?
Will there always be only As and Bs in the string? If not, you might want to do something like
(remove #\A yourstring :test-not 'char=)
According to the XLISP reference for remove, the Nyquist remove doesn't deal with strings, only lists. You need to convert a string to a list in order to operate on it this way, but there's no coerce either. It's a touch hacky, but the easiest way around it I see is to stream a string and read-char it. This will produce a list of chars that you can then manipulate with remove.
(defun string->list (a-string)
(let ((collector nil)
(stream (make-string-input-stream a-string)))
(dotimes (c (length a-string) (reverse collector))
(setf collector (cons (read-char stream) collector)))))
It should now be possible to
(remove #\A (string->list yourstring) :test-not 'char=)
I see this is an old question, but since it has over 800 views, it's perhaps worth having the simple answer:
(defun remove-char (character sequence)
(let ((out ""))
(dotimes (i (length sequence) out)
(setf ch (char sequence i))
(unless (char= ch character)
(setf out (format nil "~a~a" out ch))))))
(setf mystring "ABBAABABCCCCBBCCCCAAA")
(remove-char #\B mystring) ;returns "AAAACCCCCCCCAAA"

str_replace in Common Lisp?

Is there some function similar to PHP's str_replace in Common Lisp?
http://php.net/manual/en/function.str-replace.php
There is a library called cl-ppcre:
(cl-ppcre:regex-replace-all "qwer" "something to qwer" "replace")
; "something to replace"
Install it via quicklisp.
I think there is no such function in the standard. If you do not want to use a regular expression (cl-ppcre), you could use this:
(defun string-replace (search replace string &optional count)
(loop for start = (search search (or result string)
:start2 (if start (1+ start) 0))
while (and start
(or (null count) (> count 0)))
for result = (concatenate 'string
(subseq (or result string) 0 start)
replace
(subseq (or result string)
(+ start (length search))))
do (when count (decf count))
finally (return-from string-replace (or result string))))
EDIT: Shin Aoyama pointed out that this does not work for replacing, e.g., "\"" with "\\\"" in "str\"ing". Since I now regard the above as rather cumbersome I should propose the implementation given in the Common Lisp Cookbook, which is much better:
(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
(with-output-to-string (out)
(loop with part-length = (length part)
for old-pos = 0 then (+ pos part-length)
for pos = (search part string
:start2 old-pos
:test test)
do (write-string string out
:start old-pos
:end (or pos (length string)))
when pos do (write-string replacement out)
while pos)))
I especially like the use of with-output-to-string, which generally performs better than concatenate.
If the replacement is only one character, which is often the case, you can use substitute:
(substitute #\+ #\Space "a simple example") => "a+simple+example"