Appending character to string in Common Lisp - string

I have a character ch that I want to append to a string str. I realize you can concatenate strings like such:
(setf str (concatenate 'string str (list ch)))
But that seems rather inefficient. Is there a faster way to just append a single character?

If the string has a fill-pointer and maybe is also adjustable.
Adjustable = can change its size.
fill-pointer = the content size, the length, can be less than the string size.
VECTOR-PUSH = add an element at the end and increment the fill-pointer.
VECTOR-PUSH-EXTEND = as VECTOR-PUSH, additionally resizes the array, if it is too small.
We can make an adjustable string from a normal one:
CL-USER 32 > (defun make-adjustable-string (s)
(make-array (length s)
:fill-pointer (length s)
:adjustable t
:initial-contents s
:element-type (array-element-type s)))
MAKE-ADJUSTABLE-STRING
CL-USER 33 > (let ((s (make-adjustable-string "Lisp")))
(vector-push-extend #\! s)
s)
"Lisp!"

If you want to extend a single string multiple times, it is often
quite performant to use with-output-to-string, writing to the stream
it provides. Be sure to use write or princ etc. (instead of format)
for performance.

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.

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

Displaying a string while using cond in Lisp

I'm just starting off with Lisp and need some help. This is technically homework, but I gave it a try and am getting somewhat what I wanted:
(defun speed (kmp)
(cond ((> kmp 100) "Fast")
((< kmp 40) "Slow")
(t "Average")))
However, if I run the program it displays "Average" instead of just Average (without the quotes).
How can I get it to display the string without quotes?
You can use symbols instead of strings. But keep in mind that symbols will be converted to uppercase:
> 'Average
AVERAGE
If you care about case or want to embed spaces, use format:
> (format t "Average")
Average
The read-eval-print loop displays the return value of your function, which is one of the strings in a cond branch. Strings are printed readably by surrounding them with double-quotes.
You could use (write-string (speed 42)). Don't worry that it also shows the string in double-quotes - that's the return value of write-string, displayed after the quoteless output.
You can also use symbols instead of strings:
(defun speed (kmp)
(cond ((> kmp 100) 'fast)
((< kmp 40) 'slow)
(t 'average)))
Symbols are uppercased by default, so internally fast is then FAST.
You can write any symbol in any case and with any characters using escaping with vertical bars:
|The speeed is very fast!|
Above is a valid symbol in Common Lisp and is stored internally just as you write it with case preserved.

How to center a string in elisp?

I would like to center justify a given input string to a given size so that what is produced is a string with padded spaces either side (left and right) of the input string.
The code I have to do this:
(defun center-string (string size)
(let* ((padding (/ (- size (length string)) 2))
(lpad (+ (length string) padding))
(lformat (format "%%%ds" lpad))
(rformat (format "%%%ds" (- size))))
(format rformat (format lformat string))))
And some test cases:
(center-string "KJF" 10)
=> " KJF "
(center-string "KF" 10)
=> " KF "
(center-string "0123456789" 10)
=> "0123456789"
(center-string "0123456789" 5)
=> "0123456789" ; Notice justifcation is ignored as input string too large.
Is there an existing elisp function to do this or a better method?
There's a center-line, which works in a buffer (and uses the buffer's value of fill-column as the line length), so if your goal is to produce a nicely formatted file, you could do something like
(defun insert-centered (x)
(insert "\n" x)
(center-line)
(insert "\n"))
No, there is not an existing emacs lisp routine that does what you want. (the standard search through emacs lisp info and emacs info supports this).

Resources