Scheme - Manipulating strings - string

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!

Related

In DrRacket how do i alter my program that uses if statements to rather use a conditional statement

Here is the question I am trying to solve
Write a function (last-substring-len s n).
It consumes a Str and a Nat, and returns the substring of s with
length n that comes last alphabetically.
Remember to consider if your function Requires anything of its
arguments!
For example:
(last-substring-len "foobar" 4) => "ooba"
(last-substring-len "thequickbrownfoxjumpsoverthelazydogs" 7) => "xjumpso"
I have actually nearly solved my question but I want to be able to instead alter my code so that the if statements for the function get-maximium are replaced with cond statements.
This is my original code:
(define (substrings-w-len s n)
(cond
[(> n (string-length s)) '()]
[ else (cons (substring s 0 n)
(substrings-w-len (substring s 1 (string-length s)) n))]))
(define (get-maximium string-list)
(if(null? string-list) '()
(if(= 1( length string-list)) string-list
(if (equal? #true (string>=? (first string-list) (first (rest string-list))))
(get-maximium (cons (first string-list) (rest (rest string-list))))
(get-maximium (rest string-list))))))
(define (last-substring-w-len s n)
( get-maximium (substrings-w-len s n)))
(check-expect (last-substring-w-len "foobar" 4) "ooba")
This yields (list "ooba") when it should only yield "ooba" so I need to convert the list into a string which I should be able to solve myself but i'd appreciate if someone could help me with this.
The main issue now though is i want to replace the if statements in get-maximum with a conditional statement
This is me altering the get-maximium function to try to replace the if functions with a cond statement but it doesn't work yet.
(define (get-maximium string-list)
(cond
[(null? string-list) '()]
[(= 1( length string-list)) string-list]
[ else (equal? #true (string>=? (first string-list) (first (rest string list))))
(get-maximium (cons (first string-list) (rest (rest string-list))))
(get-maximium (rest string-list))]))
There's an issue with the else part. It should look like this:
(define (get-maximium string-list)
(cond [(null? string-list) '()]
[(= 1 (length string-list)) string-list]
[(string>=? (first string-list) (first (rest string list)))
(get-maximium (cons (first string-list) (rest (rest string-list))))]
[else (get-maximium (rest string-list))]))
Also, there's no need to ask (equal? #t <condition>), simply ask <condition>.
Try to retrieve the first element of the answer like so:
(define (last-substring-w-len s n)
(first ( get-maximium (substrings-w-len s n))))
(last-substring-w-len "foobar" 4) => "ooba"
(last-substring-w-len "thequickbrownfoxjumpsoverthelazydogs" 7) => "xjumpso"
For a thorough explanation see my answer here

In Racket how do I consume a String sentence and create a listof String that consists of every word in that sentence?

Essentially, I just want to convert a string sentence into a list of individual string words using DrRacket/Scheme. I currently am using Intermediate Student with Lambda so that may limit some functions I can use but any assistance would be appreciated. For instance I want
(split-string "the man over there is close") to yield
(list "the" "man" "over" "there" "is" "close")
This problem is slightly tricky. For starters, you need to think about the input string as a list of chars. Every time a space is encountered, we know that a new word is complete.
We can keep track of the current word in a variable, and use an accumulator for storing whole words, being careful of reversing the intermediate values, because we'll be consing them so they'll be in reverse. This is what I mean:
(define (split-string lst)
(let loop ((acc '()) (current '()) (chars (string->list lst)))
(cond ((null? chars)
(reverse (cons (list->string (reverse current)) acc)))
((char=? (car chars) #\space)
(loop (cons (list->string (reverse current)) acc)
'()
(cdr chars)))
(else
(loop acc
(cons (car chars) current)
(cdr chars))))))
It works as expected:
(split-string "the man over there is close")
=> '("the" "man" "over" "there" "is" "close")
Tail call recursive version
For single character separators.
(define (split-string s (sep #\space))
(define (rec-split sl sep (acc '()) (h-acc '()))
(cond ((empty? sl) (reverse (map (lambda (isl) (list->string isl))
(cons (reverse h-acc) acc))))
((char=? (car sl) sep) (rec-split (cdr sl) sep (cons (reverse h-acc) acc) '()))
(else (rec-split (cdr sl) sep acc (cons (car sl) h-acc)))))
(rec-split (string->list s) sep))
> (split-string "the man over there is close")
;; '("the" "man" "over" "there" "is" "close")

How to get the last element of a list and return nil if the element isn't in the list

I would like to get the position of any element in the list and get nil if the element isn't in the list. I did:
(defun myposition (letter list)
(cond
((atom list) nil)
((equal (car list) letter) 0)
((null (car list)) (myposition letter))
(t (1+ (myposition letter (cdr list)))) ) )
(myposition 'k '(g h i j k l)
4
(myposition 'p '(g h i j k l)
nil is not a number
When I replace ((atom list) nil) par ((atom list) 0), I get 6 instead nil
(myposition 'p '(g h i j k l)
6
In your first example, your function will recursively compute 1+1+1+1+0 = 4 to find the correct result.
In your second example, it will run through the whole list, add 1 per (non-matching) element, and finally add nil. So it actually computes 1+1+1+1+1+1+nil, which is incorrect since nil is not a number, hence the error message. If you replace nil by zero, it computes 1+1+1+1+1+1+0 which is wrong.
So your basic problem is that you recursively add 1 and, reaching the end of the list, you would like to throw away what you computed until then. But you have an addition pending which you cannot escape.
The easiest way is to change from a recursive to a tail-recursive solution, which is technically a plain goto. Here the addition is done by incrementing a variable, not by unwinding the call stack, which makes it easy to throw away the result from the previous additions and just return nil because there is no addition pending.
A (tail-)recursive solution could be:
(defun myposition (letter lst)
(labels ((sub (lst pos)
(cond
((null lst) nil)
((equal (car lst) letter) pos)
(t (sub (cdr lst) (1+ pos))))))
(if (atom lst) nil (sub lst 0))))
This will work in Common Lisp, but technically, if your implementation does no tail call optimisation, it might still blow the stack for large lists. That's why Common Lisp prefers iterative solutions, such as using the loop macro:
(defun myposition (letter lst)
(when (consp lst)
(loop for c in lst for i from 0
when (equal c letter) return i)))

"set! not an identifier" in Scheme

I am trying to modify the cdr of the car of an element of a list using set!, but I am getting an error: "set! not an identifier". Can anyone explain to me why this is happening? I am starting to work with objects by the way.
#lang racket
(define (multiset)
(let ((main-list '()))
(define (empty)
(eq? main-list '()))
(define (insert x)
(cond ((empty)
(set! main-list (cons (cons x 1) main-list)))
((= (car (car main-list)) x)
(begin (set! (cdr (car main-list))
(+ 1 (cdr (car main-list))))))
(else (cdr main-list))))
A multiset is represented as a list of pairs. For example, if I had a list '(1 1 1 2 2 2), the multiset representation would be '((1.3)(2.3))
The syntax of set! is
(set! <identifier> <expression>)
which is to say that the first form must be a symbol. In your code you are using:
(cdr (car main-list))
as the 'identifier' - hence the error.
Perhaps your background is CommonLisp and you are expecting set! to behave like setf? In Scheme there are separate functions for setting the 'car' and 'cdr' of a pair. Use set-car! and set-cdr!
> (define pair (cons 'car 'cdr))
> pair
(car . cdr)
> (set-car! pair 'foo)
> pair
(foo . cdr)
> (set-cdr! pair 'bar)
> pair
(foo . bar)
In R6RS (and probably R7RS) set-car! and set-cdr! can be found in the (rnrs mutable-pairs) library
GoZoner has given you the right explanation, but it is perfectly possible (and desirable) do avoid set! procedures. Here's an example of a procedure having the same result:
(define (rle lst)
(define (newpair c l res)
(if (> l 0) (cons (cons c l) res) res))
(let loop ((lst lst) (c #f) (l 0) (res '()))
(if (null? lst)
(reverse (newpair c l res))
(let ((n (car lst)))
(if (equal? c n)
(loop (cdr lst) c (add1 l) res)
(loop (cdr lst) n 1 (newpair c l res)))))))
such as
(rle '(1 1 1 2 2 2))
=> '((1 . 3) (2 . 3))

What's wrong with this code? (scheme)

I've set up a procedure in scheme that will analyze a list and return the middle index when the list is odd, and the average of the middle 2 values when the list is even. Here's what I have (these ones run perfectly fine by themselves):
(define (median-index-odd lst)
(define (median-index-iter1 lst times_carred)
(if (null? lst)
'()
(if (= times_carred (/ (+ (length lst) 1) 2))
(list (car lst))
(median-index-iter1 (cdr lst) (+ 1 times_carred)))))
(median-index-iter1 lst 0))
(define (median-index-even lst)
(define (median-index-iter2 lst times_carred)
(if (null? lst)
'()
(if (= times_carred (/ (length lst) 2))
(list (/ (+ (car lst) (cadr lst)) 2))
(median-index-iter2 (cdr lst) (+ 1 times_carred)))))
(median-index-iter2 lst 0))
Here's the actual procedure, without all the clutter from those helpers.
(define (median lst)
(if (null? lst)
'()
(if (even? lst)
(median-index-even lst)
(median-index-odd lst))))
However, when I try and run test cases, I get an error:
(display (median '(1 2 2 3 3 3 4 5))) (newline)
The object (1 2 2 3 3 3 4 5), passed as the first argument to integer-remainder, is not the correct type.
EDIT: Okay, yes, I completely overlooked the (even? (length lst)) part. I am currently debugging the helpers right now.
For starters this line is wrong, a list can not be even:
(if (even? lst)
A list's length, however, is a different matter:
(if (even? (length lst))
Also, in both procedures the comparison for determining if the list's mid point has been reached is wrong, you'll have to tweak this line in both helper procedures, because currently is not working:
(if (= times_carred ...
It'll be simpler if you start times_carred in 1 and change the condition to (>= times_carred (/ (length lst) 2)), the same comparison works for both cases.

Resources