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"
Related
I used string-length to get the number of characters but I am having difficulties in defining a recursive function. Should I convert the string to a list and then count the elements?
There's no useful way of doing this recursively (or even tail recursively): strings in Scheme are objects which know how long they are. There would be such an approach in a language like C where strings don't know how long they are but are delimited by some special marker. So for instance if (special-marker? s i) told you whether the i'th element of s was the special marker object, then you could write a function to know how long the string was:
(define (silly-string-length s)
(let silly-string-length-loop ([i 1])
(if (special-marker? s i)
(- i 1)
(silly-string-length-loop (+ i 1)))))
But now think about how you would implement special-marker? in Scheme: in particular here's the obvious implementation:
(define (special-marker? s i)
(= i (+ (string-length s) 1)))
And you can see that silly-string-length is now just a terrible version of string-length.
Well, if you wanted to make it look even more terrible, you could, as you suggest, convert a string to a list and then compute the length of the lists. Lists are delimited by a special marker object, () so this approach is reasonable:
(define (length-of-list l)
(let length-of-list-loop ([i 0]
[lt l])
(if (null? lt)
i
(length-of-list-loop (+ i 1) (rest lt)))))
So you could write
(define (superficially-less-silly-string-length s)
(length-of-list
(turn-string-into-list s)))
But, wait, how do you write turn-string-into-list? Well, something like this perhaps:
(define (turn-string-into-list s)
(let ([l (string-length s)])
(let loop ([i 0]
[r '()])
(if (= i l)
(reverse r)
(loop (+ i 1)
(cons (string-ref s i) r))))))
And this ... uses string-length.
What is the problem with?
(string-length string)
If the question is a puzzle "count characters in a string without using string-length",
then maybe:
(define (my-string-length s)
(define (my-string-length t n)
(if (string=? s t) n
(my-string-length
(string-append t (string (string-ref s n))) (+ n 1))))
(my-string-length "" 0))
or:
(define (my-string-length s)
(define (my-string-length n)
(define (try thunk)
(call/cc (lambda (k)
(with-exception-handler (lambda (x)
(k n))
thunk))))
(try (lambda ()
(string-ref s n)
(my-string-length (+ n 1)))))
(my-string-length 0))
(but of course string-ref will be using the base string-length or equivalent)
I am trying to find the index of a string where it is equal to a certain character, but I can seem to figure it out.
This is what I got so far, but its not working...
(define getPos
(lambda ()
(define s (apply string-append myList))
(getPosition pos (string->list s))))
(define getPosition
(lambda (position s)
(if (and (< position (length s)) (equal? (car s) #\space))
((set! pos (+ pos 1)) (getPosition (cdr s) pos));increment the positon and continue the loop
pos)));else
(define length
(lambda (s);the value s must be coverted to a string->list when passed in
(cond
((null? s) 0)
(else (+ 1 (length (cdr s)))))))
The solution is simple: we have to test each char in the list until either we run out of elements or we find the first occurrence of the char, keeping track of which position we're in.
Your proposed solution looks weird, in Scheme we try to avoid set! and other operations that mutate data - the way to go, is by using recursion to traverse the list of chars. Something like this is preferred:
(define (getPosition char-list char pos)
(cond ((null? char-list) #f) ; list was empty
((char=? char (car char-list)) pos) ; we found it!
(else (getPosition (cdr char-list) char (add1 pos))))) ; char was not found
For 0-based indexes use it like this, converting the string to a list of chars and initializing the position in 0:
(getPosition (string->list "abcde") #\e 0)
=> 4
Of course, we can do better by using existing procedures - here's a more idiomatic solution:
(require srfi/1) ; required for using the `list-index` procedure
(define (getPosition string char)
(list-index (curry char=? char)
(string->list string)))
(getPosition "abcde" #\e)
=> 4
A solution with for:
#lang racket
(define (find-char c s)
(for/first ([x s] ; for each character in the string c
[i (in-naturals)] ; counts 0, 1, 2, ...
#:when (char=? c x))
i))
(find-char #\o "hello world")
(find-char #\x "hello world")
Output:
4
#f
I was thinking a way to create a function that detects a palindrome without using reverse...
I thought I would be clever and do a condition where substring 0 to middle equals substring end to middle. I;ve found out that it only works on words with 3 letters "wow" because "w" = "w". But if the letters are like "wooow", wo doesn't equal ow. What is a way to detect palindrome without using a reverse function?
Hint or solutions might be very helpful
(define (palindrome? str)
(cond
((equal? (substring str 0 (- (/ (string-length str) 2) 0.5))
(substring str (+ (/ (string-length str) 2) 0.5) (string-length str))) str)
(else false)))
Oh and I'm using beginner language so I can't use stuff like map or filter
yes I know this is a very useless function haha
It's possible to solve this problem by messing around with the string's characters in a given index. The trick is to use string-ref wisely. Here, let me give you some hints pointing to a solution that will work with the beginner's language :
; this is the main procedure
(define (palindrome? s)
; it uses `loop` as a helper
(loop <???> <???> <???>))
; loop receives as parameters:
; `s` : the string
; `i` : the current index, starting at 0
; `n` : the string's length
(define (loop s i n)
(cond (<???> ; if `i` equals `n`
<???>) ; then `s` is a palindrome
(<???> ; if the char at the current index != its opposite (*)
<???>) ; then `s` is NOT a palindrome
(else ; otherwise
(loop <???> <???> <???>)))) ; advance the recursion over `i`
Of course, the interesting part is the one marked with (*). Think of it, a string is a palindrome if the char at the 0 index equals the char at the n-1 index (n being the string's length) and the char at the 1 index equals the char at the n-2 index, and so on. In general, if it's true that the char at the i index equals the char at the n-i-1 index (its "opposite") for all i, then we can conclude that the string is a palindrome - but if a single pair of opposite chars is not equal to each other, then it's not a palindrome.
As a further optimization, notice that you don't need to traverse the whole string, it's enough to test the characters up to the half of the string's length (this is explained in Chris' answer) - intuitively, you can see that if the char at i equals the char at n-i-1, then it follows that the char at n-i-1 equals the char at i, so there's no need to perform the same test two times.
Try to write the procedures on your own, and don't forget to test them:
(palindrome? "")
=> #t
(palindrome? "x")
=> #t
(palindrome? "axa")
=> #t
(palindrome? "axxa")
=> #t
(palindrome? "axc")
=> #f
(palindrome? "axca")
=> #f
(palindrome? "acxa")
=> #f
(palindrome? "axcta")
=> #f
Here is a creative answer
(define (palindrome list)
(let halving ((last list) (fast list) (half '()))
(cond ((null? fast) (equal? half last))
((null? (cdr fast)) (equal? half (cdr last)))
(else (halving (cdr last) (cddr fast)
(cons (car last) half))))))
It travels halfway down the list (using fast to find the end), builds up a list of the first half and then simply uses equal? on half with the remainder of list.
Simple.
For each i, from 0 to floor(length / 2), compare the character at index i and at index length - i - 1.
If mismatch, return false.
Otherwise, if the loop runs out, return true.
Skeletal code:
(define (palindrome? str)
(define len (string-length str))
(define halfway <???>)
(let loop ((i 0))
(cond ((>= i halfway) #t)
((char=? <???> <???>)
(loop (+ i 1)))
(else #f))))
I'm just starting with Scheme.
I'm trying to use some procedures from String Library.
Here's what I need:
input: "ccaAaAaAa"
function: generate all strings substituting all possible aAa to aBa, one substitution only
output: "ccaBaAaAa" and "ccaAaBaAa" and "ccaAaAaBa"
Is there any easy way to do that? Maybe a procedure that return a list of index of pattern found?
Apparently the searching function string-contains only returns the first occurrence.
What I thought is: after producing the first string "ccaBaAaAa", trim to the first index of the pattern found: the original "ccaAaAaAa" becomes "AaAaAa". Repeat (recursively).
Thanks.
string-contains won't give you a list of all occurrences of the substring, but it will tell you whether there is one, and if there is, what its index is. It also allows you to restrict the search to a particular range within the string. Based on this, if you get a match, you can recursively search the rest of the string until you no longer get a match.
From there, you can do the substitution for each match.
What is wrong by writing such a function?
(define (replace input)
(let loop ((done '())
(remaining (string->list input))
(output '()))
(if (pair? remaining)
(if (char=? #\a (car remaining))
(let ((remaining (cdr remaining)))
(if (pair? remaining)
(if (char=? #\A (car remaining))
(let ((remaining (cdr remaining)))
(if (pair? remaining)
(if (char=? #\a (car remaining))
(loop (append done (list #\a #\A))
remaining
(cons (list->string
(append done
(cons #\a
(cons #\B
remaining))))
output))
(loop (append done (list #\a #\A
(car remaining)))
(cdr remaining)
(reverse output)))
(reverse output)))
(loop (append done (list #\a (car remaining)))
(cdr remaining)
(reverse output)))
(reverse output)))
(loop (append done (list (car remaining)))
(cdr remaining)
(reverse output)))
(reverse output))))
(replace "ccaAaAaAa") ;=> ("ccaBaAaAa" "ccaAaBaAa" "ccaAaAaBa")
About 15 minutes work.
I thought there could be better string libraries that I didn't know about. But I end up doing what I'd proposed in the question. (For a general input case)
(define (aplicarRegra cadeia cadeiaOriginal regra n)
(let* ((antes (car regra))
(depois (cdr regra))
(index (string-contains cadeia antes))
(tamanho (string-length antes))
(diferenca (- (string-length cadeiaOriginal) (string-length cadeia))))
(if index
(let* ((cadeiaGerada (string-replace cadeiaOriginal depois (+ index diferenca) (+ index diferenca tamanho))))
(if(<= (string-length cadeiaGerada) n)
(lset-union equal? (list cadeiaGerada) (aplicarRegra(substring cadeia (+ 1 index)) cadeiaOriginal regra n))
(aplicarRegra (substring cadeia (+ 1 index)) cadeiaOriginal regra n)))
(list))))
But thanks anyway!
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