List of chars to string in Emacs Lisp - string

I have a list of characters (?h ?e ?l ?l ?o) and i want to convert it to string "hello". Currently i use this structure:
(concat (mapcar (lambda (ch) (char-to-string ch)) s))
Is there a more elegant and idiomatic way to convert list of chars to a string in Elisp?

Elisp's concat returns a string:
(concat '(?h ?e ?l ?l ?o))
(Found it out from coerce implementation in cl)

There's also (apply #'string LIST-OF-CHARS).

Related

Flexible replace substring - Scheme

Is there, in Scheme, a good way to replace a substring of a string with another string, the length of which could vary? I am looking for something similar to this:
(replace-all string pattern replacement)
(replace-all "slig slog slag" "g" "ggish")
=> "sliggish sloggish slaggish"
You can roll your own. It's not very efficient but it will do the work on small strings. (I wouldn't use it on string lengths above a million chars)
Make (prefix? src prefix) such that i evaluates to #t if the beginning of the list src is the same as prefix.
Make (append-reverse rev-head tail) such that (append-reverse '(1 2 3) '(4 5 6)) ; ==> (3 2 1 4 5 6). This could easily be done with foldl or it's a standard procedure in SRFI-1
Then (replace-all haystack needle replacement) is quite simple:
(define (replace-all haystack needle replacement)
;; most of the processing works on lists
;; of char, not strings.
(let ((haystack (string->list haystack))
(needle (string->list needle))
(replacement (string->list replacement))
(needle-len (string-length needle)))
(let loop ((haystack haystack) (acc '()))
(cond ((null? haystack)
(list->string (reverse acc)))
((prefix? haystack needle)
(loop (list-tail haystack needle-len)
(reverse-append replacement acc)))
(else
(loop (cdr haystack) (cons (car haystack) acc)))))))
(replace-all "The cat looks like a cat." "cat" "dog")
; ==> "The dog looks like a dog."
Sure, take a look at the documentation of your Scheme interpreter to find a suitable procedure. For instance, in Racket we have string-replace which works like this:
(string-replace "slig slog slag" "g" "ggish")
=> "sliggish sloggish slaggish"

LISP - Modify String

I have to write a program that changes a string's vowels, consonants and other symbols into C, V respectively 0. I've done this but I wonder if there is a more efficient and elegant way to do it. Would appreciate input.
(defun string-to-list (string)
(loop for char across string collect char))
(defun is-vowel (char) (find char "aeiou" :test #'char-equal))
(defun is-consonant (char) (find char "bcdfghjklmnpqrstvwxyz" :test #'char-equal))
(defun letter-type (char)
(if (is-vowel char) "V"
(if (is-consonant char) "C"
"0")))
(defun analyze-word (word-string)
(loop for char across word-string collect (letter-type char)))
Moreover, I would like to make it a string, how could I do that? Should I define a function that would iterate through the list and make it a string or is it an easier way to do it?
(defun letter-type (char)
(cond ((find char "aeiou" :test #'char-equal) #\V)
((alpha-char-p char) #\C)
(t #\0)))
CL-USER> (map 'string #'letter-type "analyze-word")
"VCVCCCV0CVCC"
Just for the sake of the idea:
(defun multi-replace-if (sequence function &rest more-functions)
(map (type-of sequence)
(lambda (x)
(loop for f in (cons function more-functions)
for result = (funcall f x)
while (eql x result)
finally (return result)))
sequence))
(multi-replace-if "bcdfghjklmnpqrstvwxyz"
(lambda (x) (if (find x "aeiouy") #\v x))
(lambda (y) (declare (ignore y)) #\c))
"cccccccccccccccccccvc"

Why does the following Clojure not detect a palindrome?

I'm just trying to convert to a string and compare to the reverse
(defn is-palindrome? [num]
(= (str num) (reverse (str num))))
Something like
(is-palindrome 1221)
Is returning false
Try this instead:
(defn is-palindrome? [num]
(= (str num) (apply str (reverse (str num)))))
In your code, the expression (reverse (str 1221)) returns the list of characters (\1 \2 \2 \1), which needs to be turned back into a string for the comparison to work. Alternatively, you could convert both numbers to character lists and perform a list comparison instead:
(defn is-palindrome? [num]
(= (seq (str num)) (reverse (str num))))
(defn palindrome? [num]
(= (seq (str num)) (clojure.string/reverse (str num))))
Your code returns false because it is comparing a string with a sequence, which can never be equal.
You can make it work by explicitly converting the string into a seq as follows:
(defn is-palindrome? [num]
(let [digit-sequence (seq (str num))]
(= digit-sequence (reverse digit-sequence))))
It turns out the the overhead of manipulating collections of characters dominates, so it's actually faster to compare the original string to a reversed version even though it seems like you're comparing twice as many characters as necessary. Make sure you use clojure.string/reverse, not clojure.core/reverse. The usual Clojure convention is to end a predicate with a question mark, but not to use the "is" prefix.
(require 'clojure.string)
(defn palindrome? [s] (= s (clojure.string/reverse s)))
(defn palindrome-num? [n] (palindrome? (str n)))
(reverse (str 1221))
returns a List of characters
(\1 \2 \2 \1)
but (str 1221) is a java String

Add character to string to get another string?

I want to add a character to a string, and get another string with the character added as a result.
This doesn't work:
(cons \a "abc")
Possible solutions, in order of preference:
Clojure core function
Clojure library function
Clojure user-defined (me!) function (such as (apply str (cons \a "abc")))
java.lang.String methods
Is there any category 1 solution before I roll-my-own?
Edit: this was a pretty dumb question. :(
How about:
(str "abc" \a)
This returns "abca" on my machine.
You can also use it for any number of strings/chars: (str "kl" \m "abc" \a \b).
You could use join from clojure.string:
(clojure.string/join [\a "abc"])
But for the simple use case you should really just use str, as #Dan Filimon suggests. join has the added benefit that you could put a separator between the joined strings, but without a separator it actually just applies str:
(defn ^String join
"Returns a string of all elements in coll, separated by
an optional separator. Like Perl's join."
{:added "1.2"}
([coll]
(apply str coll))
([separator [x & more]]
(loop [sb (StringBuilder. (str x))
more more
sep (str separator)]
(if more
(recur (-> sb (.append sep) (.append (str (first more))))
(next more)
sep)
(str sb)))))

Testing whether list elements are strings in CL

I'm trying to learn Common Lisp (sbcl) and getting practice with basic defuns. I'm trying to write one now that adds the lengths of all the strings in a list.
An early step is testing whether the first element is a string. I assumed you could call this with
(stringp (car '(s1 s2)))
where s1 and s2 are strings. Testing s1 with stringp, and asking for the car of the list seem to work ok, but combining them together doesn't give me what I expect:
CL-USER> (car '(s1 s2))
S1
CL-USER> (stringp s1)
T
CL-USER> (stringp (car '(s1 s2)))
NIL
Am I misunderstanding the stringp function, or the way lists work?
Thank you
'(s1 s2) is a list containing the symbols s1 and s2. So (car '(s1 s2)) returns the symbol s1 (as you can see by the fact that the REPL prints S1 and not whatever string is stored in the variable s1. Since a symbol is not a string, stringp returns false.
If you actually use a list of strings, it will work as you expect:
* (car (list s1 s2))
"contents of s1"
* (stringp (car (list s1 s2)))
T
The QUOTE prevents the evaluation of its enclosed form. The enclosed form is returned as is.
(car '(s1 s2)) returns S1. Which is a symbol and not a string.
If you evaluate s1, then Lisp returns its value. But that uses another evaluation step.
If you look at s1 as a symbol, then it stays a symbol if you tell Lisp:
CL-USER > 's1
S1
CL-USER > (stringp 's1)
NIL

Resources