Common Lisp: read each input character as a list element - lisp

Lisp newbie here.
I want to read from standard-in a string of characters such as:
aabc
I want to convert that input into a list, where each character becomes a list element:
(a a b c)
And I want the list assigned to a global variable, text.
I created this function:
(defun get-line ()
(setf text (read)))
but that just results in assigning a single symbol to text, not tokenizing the input into a list of symbols.
What's the right way to implement get-line() please?

Here you go: First using coerce to convert the string to a list of characters, then mapcar to convert each character to a string.
(defun get-line ()
(setf text (mapcar 'string (coerce (string (read)) 'list))))

(loop
for x = (string-upcase (string (read-char)))
while (not (equal " " x))
collecting (intern x))
Note the upcase is there because symbols in CL are not case sensitive and are upcased by default by the reader.

Related

How do I count the number of specific characters in a string in Emacs Lisp?

Let's say I have a string s.
And this string s could contain this:
asdf-asdfasdfasf-fasdf-asdfasdfasdf
or this:
asf-asdfaf
but also this:
aasdaf
How do I count the number of dashes (-) in this string using Emacs Lisp and store this number in some variable e.g. count-of-dashes?
The following function should do it:
(defun count-chars (char str)
(let ((s (char-to-string char))
(count 0)
(start-pos -1))
(while (setq start-pos (string-search s str (+ 1 start-pos)))
(setq count (+ 1 count)))
count))
You call it like this:
(count-chars ?- "---") ==> 3
(count-chars ?- "foo-bar") ==> 1
(count-chars ?- "-foo-bar-baz") ==> 3
(count-chars ?- "foobarbaz") ==> 0
To set a variable to the number found, you just use
setq:
(setq count-of-chars (count-chars ?- "foo-bar-baz"))
Basically, we loop looking for the first dash: if we find it we remember where so that we start looking at the place just to the right of it the next time around the loop. The loop body then just counts every one we see. When we can't find any more, string-search (and the setq) returns nil and the loop exits, whereupon we return the accumulated count. See the doc string of the function string-search with C-h f string-search for the details.
Another method is more akin to the split string method of python: split-string splits a string on a separator into a list of parts. We then count the parts (the length of the list) and subtract 1: "a-b-c" is split into ("a" "b" "c") so there are three parts but only two separators.
(defun count-chars (char str)
(let ((s (char-to-string char)))
(- (length (split-string str s)) 1)))
Again, see the doc string of split-string (C-h f split-string) for all the details.
In both cases, we converted the character argument to a string argument, because both string-search in the first case and split-string in the second expect a string argument (to search for in the first case and to use as a separator in the second case - in fact, split-string can use a regular expression as a separator). Characters and strings are different data types in Emacs Lisp, so the conversion is necessary if you really want a character s the first argument of count-chars. But you could make it a string instead:
(defun count-seps (sep str)
(- (length (split-string str sep)) 1))
and then you would call it like this instead:
(count-seps "-" "abc-def-ghi-")
which is simpler and more general:
(count-seps "-;-" "abc-;-def") ==> 1
but you do have to worry about special characters in the separator string:
(count-seps "-*-" "abcd-------def") ==> 1
since the regular expression -*- matches one or more dashes so it matches all seven dashes: there is only one separator. Whether that's what you want is debatable. If you don't want it, you'd need to escape the special characters in the separator string:
(defun count-chars (sep str)
(let ((qsep (regexp-quote sep)))
(- (length (split-string str qsep)) 1)))

Function returns list but prints out NIL in LISP

