Input: a nonempty string of characters without spaces
Output: a prefix of the input string defined as follows:
- First character goes into the prefix regardless of case
- If the next character is lowercase, it goes to the prefix
- If the next character is uppercase, it goes to the prefix, and the prefix is done and returned.
It should work like this: alfa -> alfa Alfa -> Alfa DiCAp -> DiC BRaVo -> BR b -> b B -> B
...
No big deal, if you can break out of the cycle. I can't even see how things like drop while may help.
The following is better: (1) itâs functional and doesnât rely on a control effect and (2) it probably deals with large strings better because string-append (in ~a) can get expensive when strings get long.
Hereâs another solution, using forâs #:final, which is less general than let/ec, but happens to fit the task. I also use cons to accumulate results in constant time.
I think I'm going to call this the HtDP approach? You presented it as a state machine, so I wrote it as a state machine with two states, the initial state and the continue state.
;; the initial state
(define (prefix str)
;; string is nonempty, so we can definitely advance to character 1
(prefix-continue str 1))
;; given a string and the position of the first unexamined character,
;; return the prefix that matches the specification.
(define (prefix-continue str posn)
(cond [(<= (string-length str) posn)
;; string is over, return it:
str]
[else
(match (string-ref str posn)
[(? char-upper-case?) (substring str 0 (add1 posn))] ; stop
[(? char-lower-case?) (prefix-continue str (add1 posn))] ; continue
[other (error 'abbrev-continue "unexpected character: ~v" other)])]))
(define examples
'[(alfa alfa)
(Alfa Alfa)
(DiCAp DiC)
(BRaVo BR)
(b b)
(B B)])
(require rackunit)
(for-each (λ (x) (check-equal? (prefix (~a (first x))) (~a (second x)))) examples)
Amazing display, well beyond my expectations! Thank you very much!
A lot of material to study. A sad point: even after reading this or that, the Racket Guide including, I discover important bits and pieces I never seen before. Much less tinkered with.
You are reading too much into this. Itâs not a programming construct. Itâs just a âtextual commentâ, saying that you have not specified what should be done in that case. You wrote:
Input: a nonempty string of characters without spaces
Output: a prefix of the input string defined as follows:
First character goes into the prefix regardless of case
If the next character is lowercase, it goes to the prefix
If the next character is uppercase, it goes to the prefix, and the prefix is done and returned.
But this specification is incomplete. What if the next character is a number -- what should happen? Matthias didnât know what should be done, so he left a comment saying that heâs implementing that case in a way that makes sense to him, but might not be what you ultimately want if/when you refine your specification. He was trying to be concise by using the S-expression comment there, but if that causes so much confusion, perhaps this might help?
(define (prefix str)
(define char* (string->list str))
(let while ([char* (rest char*)] [result (list (first char*))])
(cond
[(empty? char*) (list->string (reverse result))]
[else
(define c (first char*))
(cond
[(char-upper-case? c) (list->string (reverse (cons c result)))]
[(char-lower-case? c) (while (rest char*) (cons c result))]
[else
;; This case is unspecified in the problem statement.
;; Here, we just ignore the character
(while (rest char*) result)])])))
EDITED: the else case ignores the char, not treating it like lower case.
As an aside to this, I am partial to using the sexpr comments to help me remember non-keyword arguments that might not be obvious at first--a habit which I picked up from reading other people's code:
(At lest, until you get into multi-codepoint extended grapheme clusters like when dealing with combinining characters; Racket's regular expressions don't have an atom to match one of those like perl's \X. Hmm. Maybe I should work on a PCRE2 binding library...)
There are so many things that PCRE regular expression dialect supports that Racket ones don't that it's easier to just use it than trying to reimplement everything I want in a codebase I'm not familiar with.