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.
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'm writing a grammar which I intend to implement in a Lisp read procedure, i.e. reading one expression at a time from an input source which is i.e. mutable. Most of the grammar is just like Lisp, but the two pertinent changes are:
Whitespace is read and is part of the resulting syntax. Contiguous whitespace is grouped together like contiguous non-whitespace characters are grouped as identifiers, and the result of reading such a string is a "whitespace object", which stores the exact sequence of characters read. The evaluator ignores whitespace objects when they appear in a list (in other words, if foo is a whitespace object then (eval '(+ 3 foo 4)) is equivalent to (eval '(+ 3 4))), and if it is asked to evaluate one directly, it is self-evaluating.
Secondly, if several tokens other than whitespace tokens appear on the same line, those tokens are collected into a list and that list is the result of the read.
e.g.,
+ 3 4 5
(+ 3 4 5)
+ 3 4 (+ 1 4)
(+ 3 4 (+ 1 4))
all produce the value 12.
Is it possible to implement this reader as a Lisp read procedure that follows the typical expectations of a read procedure? If so, how? (I'm at a loss.)
Edit: Clarification on whitespace:
If we say that a "whitespace object" is simply a string and read, then reading the following segment:
(foo bar baz)
produces a syntax object like:
'(foo " " bar " " baz)
In other words, the whitespace between tokens is stored in the resultant syntax object.
Suppose I write a macro named ->, which takes a syntax object (scheme style macro), and whitespace? is a predicate identifying whitespace syntax objects
(define-macro (-> stx)
(let* ((stxl (syntax-object->list stx))
(obj (car stxl))
(let proc ((res empty))
(lst (cdr stxl)))
(let ((method (car lst)))
(if (whitespace? method)
; skip whitespace, recur immediately
(proc res (cdr lst))
; Insert obj as the second element in method
(let ((modified-method (cons (car method)
(cons obj (cdr method)))))
; recur
(proc (cons res modified-method) (cdr lst))))))))
The reading part of this is pretty easy. You just need a whitespace test, and then your reading function will install a custom reader character macro that detects whitespace and reads consecutive sequences of whitespace into a single object. First, the whitespace test and a whitespace object; these are pretty simple:
(defparameter *whitespace*
#(#\space #\tab #\return #\newline)
"A vector of whitespace characters.")
(defun whitespace-p (char)
"Returns true if CHAR is in *WHITESPACE*."
(find char *whitespace* :test 'char=))
(defstruct whitespace-object
characters)
Now the macro character function:
(defun whitespace-macro-char (stream char)
"A macro character function that consumes characters from
stream (including CHAR), until a non-whitespace character (or end of
file) is encountered. Returns a whitespace-object whose characters
slot contains a string of the whitespace characters."
(let ((chars (loop for c = (peek-char nil stream nil #\a)
while (whitespace-p c)
collect (read-char stream))))
(make-whitespace-object
:characters (coerce (list* char chars) 'string))))
Now the read function just has the same signature as the normal read, but copies the readtable, then installs the macro function, and calls read. The result from read is returned, and the readtable is restored:
(defun xread (&optional (stream *standard-input*) (eof-error-p t) eof-value recursive-p)
"Like READ, but called with *READTABLE* bound to a readtable in
which each whitespace characters (that is, each character in
*WHITESPACE*) is a macro characters whose macro function is
WHITESPACE-MACRO-CHAR."
(let ((rt (copy-readtable)))
(map nil (lambda (wchar)
(set-macro-character wchar #'whitespace-macro-char))
*whitespace*)
(unwind-protect (read stream eof-error-p eof-value recursive-p)
(setf *readtable* rt))))
Example:
(with-input-from-string (in "(+ 1 2 (* 3
4))")
(xread in))
(+ #S(WHITESPACE-OBJECT :CHARACTERS " ") 1
#S(WHITESPACE-OBJECT :CHARACTERS " ") 2
#S(WHITESPACE-OBJECT :CHARACTERS " ")
(* #S(WHITESPACE-OBJECT :CHARACTERS " ") 3
#S(WHITESPACE-OBJECT
:CHARACTERS "
")
4))
Now, to implement the eval counterpart that you want, you need to be able to remove whitespace objects from lists. This isn't too hard, and we can write a slightly more general utility function to do it for us:
(defun remove-element-if (predicate tree)
"Returns a new tree like TREE, but which contains no elements in an
element position which ssatisfy PREDICATE. An element is in element
position if it is the car of some cons cell in TREE."
(if (not (consp tree))
tree
(if (funcall predicate (car tree))
(remove-element-if predicate (cdr tree))
(cons (remove-element-if predicate (car tree))
(remove-element-if predicate (cdr tree))))))
CL-USER> (remove-element-if (lambda (x) (and (numberp x) (evenp x))) '(+ 1 2 3 4))
(+ 1 3)
CL-USER> (with-input-from-string (in "(+ 1 2 (* 3
4))")
(remove-element-if 'whitespace-object-p (xread in)))
(+ 1 2 (* 3 4))
So now the evaluation function is a simple wrapper around eval:
(defun xeval (form)
(eval (remove-element-if 'whitespace-object-p form)))
CL-USER> (with-input-from-string (in "(+ 1 2 (* 3
4))")
(xeval (xread in)))
15
Let's make sure that standalone whitespace objects still appear as expected:
CL-USER> (with-input-from-string (in " ")
(let* ((exp (xread in))
(val (xeval exp)))
(values exp val)))
#S(WHITESPACE-OBJECT :CHARACTERS " ")
#S(WHITESPACE-OBJECT :CHARACTERS " ")
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 am attempting to write a program whereby I need to process a string character-by-character, including whitespace. This string would be given by the user running the program, from the standard input stream.
I have noticed that the (read) function stops only after some non-whitespace characters have been captured, and cuts off everything starting with the first whitespace character.
In other words if I do a (read) function and enter hi to you, it will return HI. What I would like my function to return is (#\h #\i #\Space #\t #\o #\Space #\y #\o #\u). Can anyone give me some pointers as to how to accomplish this? Sorry guys, I am VERY new to LISP but I like it a lot.
EDIT: One thing I've tried is using (cons) with (read-char). For example: (cons (read-char) (cons (read-char) (cons (read-char) (cons (read-char) (read-char))))). When the user enters "hello", this outputs (#\h #\e #\l #\l . #\o), which is close, but what is that extra period doing in there?
Xach basically answered your main question: you'd probably want to read a line with read-line and coerce it to a list: (coerce (read-line) 'list) and then do something with this list.
The reason that read doesn't work is that it reads and returns s-expressions:
> (car (read))
(foo bar baz) ; this is me typing
FOO
So it just reads the first symbol hi in the input "hi to you" and stops. Successive calls to read will return the rest:
> (list (read) (read) (read))
hi to you ; me typing again
(HI TO YOU)
You probably still want to use Xach's suggestion to get your input though.
To address your other question, the reason there's a dot in that list of characters is that the expression (cons (read-char) (cons (read-char) (cons (read-char) (cons (read-char) (read-char))))) ends with (cons (read-char) (read-char)) which conses two characters together.
Cons creates "cons cells" or Lisp pairs, which are represented as a dotted pair:
> (cons 'a 'b)
(A . B)
Lisp lists are usually formed with nil or the empty list as the last element. But they are actually represented as linked lists with these cons cells, these are the same:
(a . (b . (c . ())))
(a b c)
They are just usually printed the second way. In your case, the last element is a character, so it's printed as a dotted tail list. Experiment with it at the REPL and it will make sense:
> '(a . (b . c))
(A B . C)
> '(a b . c)
(A B . C)
> '(a b c)
(A B C)
> '(a . (b . (c . nil)))
(A B C)
So what you would want to do with that original expression is this:
> (cons (read-char) (cons (read-char) (cons (read-char) (cons (read-char) (cons (read-char) nil)))))
(#\h #\e #\l #\l #\o)
read-char will return the next available character, and read-line will return an entire line of input as a string. You can use coerce to change a string to a list of characters, e.g.
(coerce "foo" 'list) => (#\f #\o #\o)