I'm reading a file char by char and constructing a list which is consist of list of letters of words. I did that but when it comes to testing it prints out NIL. Also outside of test function when i print out list, it prints nicely. What is the problem here? Is there any other meaning of LET keyword?
This is my read fucntion:
(defun read-and-parse (filename)
(with-open-file (s filename)
(let (words)
(let (letter)
(loop for c = (read-char s nil)
while c
do(when (char/= c #\Space)
(if (char/= c #\Newline) (push c letter)))
do(when (or (char= c #\Space) (char= c #\Newline) )
(push (reverse letter) words)
(setf letter '())))
(reverse words)
))))
This is test function:
(defun test_on_test_data ()
(let (doc (read-and-parse "document2.txt"))
(print doc)
))
This is input text:
hello
this is a test
You're not using let properly. The syntax is:
(let ((var1 val1)
(var2 val2)
...)
body)
If the initial value of the variable is NIL, you can abbreviate (varN nil) as just varN.
You wrote:
(let (doc
(read-and-parse "document2.txt"))
(print doc))
Based on the above, this is using the abbreviation, and it's equivalent to:
(let ((doc nil)
(read-and-parse "document2.txt"))
(print doc))
Now you can see that this binds doc to NIL, and binds the variable read-and-parse to "document2.txt". It never calls the function. The correct syntax is:
(let ((doc (read-and-parse "document2.txt")))
(print doc))
Barmar's answer is the right one. For interest, here is a version of read-and-parse which makes possibly-more-idiomatic use of loop, and also abstracts out the 'is the character white' decision since this is something which is really not usefully possible in portable CL as the standard character repertoire is absurdly poor (there's no tab for instance!). I'm sure there is some library available via Quicklisp which deals with this better than the below.
I think this is fairly readable: there's an outer loop which collects words, and an inner loop which collects characters into a word, skipping over whitespace until it finds the next word. Both use loop's collect feature to collect lists forwards. On the other hand, I feel kind of bad every time I use loop (I know there are alternatives).
By default this collects the words as lists of characters: if you tell it to it will collect them as strings.
(defun char-white-p (c)
;; Is a character white? The fallback for this is horrid, since
;; tab &c are not a standard characters. There must be a portability
;; library with a function which does this.
#+LispWorks (lw:whitespace-char-p c)
#+CCL (ccl:whitespacep c) ;?
#-(or LispWorks CCL)
(member char (load-time-value
(mapcan (lambda (n)
(let ((c (name-char n)))
(and c (list c))))
'("Space" "Newline" "Page" "Tab" "Return" "Linefeed"
;; and I am not sure about the following, but, well
"Backspace" "Rubout")))))
(defun read-and-parse (filename &key (as-strings nil))
"Parse a file into a list of words, splitting on whitespace.
By default the words are returned as lists of characters. If
AS-STRINGS is T then they are coerced to strings"
(with-open-file (s filename)
(loop for maybe-word = (loop with collecting = nil
for c = (read-char s nil)
;; carry on until we hit EOF, or we
;; hit whitespace while collecting a
;; word
until (or (not c) ;EOF
(and collecting (char-white-p c)))
;; if we're not collecting and we see
;; a non-white character, then we're
;; now collecting
when (and (not collecting) (not (char-white-p c)))
do (setf collecting t)
when collecting
collect c)
while (not (null maybe-word))
collect (if as-strings
(coerce maybe-word 'string)
maybe-word))))

Lisp string formatting with named parameters

Is there a way in Lisp to format a string using named parameters?
Perhaps something with association lists like
(format t "All for ~(who)a and ~(who)a for all!~%" ((who . "one")))
in order to print "All for one and one for all".
Similar to this python question, or this scala one, or even c++, but in Lisp.
If this functionality isn't in the language, does anyone have any cool functions or macros that could accomplish the same thing?
Use CL-INTERPOL.
(cl-interpol:enable-interpol-syntax)
String interpolation
For simple cases, you don't need FORMAT:
(lambda (who) #?"All for $(who) and $(who) for all!")
Then:
(funcall * "one")
=> "All for one and one for all!"
Interpret format directives
If you need to format, you can do:
(setf cl-interpol:*interpolate-format-directives* t)
For example, this expression:
(let ((who "one"))
(princ #?"All for ~A(who) and ~S(who) for all!~%"))
... prints:
All for one and "one" for all!
If you are curious, the above reads as:
(LET ((WHO "one"))
(PRINC
(WITH-OUTPUT-TO-STRING (#:G1177)
(WRITE-STRING "All for " #:G1177)
(FORMAT #:G1177 "~A" (PROGN WHO))
(WRITE-STRING " and " #:G1177)
(FORMAT #:G1177 "~S" (PROGN WHO))
(WRITE-STRING " for all!" #:G1177))))
Alternate reader function
Previously, I globally set *interpolate-format-directives*, which interprets format directive in all interpolated strings.
If you want to control precisely when format directives are interpolated, you can't just bind the variable temporarily in your code, because the magic happens at read-time. Instead, you have to use a custom reader function.
(set-dispatch-macro-character
#\#
#\F
(lambda (&rest args)
(let ((cl-interpol:*interpolate-format-directives* t))
(apply #'cl-interpol:interpol-reader args))))
If I reset the special variable to its default value NIL, then strings where directives are formatted are prefixed with #F, whereas normal interpolated ones use the #? syntax. If you want to change readtables, have a look at named readtables.

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.

Treating the values from a list of slots and strings

I want to do a macro in common lisp which is supposed to take in one of its arguments a list made of slots and strings. Here is the prototype :
(defclass time-info ()
((name :initarg name)
(calls :initarg calls)
(second :initarg second)
(consing :initarg consing)
(gc-run-time :initarg gc-run-time)))
(defun print-table (output arg-list time-info-list) ())
The idea is to print a table based on the arg-list which defines its structure. Here is an example of a call to the function:
(print-table *trace-output*
'("|" name "||" calls "|" second "\")
my-time-info-list)
This print a table in ascII on the trace output. The problem, is that I don't know how to explicitely get the elements of the list to use them in the different parts of my macro.
I have no idea how to do this yet, but I'm sure it can be done. Maybe you can help me :)
I would base this on format. The idea is to build a format string
from your arg-list.
I define a helper function for that:
(defun make-format-string-and-args (arg-list)
(let ((symbols ()))
(values (apply #'concatenate 'string
(mapcar (lambda (arg)
(ctypecase arg
(string
(cl-ppcre:regex-replace-all "~" arg "~~"))
(symbol
(push arg symbols)
"~a")))
arg-list))
(nreverse symbols))))
Note that ~ must be doubled in format strings in order to escape them.
The printing macro itself then just produces a mapcar of format:
(defmacro print-table (stream arg-list time-info-list)
(let ((time-info (gensym)))
(multiple-value-bind (format-string arguments)
(make-format-string-and-args arg-list)
`(mapcar (lambda (,time-info)
(format ,stream ,format-string
,#(mapcar (lambda (arg)
(list arg time-info))
arguments)))
,time-info-list)))
You can then call it like this:
(print-table *trace-output*
("|" name "||" calls "|" second "\\")
my-time-info-list)
Please note the following errors in your code:
You need to escape \ in strings.
Second is already a function name exported from the common-lisp
package. You should not clobber that with a generic function.
You need to be more precise with your requirements. Macros and Functions are different things. Arrays and Lists are also different.
We need to iterate over the TIME-INFO-LIST. So that's the first DOLIST.
The table has a description for a line. Each item in the description is either a slot-name or a string. So we iterate over the description. That's the second DOLIST. A string is just printed. A symbol is a slot-name, where we retrieve the slot-value from the current time-info instance.
(defun print-table (stream line-format-description time-info-list)
(dolist (time-info time-info-list)
(terpri stream)
(dolist (slot-or-string line-format-description)
(princ (etypecase slot-or-string
(string slot-or-string)
(symbol (slot-value time-info slot-or-string)))
stream))))
Test:
> (print-table *standard-output*
'("|" name "||" calls "|" second "\\")
(list (make-instance 'time-info
:name "foo"
:calls 100
:second 10)
(make-instance 'time-info
:name "bar"
:calls 20
:second 20)))
|foo||100|10\
|bar||20|20\
First, you probably don't want the quote there, if you're using a macro (you do want it there if you're using a function, however). Second, do you want any padding between your separators and your values? Third, you're probably better off with a function, rather than a macro.
You also seem to be using "array" and "list" interchangeably. They're quite different things in Common Lisp. There are operations that work on generic sequences, but typically you would use one way of iterating over a list and another to iterate over an array.