return a line of text if match found - lisp

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))

Related

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))

Can a Lisp read procedure read this and how?

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 " ")

Convert char to number

I'm in the process of reading a flat file - to use the characters read I want to convert them into numbers. I wrote a little function that converts a string to a vector:
(defun string-to-vec (strng)
(setf strng (remove #\Space strng))
(let ((vec (make-array (length strng))))
(dotimes (i (length strng) vec)
(setf (svref vec i) (char strng i)))))
However this returns a vector with character entries. Short of using char-code to convert unit number chars to numbers in a function, is there a simple way to read numbers as numbers from a file?
In addition to Rainer's answer, let me mention read-from-string (note that Rainer's code is more efficient than repeated application of read-from-string because it only creates a stream once) and parse-integer (alas, there is no parse-float).
Note that if you are reading a CSV file, you should probably use an off-the-shelf library instead of writing your own.
Above is shorter:
? (map 'vector #'identity (remove #\Space "123"))
#(#\1 #\2 #\3)
You can convert a string:
(defun string-to-vector-of-numbers (string)
(coerce
(with-input-from-string (s string)
(loop with end = '#:end
for n = (read s nil end)
until (eql n end)
unless (numberp n) do (error "Input ~a is not a number." n)
collect n))
'vector))
But it would be easier to read the numbers directly form the file. Use READ, which can read numbers.
Note that read-like functions are affected by reader macros.
Pick an example:
* (defvar *foo* 'bar)
*FOO*
* (read-from-string "#.(setq *foo* 'baz)")
BAZ
19
* *foo*
BAZ
As you can see read-from-string can implicitly set a variable. You can disable the #. reader macro by setting *read-eval* to nil but anyway if you have only integers on the input then consider using parse-integer instead.

Lisp Formatting Polynomial

I am representing sparse polynomials as lists of (coefficient, pairs). For example:
'((1 2) (3 6) (-20 48)) => x^2 + 3x^6 - 20x^48
I am new to Lisp formatting, but have come across some pretty nifty tools, such as (format nil "~:[+~;-~]" (> 0 coefficient)) to get the sign of the coefficient as text (I know, that's probably not idiomatic).
However, there are certain display problems when formatting single terms. For example, the following should all be true:
(1 0) => 1x^0 => 1 (reducible)
(1 1) => 1x^1 => x (reducible)
(1 2) => 1x^2 => x^2 (reducible)
(2 0) => 2x^0 => 2 (reducible)
(2 1) => 2x^1 => 2x (reducable)
(2 2) => 2x^2 => 2x^2 (this one is okay)
I'm wondering if there's a way to do this without a large series of if or cond macros - a way just to do this with a single format pattern. Everything works but the 'prettifying' the terms (the last line in FormatPolynomialHelper3 should do this).
(defun FormatPolynomial (p)
"Readably formats the polynomial p."
; The result of FormatPolynomialHelper1 is a list of the form (sign formatted),
; where 'sign' is the sign of the first term and 'formatted' is the rest of the
; formatted polynomial. We make this a special case so that we can print a sign
; attached to the first term if it is negative, and leave it out otherwise. So,
; we format the first term to be either '-7x^20' or '7x^20', rather than having
; the minus or plus sign separated by a space.
(destructuring-bind (sign formatted-poly) (FormatPolynomialHelper1 p)
(cond
((string= formatted-poly "") (format nil "0"))
(t (format nil "~:[~;-~]~a" (string= sign "-") formatted-poly)))))
; Helpers
(defun FormatPolynomialHelper1 (p)
(reduce #'FormatPolynomialHelper2 (mapcar #'FormatPolynomialHelper3 p) :initial-value '("" "")))
(defun FormatPolynomialHelper2 (t1 t2)
; Reduces ((sign-a term-a) (sign-b term-b)) => (sign-b "term-b sign-a term-a"). As
; noted, this accumulates the formatted term in the variable t2, beginning with an
; initial value of "", and stores the sign of the leading term in the variable t1.
; The sign of the leading term is placed directly before the accumulated formatted
; term, ensuring that the signs are placed correctly before their coefficient. The
; sign of the the leading term of the polynomial (the last term that is processed)
; is available to the caller for special-case formatting.
(list
(first t2)
(format nil "~#{~a ~}" (second t2) (first t1) (second t1))))
(defun FormatPolynomialHelper3 (tm)
; Properly formats a term in the form "ax^b", excluding parts of the form if they
; evaluate to one. For example, 1x^3 => x^3, 2x^1 => 2x, and 3x^0 => 3). The list
; is in the form (sign formatted), denoting the sign of the term, and the form of
; the term state above (the coefficient have forced absolute value).
(list
(format nil "~:[+~;-~]" (> 0 (first tm)))
(format nil "~a~#[x^~a~]" (abs (first tm)) (second tm))))
EDIT: It's correctly been stated that output should not contain logic. Perhaps I was asking too specific of a question for my problem. Here is the logic that correctly formats a polynomial - but I'm looking for something cleaner, more readable, and more lisp-idiomatic (this is only my third day writing lisp).
(defun FormatPolynomialHelper3 (tm)
; Properly formats a term in the form "ax^b", excluding parts of the form if they
; evaluate to one. For example, 1x^3 => x^3, 2x^1 => 2x, and 3x^0 => 3). The list
; is in the form (sign formatted), denoting the sign of the term, and the form of
; the term state above (the coefficient have forced absolute value).
(list
(format nil "~:[+~;-~]" (> 0 (first tm)))
(cond
((= 0 (second tm)) (format nil "~a" (abs (first tm))))
((= 1 (abs (first tm))) (cond
((= 1 (second tm)) (format nil "x"))
(t (format nil "x^~a" (second tm)))))
((= 1 (second tm)) (format nil "~ax" (abs (first tm))))
(t (format nil "~ax^~a" (abs (first tm)) (second tm))))))
Answer:
I would not put this logic into FORMAT statements. Only if you want to encrypt your code or create more maintenance work for yourself. Good Lisp code is self-documenting. FORMAT statements are never self-documenting.
Before printing I would first simplify the polynomial. For example removing every term which is multiplied by zero.
((0 10) (1 2)) -> ((1 2))
Then if the multiplier is 1 can be tested in a normal COND or CASE statement.
Also make sure that you never use CAR, CDR, FIRST, SECOND with a self-made data structure. The components of a polynomial should mostly be accessed by self-documenting functions hiding most of the implementation details.
I would write it without FORMAT:
Example code:
(defun term-m (term)
(first term))
(defun term-e (term)
(second term))
(defun simplify-polynomial (p)
(remove-if #'zerop (sort p #'> :key #'term-e)
:key #'term-m))
(defun write-term (m e start-p stream)
; sign or operator
(cond ((and (minusp m) start-p)
(princ "-" stream))
((not start-p)
(princ (if (plusp m) " + " " - ") stream)))
; m
(cond ((not (= (abs m) 1))
(princ (abs m) stream)))
(princ "x" stream)
; e
(cond ((not (= 1 e))
(princ "^" stream)
(princ e stream))))
(defun write-polynomial (p &optional (stream *standard-output*))
(loop for (m e) in (simplify-polynomial p)
for start-p = t then nil
do (write-term m e start-p stream)))
Example use:
CL-USER 14 > (write-polynomial '((1 2) (3 6) (-20 48)))
-20x^48 + 3x^6 + x^2

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"