I am writing a function which can return the uppercase alphabets from an input string. And it works well when I display it. However, can anyone tell me how to return the output string rather than just display it?
(define (convert input)
(define s(string))
(for ([i (string->list input)])
(when (char-alphabetic? i)
(let ((s(string-append s (string i))))
(display (string-upcase s))))))
If you want to return data from a function, like you are here with returning a string, I suggest you look past the basic for loop to its variants, such as for/list, for/vector, for/hash, and for/fold. In this case for/list can help:
(define (convert input)
(list->string
(for/list ([i input] #:when (char-alphabetic? i))
(char-upcase i))))
Using it:
> (convert "ab1c23")
"ABC"
Here's one possible solution:
(define (convert input)
(list->string
(foldr (lambda (chr acc)
(if (char-alphabetic? chr)
(cons (char-upcase chr) acc)
acc))
'()
(string->list input))))
We need to accumulate the result somewhere, instead of printing char by char. For that, we use foldr to process a list of chars, uppercasing alphabetic chars and ignoring the others. This produces a list of chars that we convert back to a string using list->string. It works as expected:
(convert "ab1c23")
=> "ABC"
Related
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
I have a list of chars and integers, and I'm trying to convert them to a string.
> (define l (cons #\a (cons #\b (cons 3 null))))
I want to convert this list to the string "ab3".
Using list->string doesn't work:
> (list->string l)
list->string: contract violation
expected: (listof char?)
given: (list #\a #\b 3)
When I try that combined with integer->char, it gives this numeric value:
> (define l (cons #\a (cons #\b (cons (integer->char 3) null))))
> (list->string l)
"ab\u0003"
Using number->string doesn't work either:
> (define l (cons #\a (cons #\b (cons (number->string 3) null))))
> (list->string l)
list->string: contract violation
expected: (listof char?)
given: '(#\a #\b "3")
context...:
C:\Program Files\Racket\collects\racket\private\misc.rkt:87:7
list->string requires a list of chars, it does not accept strings.
Another try, first converting the string to a list:
> (define l (cons #\a (cons #\b (cons (string->list (number->string 123)) null))))
> (list->string l)
list->string: contract violation
expected: (listof char?)
given: '(#\a #\b (#\3))
context...:
C:\Program Files\Racket\collects\racket\private\misc.rkt:87:7
It does not accept a sub list either. How can I convert this to the string "ab3"?
You want to process a list of characters and integers, and concatenate all of them together in a single string. Try this:
(define (process lst)
(apply string-append ; append all the strings
(map (lambda (e) ; create a list of strings
(if (char? e) ; if it's a char
(string e) ; convert it to string
(number->string e))) ; same if it's a number
lst)))
For example:
(process (list #\a #\b 123 #\c))
=> "ab123c"
A char is a type that has a value. This value is defined in unicode so that 65 is a upper case A and a 66 is an upper case B. All characters has an integer value and char->integer converts from characters to the numeric unicode value and integer->char convert from unicode value to a character.
The numeric characters start at 48 (#x30) which is the zero to 57 (#x39) which is a nine. Thus (list->string (list #\a #\b (integer->number #x33))) ; ==> "ab3"
A numeric value can be converted to a string with number->string. eg. (number->string 123) => "123". This is displayed in base 10 but if you perhaps want it displayed in hex you can (number->string 123 16) ;==> "7b". Note that list->string only takes a list of chars and cannot have other elements like numbers in them.
You may join many strings together with string-append:
(string-append (list->string '(#\a #\b))
(number->string #x7b)
"c")
; ==> "ab123c"
Racket has a handy function called ~a that converts its arguments to strings as if by using display, and concatenates them all together (Plus a bunch of optional keyword arguments to control formatting, but they're not needed here). Combine it with apply to treat the elements of a list as its arguments.
> (apply ~a '(#\a #\b 3))
"ab3"
(If you're using #lang racket/base, you'll have to (require racket/format)).
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))
I'd like to turn String into lists. For example, http => (h t t p).
I try:
(defun string-to-list (s)
(assert (stringp s) (s) "~s :questa non e una stringa")
(coerce s 'list))
but if I do
(string-to-list "http")
results:
(#\h #\t #\t #\p).
Can I remove #\ ?
thanks in advance :)
Why would you do that? What you ask is to split a string (a one-dimensional array of characters) into a list of symbols. Do you really want that?
#\h is a character object printed.
You can print them differently:
CL-USER 8 > (princ #\h)
h
CL-USER 9 > (prin1 #\h)
#\h
Let's print the list using PRINC:
CL-USER 10 > (map nil #'princ (coerce "Hello!" 'list))
Hello!
Btw., since strings, vectors and lists are sequences, you can MAP directly over the string...
CL-USER 11 > (map nil #'princ "Hello!")
Hello!
You can turn a string into a symbol with intern. You can turn a character into a string with string. Interning a lower-case string might cause it to be printed as |h| instead of h, so you'll want to string-upcase it. Putting all that together gives:
(loop for c in (coerce "http" 'list)
collecting (intern (string-upcase (string c))))
Expanding upon larsmans' answer, you can print lowercase symbols unquoted if you change the readtable:
(let ((*readtable* (copy-readtable)))
(setf (readtable-case *readtable*) :preserve)
(prin1 (loop for c in (coerce "http" 'list)
collecting (intern (string c)))))
This will print (h t t p) and return (|h| |t| |t| |p|).
You can print characters unescaped. See the variable *PRINT-ESCAPE*.
The function WRITE has a keyword parameter :ESCAPE for that:
(defun string-to-list (s)
(assert (stringp s) (s) "~s :questa non e una stringa")
(write (coerce s 'list) :escape nil)
CL-USER 11 > (string-to-list "abcd")
(a b c d)
(#\a #\b #\c #\d)
In above example the first form is printed by calling WRITE and the second form is the return value printed by the REPL.
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"