I want to convert a string into a list of one strings in Racket:
(string-split-wishful "abcd" "") => (list "a" "b" "c" "d")
This is the function that I wish for. The closest thing is string-split which doesn't do what I want:
(string-split "abcd" "") => (list "" "a" "b" "c" "d" "")
How do I get rid of the superfluous empty strings at the beginning and the end? I know that I can do something like (reverse (cdr (reverse (cdr (string-split "abcd" ""))))) but I want to know if there's a more idiomatic way of doing this.
Try this:
(string-split "abcd" #rx"(?<=.)(?=.)")
; ==> ("a" "b" "c" "d")
It uses a regular expression instead of string and the regular expression consist of a zero-width positive look-behind assertion such that it only matches after a character and one zero-width positive look-ahead assertion such that the match need one char in its right side to match.
Alexis' suggestion is nice too, might even perform better too:
(map string (string->list "abcd"))
Related
Basically I have a problem, here is the information needed to solve the problem.
PigLatin. Pig Latin is a way of rearranging letters in English words for fun. For example, the sentence “pig latin is stupid” becomes “igpay atinlay isway upidstay”.
Vowels(‘a’,‘e’,‘i’,‘o’,and‘u’)are treated separately from the consonants(any letter that isn’t a vowel).
For simplicity, we will consider ‘y’ to always be a consonant. Although various forms of Pig Latin exist, we will use the following rules:
(1) Words of two letters or less simply have “way” added on the end. So “a” becomes “away”.
(2) In any word that starts with consonants, the consonants are moved to the end, and “ay” is added. If a word begins with more than two consonants, move only the first two letters. So “hello” becomes “ellohay”, and “string” becomes “ringstay”.
(3) Any word which begins with a vowel simply has “way” added on the end. So “explain” becomes “explainway”.
Write a function (pig-latin L) that consumes a non-empty (listof Str) and returns a Str containing the words in L converted to Pig Latin.
Each value in L should contain only lower case letters and have a length of at least 1.
I understand that i need to set three main conditions here, i'm struggling with Racket and learning the proper syntax to write out my solutions. first I need to make a conditions that looks at a string and see if it's length is 2 or less to meet the (1) condition. For (2) I need to look at the first two characters in a string, i'm assuming I have to convert the string into a list of char(string->list). For (3) I understand I just have to look at the first character in the string, i basically have to repeat what I did with (2) but just look at the first character.
I don't know how to manipulate a list of char though. I also don't know how to make sure string-length meets a criteria. Any assistance would be appreciated. I basically have barely any code for my problem since I am baffled on what to do here.
An example of the problem is
(pig-latin (list "this" "is" "a" "crazy" "exercise")) =>
"isthay isway away azycray exerciseway"
The best strategy to solve this problem is:
Check in the documentation all the available string procedures. We don't need to transform the input string to a list of chars to operate upon it, and you'll find that there are existing procedures that meet all of our needs.
Write helper procedures. In fact, we only need a procedure that tells us if a string contains a vowel at a given position; the problem states that only a-z characters are used so we can negate this procedure to also find consonants.
It's also important to identify the best order to write the conditions, for example: conditions 1 and 3 can be combined in a single case. This is my proposal:
(define (vowel-at-index? text index)
(member (string-ref text index)
'(#\a #\e #\i #\o #\u)))
(define (pigify text)
; cases 1 and 3
(cond ((or (<= (string-length text) 2)
(vowel-at-index? text 0))
(string-append text "way"))
; case 2.1
((and (not (vowel-at-index? text 0))
(vowel-at-index? text 1))
(string-append (substring text 1)
(substring text 0 1)
"ay"))
; case 2.2
(else
(string-append (substring text 2)
(substring text 0 2)
"ay"))))
(define (pig-latin lst)
(string-join (map pigify lst)))
For the final step, we only need to apply the pigify procedure to each element in the input, and that's what map does. It works as expected:
(pig-latin '("this" "is" "a" "crazy" "exercise"))
=> "isthay isway away azycray exerciseway"
I have a Clojure function autocomplete that takes in a prefix and returns the possible autocompletion of that string. For example, if the input is abs, I get absolute as the possible complete word.
The problem is that the function does a case-insentitive match for the prefix so if I have Abs as the prefix input, I still get absolute instead of Absolute.
What would be the correct way to get the final string completion that matches the case of the original prefix entered?
So, a function case-match that could work like
(case-match "Abs" "absolute") => "Absolute"
You can use the prefix string as the prefix to the case-insensitive search result. Just use subs to drop the length of the prefix from the search result:
(defn case-match [prefix s]
(str prefix (subs s (count prefix))))
(case-match "Abs" "absolute")
=> "Absolute"
This assumes your autocomplete function stays separate, and case-match would be applied to its result.
also, you can go with startsWith
(defn case-match
[prefix s]
(when (.startsWith (clojure.string/lower-case s)
(clojure.string/lower-case prefix))
s))
(case-match "Abs" "absolute")
#=> "absolute"
(case-match "Absence" "absolute")
#=> nil
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.
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
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.