Select random char from string in Common Lisp - string

Im learning Common Lisp and writing a simple password generator as an intro project.
Here is my code:
(setq chars
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
(print (nth (random (length chars)) chars))
But using CLISP I just get
*** - NTH: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" is not a list
I thought every string in Lisp was a list? How can I "cast" the string to a list?

NTH works only for lists. Strings are not lists, but vectors of characters.
Here is the dictionary for strings. CHAR is an accessor for strings.
CL-USER 7 > (char "abc" 1)
#\b
Since strings are also sequences, all sequence operations apply. See: Sequence dictionary.
CL-USER 8 > (elt "abc" 1)
#\b

Lisp is an interactive system. Learn to have conversations with the REPL:
CL-USER> (type-of "abc")
(SIMPLE-ARRAY CHARACTER (3))
You should get a similar result from CLISP.
Can you take it from here?

A string is not a list. Not everything is a list in Lisp ;)
You can use coerce to create a list of the characters (coerce "some string" 'list).

Related

How to distinguish escaped characters from non-escaped e.g. "\x27" from "x27" in a string in Common Lisp?

Solving Advent of Code 2015 task 8 part2 I encountered the problem to have to distinguish in a string the occurrence of "\x27" from plain "x27".
But I don't see a way how I can do it. Because
(length "\x27") ;; is 3
(length "x27") ;; is also 3
(subseq "\x27" 0 1) ;; is "x"
(subseq "x27" 0 1) ;; is "x"
Neither print, prin1, princ made a difference.
# nor does `coerce`
(coerce "\x27" 'list)
;; (#\x #\2 #\7)
So how then to distinguish in a string when "\x27" or any of such
hexadecimal representation occurs?
It turned out, one doesn't need to solve this to solve the task. However, now I still would like to know whether there is a way to distinguish "\x" from "x" in common lisp.
The string literal "\x27" is read as the same as "x27", because \ is an escape character in string literals. If you want a string with the contents \x27, you need to write the literal as "\\x27" (i. e. escape the escape character). This has nothing to do with the strings themselves. If you read a string from a file containing \x27 (e. g. with read-line), then the four-character string \x27 results.
By the time that the Lisp reader gets to work, \x is the same as x. There may be some way to turn this off - I wouldn't be surprised - but the original text talks about Santa's file.
So, I created my own file, like this:
x27
\x27
And I read the data into special variables like this:
(defun read-line-crlf (stream)
(string-right-trim '(#\Return) (read-line stream nil)))
(defun read-lines (filename)
(with-open-file (stream filename)
(setf x (read-line-crlf stream))
(setf x-esc (read-line-crlf stream))
))
The length of x is then 3, and the length of x-esc is 4. The returned string must be trimmed on Windows, or an external format declared, because otherwise SBCL will leave half of the CR-LF on the end of the read strings.

Clojure: Idiomatic Way to Insert a Char in a String

I have a string in Clojure and a character I want to put in between the nth and (n+1)st character. For example: Lets say the string is "aple" and I want to insert another "p" between the "p" and the "l".
(prn
(some-function "aple" "p" 1 2))
;; prints "apple"
;; ie "aple" -> "ap" "p" "le" and the concatenated back together.
I'm finding this somewhat challenging, so I figure I am missing information about some useful function(s) Can someone please help me write the "some-function" above that takes a string, another string, a start position and an end position and inserts the second string into the first between the start position and the end position? Thanks in advance!
More efficient than using seq functions:
(defn str-insert
"Insert c in string s at index i."
[s c i]
(str (subs s 0 i) c (subs s i)))
From the REPL:
user=> (str-insert "aple" "p" 1)
"apple"
NB. This function doesn't actually care about the type of c, or its length in the case of a string; (str-insert "aple" \p 1) and (str-insert "ale" "pp" 1) work also (in general, (str c) will be used, which is the empty string if c is nil and (.toString c) otherwise).
Since the question asks for an idiomatic way to perform the task at hand, I will also note that I find it preferable (in terms of "semantic fit" in addition to the performance advantage) to use string-oriented functions when dealing with strings specifically; this includes subs and functions from clojure.string. See the design notes at the top of the source of clojure.string for a discussion of idiomatic string handling.

Lisp - Displaying a String to List

I've been looking for a way to convert user input (read-line) to a list of atoms that I can manipulate more easily.
For example:
SendInput()
This is my input. Hopefully this works.
and I want to get back..
(This is my input. Hopefully this works.)
Eventually it'd be ideal to remove any periods, commas, quotes, etc. But for now I just wanna store the users input in a list (NOT AS A STRING)
So. For now i'm using
(setf stuff (coerce (read-line) 'list))
and that returns to me as...
(#\T #\h #\i #\s #\Space #\i #\s #\Space #\m #\y #\Space #\i #\n #\p #\u #\t #. #\Space #\H #\o #\p #\e #\f #\u #\l #\l #\y #\Space #\t #\h #\i #\s #\Space #\w #\o #\r #\k #\s #.)
So now i'm on the hunt for a function that can take that list and format it properly...
Any help would be greatly appreciated!
Rainer's answer is better in that it's a bit more lightweight (and general), but you could also use CL-PPCRE , if you already have it loaded (I know I always do).
You can use SPLIT directly on the string you get from READ-LINE, like so:
(cl-ppcre:split "[ .]+" (read-line))
(Now you have two problems)
What you want to do is to split a sequence of characters (a String) into a list of smaller strings or symbols.
Use some of the split sequence functions available from a Lisp library (see for example cl-utilities).
In LispWorks, which comes with a SPLIT-SEQUENCE function) I would for example write:
CL-USER 8 > (mapcar #'intern
(split-sequence '(#\space #\.)
"This is my input. Hopefully this works."
:coalesce-separators t))
(|This| |is| |my| |input| |Hopefully| |this| |works|)
Remember, to get symbols with case preserving names, they are surrounded by vertical bars. The vertical bars are not part of the symbol name - just like the double quotes are not part of a string - they are delimiters.
You can also print it:
CL-USER 19 > (princ (mapcar #'intern
(split-sequence '(#\space #\.)
"This is my input. Hopefully this works."
:coalesce-separators t)))
(This is my input Hopefully this works)
(|This| |is| |my| |input| |Hopefully| |this| |works|)
Above prints the list. The first output is the data printed by PRINC and the second output is done by the REPL.
If you don't want symbols, but strings:
CL-USER 9 > (split-sequence '(#\space #\.)
"This is my input. Hopefully this works."
:coalesce-separators t)
("This" "is" "my" "input" "Hopefully" "this" "works")

Make String from Sequence of Characters

This code does not work as I expected. Could you please explain why?
(defn make-str [s c]
(let [my-str (ref s)]
(dosync (alter my-str str c))))
(defn make-str-from-chars
"make a string from a sequence of characters"
([chars] make-str-from-chars chars "")
([chars result]
(if (== (count chars) 0) result
(recur (drop 1 chars) (make-str result (take 1 chars))))))
Thank you!
This is very slow & incorrect way to create string from seq of characters. The main problem, that changes aren't propagated - ref creates new reference to existing string, but after it exits from function, reference is destroyed.
The correct way to do this is:
(apply str seq)
for example,
user=> (apply str [\1 \2 \3 \4])
"1234"
If you want to make it more effective, then you can use Java's StringBuilder to collect all data in string. (Strings in Java are also immutable)
You pass a sequence with one character in it to your make-str function, not the character itself. Using first instead of take should give you the desired effect.
Also there is no need to use references. In effect your use of them is a gross misuse of them. You already use an accumulator in your function, so you can use str directly.
(defn make-str-from-chars
"make a string from a sequence of characters"
([chars] (make-str-from-chars chars ""))
([chars result]
(if (zero? (count chars))
result
(recur (drop 1 chars) (str result (first chars))))))
Of course count is not very nice in this case, because it always has to walk the whole sequence to figure out its length. So you traverse the input sequence several times unnecessarily. One normally uses seq to identify when a sequence is exhausted. We can also use next instead of drop to save some overhead of creating unnecessary sequence objects. Be sure to capture the return value of seq to avoid overhead of object creations later on. We do this in the if-let.
(defn make-str-from-chars
"make a string from a sequence of characters"
([chars] (make-str-from-chars chars ""))
([chars result]
(if-let [chars (seq chars)]
(recur (next chars) (str result (first chars)))
result)))
Functions like this, which just return the accumulator upon fully consuming its input, cry for reduce.
(defn make-str-from-chars
"make a string from a sequence of characters"
[chars]
(reduce str "" chars))
This is already nice and short, but in this particular case we can do even a little better by using apply. Then str can use the underlying StringBuilder to its full power.
(defn make-str-from-chars
"make a string from a sequence of characters"
[chars]
(apply str chars))
Hope this helps.
You can also use clojure.string/join, as follows:
(require '[clojure.string :as str] )
(assert (= (vec "abcd") [\a \b \c \d] ))
(assert (= (str/join (vec "abcd")) "abcd" ))
There is an alternate form of clojure.string/join which accepts a separator. See:
http://clojuredocs.org/clojure_core/clojure.string/join

Split string into individual characters

I am having two problems while working in Lisp and I can't find any tutorials or sites that explain this. How do you split up a string into its individual characters? And how would I be able to change those characters into their corresponding ASCII values? If anyone knows any sites or tutorial videos explaining these, they would be greatly appreciated.
CL-USER 87 > (coerce "abc" 'list)
(#\a #\b #\c)
CL-USER 88 > (map 'list #'char-code "abc")
(97 98 99)
Get the Common Lisp Quick Reference.
A Lisp string is already split into its characters, in a way. It is a vector of characters, and depending upon what you need to do, you can use either whole string operations on it, or any operations applicable to vectors (like all the operations of the sequence protocol) to handle the individual characters.
split-string splits string into substrings based on the regular expression separators
Each match for separators defines a splitting point; the substrings between splitting points are made into a list, which is returned. If omit-nulls is nil (or omitted), the result contains null strings whenever there are two consecutive matches for separators, or a match is adjacent to the beginning or end of string. If omit-nulls is t, these null strings are omitted from the result. If separators is nil (or omitted), the default is the value of split-string-default-separators.
As a special case, when separators is nil (or omitted), null strings are always omitted
from the result. Thus:
(split-string " two words ") -> ("two" "words")
The result is not ("" "two" "words" ""), which would rarely be useful. If you need
such a result, use an explicit value for separators:
(split-string " two words " split-string-default-separators) -> ("" "two" "words" "")
More examples:
(split-string "Soup is good food" "o") -> ("S" "up is g" "" "d f" "" "d")
(split-string "Soup is good food" "o" t) -> ("S" "up is g" "d f" "d")
(split-string "Soup is good food" "o+") -> ("S" "up is g" "d f" "d")
You can also use elt or aref to get specific characters out of a string.
One of the best sites for an in-depth introduction to Common Lisp is the site for the Practical Common Lisp book (link to the section on numbers, chars and strings). The whole book is available online for free. Check it out.

Resources