Scheme String Help - string

I am trying to write a function that evaluates to the number of distinct characters in the input string str. So for example (distinct-char "eeeiicczz") would return 4. I need some help with my code. This is what I have.
(define string-contains
(lambda (str char)
(if (equal? str "")
#f
(if (char=? (string-ref str 0) char)
#t
(if (not (char=? (string-ref str 0) char))
(string-contains (substring str 1 (string-length str)) char))))))
(define unique-chars
(lambda (str)
(cond
((equal? str "") "")
((equal? (string-length str) 1) (string-ref str 0))
(else
(if (equal? (string-contains (substring str 1 (string-length str)) (string-ref str 0)) #t)
(unique-chars (substring str 1 (string-length str)))
(string-append (substring str 0 1) (substring str 1 (string-length str))))))))
(define distinct-char
(lambda (str)
(string-length (unique-chars str))))
I am limited to using these Built in functions:
(if x y z), (cond ...),
(read)
(string-length x)
(string-ref str x)
(substring x y)
(string-append x y)
(equal? x y), (char=?)
(remainder x y), (quotient x y)
(max ...), (min ...)
(+ x y), (- x y), (* x y), (/ x y)
(> x y), (< x y), (<= x y), (>= x y)
(and x y), (or x y), (not x y)

Since it's possible to convert a string to a list, it would be much easier to do that and use the built in list functions. (Note: the following code is in Racket. Since it's pretty much Scheme, I'm assuming these functions exist. If they don't check your docs for something similar)
(define (distinct-char str)
(length (remove-duplicates (string->list str))))
Here is a template you can fill in. Replace the comments with what you think should happen in each situation. Good luck!
(define (empty-string? str)
( #| What property does an empty string have? Add a simple boolean expression here. |# )
(define (char-exists? str char)
(cond
[(empty-string? str) ( #| If the string is empty, does the character exist? |# )]
[else ( #| Check the first character in the string. If it is what we're looking for
we're done! If it's not call this function on the rest of the string. |# )]))
(define (unique-chars str)
(cond
[(empty-string? str) ( #| What should you return if the string is empty? |# )]
[(equal? (string-length str) 1) ( #| What should you return if the string is one character long? |# )]
[else ( #| If the character at the beginning of the string exists in the rest of the string, ignore it
and preform recursion on the rest of the string. If it doesn't, append the character to
the result of this function on the rest of the string. |# )]))
(define (distinct-char str)
(string-length (unique-chars str)))

One of the reasons you learn stuff in scheme is it trains you to build up useful building blocks yourself, and then hook those building blocks together.
In this case, the general approach I would recommend is to write a function:
(string-contains str ch)
That returns #t or #f depending on whether or not str contains the character ch, and then use that to define a function:
(unique-chars str)
that returns a string of the unique characters in str (you scan str, building up your answer, and at every spot see if the next character is already in the answer string you're building, and if not, add it to your answer string).
Then, the function you wanted is just
(string-length (unique-chars str))

You can keep and pass along a list of every distinct character that you've come across before. That way, every time you check a new character, you look it up in your list to see if you've already seen it before. If it's not in your list, then it's a new character and you can add it to your list.
Homework question, so I'm not writing code in the answer.

Related

How do I find the number of characters in a string using scheme programming language?

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)

Checking if element is present in list in racket

How to check if an element is present in a list, both taken as input from the function call, without using the lambda? I was trying member? but could not get it.
(define (find-string (lst lst str ua)
(cond ((member? ua lst) #t)
(else #f))
The use of member would work it's just that you are adding extra "?" in front of the function none is required
(member 2 (list 1 2 3 4)) [1]
would return true
another way around is writing ones own recursive function
(define (is-in-list list value)
(cond
[(empty? list) false]
[(= (first list) value) true]
[else (is-in-list (rest list) value)]))
[1]https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._member%29%29
First, the way with lambda and ormap (for testing):
; ismember? :: String List-of-Strings -> Bool
(define (ismember1? str strs) (ormap [lambda (s) (string=? s str)] strs) )
Second way, with for/or, without lambda:
(define (ismember2? str strs)
(for/or ([s (in-list strs)])
(string=? s str) ) )
Third way, with member, without lambda:
(define (ismember3? str strs) (if [member str strs] #t #f) )
Refer to the official Racket documentation for member.
Notice that the last version is actually the worst in terms of performance.

Scheme: How to find a position of a char in a string

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

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"

detecting a palindrome without using reverse

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

Resources