Iterating over strings - string

I have two strings of the same length which differ in exactly one character and I want a string of all the characters which are equal. So basically something like this which evaluates to a string instead of a list of characters:
(loop for a across "abcd"
for b across "abce"
when (char= a b) collect a)
Although performance isn't an issue here, I found it cumbersome to have a (coerce ... 'string) around it.
So I came up with something like
(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
which does the job but looks not very elegant to me.
(map 'string (lambda (a b) (when (char= a b) a)) "abcd" "abce")
looks nicer but is not working because NIL is not a character when aand bare not equal.
Is there a more elegant idiom to iterate over a string and get a string back?

(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
Repeated concatenate are not a that good idea for longer strings.
Alternatives:
Loop into a list and coercing to a string
CL-USER 3 > (loop for a across "abcd"
and b across "abce"
when (char= a b) collect a into list
finally (return (coerce list 'string)))
"abc"
Using stream and converting it to a string
CL-USER 4 > (with-output-to-string (*standard-output*)
(loop for a across "abcd"
and b across "abce"
when (char= a b) do (write-char a)))
"abc"
Using an adjustable string
CL-USER 5 > (loop with string = (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)
for a across "abcd"
for b across "abce"
when (char= a b) do (vector-push-extend a string)
finally (return string))
"abc"

Another possibility is to use mismatch as in the comment of David Hodge:
CL-USER> (defun f(a b)
(let ((pos (mismatch a b)))
(concatenate 'string (subseq a 0 pos) (subseq a (1+ pos)))))
F
CL-USER> (f "abcdefg" "abcxefg")
"abcefg"

Use map to loop over multiple lists simultaneously
(map 'string #'(lambda (a b) (if (char= a b) a #\Rubout)) "abce" "abcd")
'string coerces resulting list into a string. #\Rubout get's coerced to a zero-length string. #\Backspace would even delete the last character.

Another way (it does not assume anything about elements position)
(defun only-matching (seq1 seq2)
(remove-if-not (lambda (c) (find c seq1)) seq2))
CL-USER> (only-matching "abcd" "abce")
"abc"
CL-USER> (only-matching "abdc" "abec")
"abc"`
or
(coerce
(intersection (coerce "abdc" 'list)
(coerce "abec" 'list))
'string)
which does not preserve order also
Note: remove-if-not is deprecated 'officially'.

Related

Check are there lowercase characters in string

I need return True or False
True if at least one lowercase character
False no lowercase characters
I tried do it with loop and lambda function
Something like this
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
)
I need False if never worked "if"
With a predefined function, you could use some (manual):
CL-USER> (some #'lower-case-p "AbC")
T
CL-USER> (some #'lower-case-p "ABC")
NIL
There is a similar operation for the loop syntax (manual):
CL-USER> (loop for x across "AbC" thereis (lower-case-p x))
T
CL-USER> (loop for x across "ABC" thereis (lower-case-p x))
NIL
Finally, note that loop always returns nil when the iteration terminates without producing a result, so a less concise use of loop could be:
CL-USER> (loop for x across "AbC" if (lower-case-p x) do (return t))
T
CL-USER> (loop for x across "ABC" if (lower-case-p x) do (return t))
NIL
Code errors
You code is not balanced with respect to parentheses, there is a missing closing parenthesis at the end:
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
) ; <-- closes "(loop"
The syntax error in your loop should have raised an error, it does not make sense to write an expression EXPR directly in (loop for c across w EXPR), there should be a preceding do.
The literal ((lambda (c) E) ch) can be directly written as E where every occurence of the variable c is substituted by ch, namely:
(if (lower-case-p ch) return T)
The use of an intermediate literal lambda brings nothing here.
Also, the above reads as: if ch is lowercase, the value of the if is the value bound to the return variable, otherwise it is T. You are indeed missing parens around (return T). A "one-armed" (if T X) is best written as (when T X).
Another approach
You already have an example with some and loop, you can also use a short-circuiting map:
(defun check-lower-word (word)
(block nil
(map ()
(lambda (c)
(when (lower-case-p c)
(return t)))
word)))
A call to MAP with nil as a first argument means the sequence is iterated for effects and returns nil. For each character in the sequence (list or vector), when the character is lower-case, return T. The return exits the iteration early up to the NIL block.
I did it as
(defun check-lower-word (word)
(block outer
(loop
for ch across word do
(if (lower-case-p ch) (return-from outer T))
)
)
)

extracting a n element from a list with dolist on lisp

I am trying to create a function where it takes a list of letters as parameter and a single letter as a parameter. I want to remove the single letter from the list.
(defun extract-all (lett li)
(let ((new-list nil))
(dolist (letter li new-list)
(if (eql lett letter)
(setf new-list (cons nil new-list))
(setf new-list (cons letter new-list))))))
so if I call the function with (extract-all 'n '(i n t e l l)), I want it to return i t e l l with the n removed.
First of all, you are not removing letters
(characters), but rather
symbols:
(type-of 'n)
==> SYMBOL
Second, there is a standard function remove to do just that:
(remove 'n '(i n t e l l))
==> (I T E L L)
Third, if you remove your "then" clause and reverse the result, you will get what you want:
(defun my-remove (object list)
(let ((new-list nil))
(dolist (element list (nreverse new-list))
(unless (eql object element)
(push element new-list)))))
(my-remove 'n '(i n t e l l))
==> (I T E L L)
Note that there are more ways to skin the cat, e.g.,
(defun my-remove (object list)
(loop for element in list
unless (eql object element)
collect element))
However, it is always best to use the library.
See also Where to learn how to practically use Common Lisp.

How to modify string by index in Clojure?

I want to modify a string by applying a function to some of its chars (by starting index and length).
For example, I want to increment the ascii representation of the string "aaaaa" from the 2nd index to the 4th.
[start=1 length=3]
"aaaaa" => "abbba"
The only way I could think of is applying map, but it goes over all the sequence.
You could use subs to get the portions you do and don't want to modify. After modification use str to concatenate the result together:
(defn replace-in-str [f in from len]
(let [before (subs in 0 from)
after (subs in (+ from len))
being-replaced (subs in from (+ from len))
replaced (f being-replaced)]
(str before replaced after)))
You can call it:
(replace-in-str
(fn [sub-str] (apply str (map #(char (+ 1 (int %))) sub-str)))
"aaaaa"
1
3)
Indeed map applies the function to every element in the sequence. One way to get around that is to start with map-indexed. Unlike map, map-indexed passes the element's index as the first argument to the mapping function. When we have element's index, we can use it to choose if we need to perform the operation or just return the element as is.
A solution might look like this:
(defn inc-char [c]
(char (inc (long c))))
(defn if-in-range [from to f]
(fn [i x & args]
(if (<= from i (dec to))
(apply f x args)
x)))
(defn map-subs [from to f s]
(apply str (map-indexed (if-in-range from to f) s)))
(map-subs 1 4 inc-char "aaaaa")
;; "abbba"
I thought of using map-index to execute the operation only on the specified index:
((fn [op start length] (map-indexed (fn [i m] (if (<= start i length)
(op m)
m)) "aaaaa"))
#(char (+ 1 (int %)))
1
3)
=> (\a \b \b \b \a)
Here you go:
(defn replace-str
[s start-i end-i]
(apply str (map-indexed (fn [index val]
(if (and (>= index start-i)
(<= index end-i))
(char (+ (int val) 1))
val))
s)))
(replace-str "aaaa" 1 2)
;=> "abba"

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"

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

Resources