Building repeating strings with spaces between the repeats - scheme

repeat takes in a number and a string, and returns the string repeated n times, separated by a space.
;; repeat: number string -> string
(define (repeat n str)
(replicate n str))
(repeat 2 "home")
gives me:
"homehome"
How would I add a space so it can give me "home home"?

Add a space to the string str at the end, before passing it to replicate:
(replicate n (string-append str " "))
That'll leave an extra space at the end, if you want to get rid of it do as #Tobia suggests:
(string-trim (replicate n (string-append str " ")))

Racket has a build-in function string-join for this, so in plain Racket this would be
> (string-join (build-list 2 (lambda (i) "home")))
"home home"

Related

Combining words list to a para in Racket

I have to combine a list of words to produce a para. I managed following:
(define (wordlist2para wl)
(define str " ")
(for ((w wl))
(set! str (string-append str w " ")))
(string-trim str))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"
It works but it is not functional. How can I write functional code for this?
If I wanted to do it explicitly and not use string-join, I would recurse and use three cases:
The empty list produces the empty string
A one-element list produces its sole element (this avoids having a trailing separator)
Otherwise, append the car and a space to the recursion on the cdr.
Like this:
(define (wordlist2para ws)
(cond ((null? ws) "")
((null? (cdr ws)) (car ws))
(else (string-append (car ws) " " (wordlist2para (cdr ws))))))
No need of recursion or loop, there is the primitive function string-join for this (see the manual):
(define (wordlist2para wl)
(string-join wl " "))
(wordlist2para '("this" "is" "a" "test"))
;; -> "this is a test"
We have standard procedures that does this:
;; racket library or srfi/13
(string-join '("this" "is" "it")) ; ==> "this is it"
There is a way to always rewrite these that are quite simple. I'd like to step away from rackets great feature set and just focus on simple scheme with recursive procedures. Notice that in your loop you are changing 2 things wl gets smaller, str gets longer, so lets make that:
; all things that change as arguments
(define (wordlist2para-loop wl str)
(if (null? wl)
str
(wordlist2para-loop (cdr wl)
(string-append str (car wl) " "))))
Now for we just replace the loop:
(define (wordlist2para wl)
(wordlist2para-loop wl ""))
From here on you can move the helper to become local or perhaps make it a named let or any other refactoring, but it doesn't really change the resulting compiled result in an implementation much, just how it looks.
Notice I haven't fixed the bug where there is only one word. (wordlist2para '("this")) ; ==> "this " The result is actually exactly the same as in your, only that it's tail recursive and functional.
I am not sure if following can be called functional but it does use some higher order functions:
(define (wordlist2para wl)
(string-trim
(apply string-append
(map (lambda(x) (string-append x " ")) wl))))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"

About strings and substrings

How would you design a function that consumes a string and a number i which inserts "_" at the ith position of the string?
(define (string-insert n i)
(substring n i))
I just need to add the "_" at the ith position which is driving me crazy
Just use string-append and substring, the trick is to get the indexes right:
(define (string-insert n i)
(string-append
(substring n <???> <???>)
"_"
(substring n <???>)))
I'll let you figure out the details, you'll see that it works as expected:
(string-insert "HelloWorld" 5)
=> "Hello_World"

In beginner scheme how do I represent a variable as a string?

My question is how do i code for
(triangle 5) produces (list "*****" "****" "***" "**" "*")
Note: (5 astericks 4, then 3 then 2 then 1). So far I have:
(define (triangle n)
(cond
[(zero? n) empty]
[else (cons n (triangle (sub1 n)))]))
But that only gives me (list 5 4 3 2 1). Please keep note that this uses only the basic of scheme beginner lists and abbreviations. Thanks!
It's always a good idea to split a complex problem in simpler, shorter subparts. In this case, we can simplify the general solution by first writing solutions to subproblems, like this:
First, build a procedure that creates a list of strings, where a string is "*****" or "****" or ... or "*"
Second, write a repeat helper procedure that given a string and a number, repeats the string that many times - for example: (repeat "*" 3) will return "***"
It's easy to see how the first subproblem can be expressed in terms of the second one. Because this looks like a homework, you shouldn't be asking for complete solutions here. It'll be more useful for you to reach the answer by yourself, here's the general idea, fill-in the blanks:
(define (triangle n)
(cond [<???> <???>] ; if n is zero return the empty list: '()
[else ; otherwise
(cons <???> ; cons n repetitions of * (using `repeat`)
(triangle <???>))])) ; and advance the recursion
(define (repeat str n)
(cond [<???> <???>] ; if n is zero return the empty string: ""
[else ; otherwise
(string-append <???> ; append the given string
(repeat <???> <???>))])) ; and advance the recursion
If you look at it carefully, both procedures share exactly the same structure. What changes is the value returned in the base case (an empty list and an empty string) and the procedure used for sticking together the partial answers (cons and string-append).
If you're just looking for how to convert numbers to string, you can use the (number->string x).
However, since you're looking to have the numbers represented as asterisks, it may be better for you to keep them as numbers until you've constructed a string of asterisks. In that case, you probably want a method like:
(define (num-to-asterisks x)
(make-string x #\*))
Try this:
(define (triangle n)
(let building ((i 0) (r '()))
(if (= i n)
r
(building (+ i 1)
(cons (string-append "*" (if (null? r) "" (car r)))
r)))))
this is nicely tail-recursive; builds up the result list by adding "*" to the first element of the result list.

Scheme case error

I'm trying to create a word count program in Scheme. I think I've worked out an algorithm that'll count my lines, words, and chars, but when I start to run the program, it tells me "The object #\1 is not applicable." "1" is the first character in the file I'm reading, and it should fall under "else". Everything I look at matches my case statement, so I think I'm doing it right, but clearly something's messed up somewhere. Thank you for your help!
(define files
(lambda (reading n)
(begin
(define in (open-input-file reading))
(let loop ((lines 0)
(words 0)
(chars 0)
(port (read-char in)))
(case (port)
((#\newline)
(loop (+ lines 1) words (+ chars 1) (read-char in)))
((#\space #\tab)
(loop lines (+ words 1) (+ chars 1) (read-char in)))
(else (loop lines words (+ chars 1) (read-char in)))))
(close-input-port in)
(display lines)
(display " ")
(display words)
(display " ")
(display chars)
(newline)
(display "Top ")
(display n)
(display " word(s):")
(newline)
'())))
Your problem is fortunately easy to fix. You've written:
(case (port) ...)
but that does a case on the result of calling the function port. Of course, port isn't a function, it's a character, so you just want:
(case port ...)
How does the "let loop" know when you've reached the end of the file? What does read-char return when it hits the end? Hint: read about the eof-object? predicate. A predicate is a function that returns #t or #f. You may need to use a cond rather than a case to use this predicate
Also, the lines, chars and words variables are local to the named let, so you can't print then out "outside". (Hint: print them inside the loop when (eof-object? port) returns #t.
Style quibble: don't use the name "port" for the char that read-char returns. "in" is the port (file handle), Maybe you can use "ch" instead of "port".

How can I use concatenation in scheme without spaces

I have a problem with concatenation and spaces in Scheme.
The result of the command:
(append '(%procedure:) (list '+) '(%))** //with spaces
is:
%procedure: + % //without spaces
How can I make the same result without space between the lists, so the result will be:
%procedure:+%
You're trying to use symbols, which aren't the same thing as strings in Scheme. If you want to have control over your printed output, you should use strings, which are arrays of characters.
> (append '(hello) '(world))
(hello world)
> (string-append "hello " "world")
"hello world"
> (symbol->string 'hello)
"hello"
> (apply string-append (map symbol->string '(a b c d e f g)))
"abcdefg"
append returns a list, and the evaluator prints the result like (a b c), where spaces are inserted to make the representation clear. If you need %procedure:+%, you may create a new symbol or use strings instead of symbols.

Resources