Day 024: LCASE$
#1
Not much to say here. LCASE$ just converts any uppercase characters in a string to lowercase. LCASE$(mystring$)

The most obvious use for LCASE$ is an input routine...

Code: (Select All)
PRINT "Was this information helpful? Y/N"; ans$
DO
    _LIMIT 30
    mykey$ = INKEY$
    IF LCASE$(mykey$) = "y" THEN PRINT "You darn tootin' it waz ya mangy polecat!"
LOOP

The code above allows the user to type either "y" or "Y" to get a printed response."

Anyway, not much ELSE to see here; besides, it's Sunday, and God takes this day off. Me, I take a 1/2 day! Big Grin

Pete
Reply
#2
This recalls something. If the QB64 IDE could capitalize keywords, why not offer a keyword in the language which does it to a given string? I just did the search function on this forum and was unable to find a routine that does it. I'm sure it was done a few times... by Steve.

Otherwise "LCASE$()" is useful for programs written in BASIC that create C or Lua code, or for any other programming language that must have all keywords in lowercase. There are a surprising amount of programs out there that invent a Lua script out of something else, such as the "Brain___k" interpreter. "UCASE$()" is helpful for reconstructing classic COBOL and Fortran programs LOL.

Use one or the other where letter case shouldn't matter, especially for those lovers of forums unwilling to use [CAPS-LOCK] nor [SHIFT] for any of their writing...
Reply
#3
(12-04-2022, 12:01 PM)mnrvovrfc Wrote: If the QB64 IDE could capitalize keywords, why not offer a keyword in the language which does it to a given string?

The IDE isn't doing any clever captalisation processing with keywords, it just has a list of what they should be: https://github.com/QB64-Phoenix-Edition/...ctions.bas
Reply
#4
One Steve Note with regards to LCASE$ and UCASE$ -- They're string manipulation functions, and as such, they're relatively sloooow in processing.  Judicious and limited use of them is advised whenever they may be called upon heavily.

Let me give an example of what I'm speaking of:
Code: (Select All)
FOR x = 0 TO UBOUND(Array)
    FOR y = 0 TO UBOUND(Array)
        IF Lcase$(Array(x)) < Lcase$(Array(y)) THEN SWAP Array(x), Array(y)
    NEXT
NEXT


Now, the above is a yucky bubble sort to begin with, but let's overlook that as it's just for the sake of an example.  Inside that nasty bubble sort, we're using LCASE$ over and over and over and over and over some more, just to compare and sort our array.  All these calls to LCASE$ are going to end up bogging down an already slow process (bubble sorts are quick and easy to write, but not very efficient at all) -- maybe pushing it to the point of unuseability.  After all, who wants to sit there for an hour, waiting on some list to sort itself out in alphabetical order??

A better solution, in a case like this, is to minimize the usage of that slow processing string command, as much as possible.
Code: (Select All)
DIM tempArray(UBOUND(Array)) AS STRING
FOR i = 0 TO UBOUND(Array)
    tempArray(i) = LCASE$(Array(i))
NEXT

 FOR x = 0 TO UBOUND(tempArray)
    FOR y = 0 TO UBOUND(tempArray)
        IF tempArray(x) < tempArray(y) THEN SWAP tempArray(x), tempArray(y)
    NEXT
NEXT

FOR i = 0 TO UBOUND(Array)
    Array(i) = tempArray(i)
NEXT


More coding for the programmer, but the difference in performance is going to be very noticeable.  Here, I simply made a temp array to hold all the elements of our normal Array, and I made them all lowercase *ONCE* for comparison usage.  The first routine made calls to LCASE$ twice each loop, and repeated that same process over again a second time for the outer loop.

UBOUND(Array) number of calls to LCASE$ in the second set of code.
2 * UBOUND(Array) * UBOUND(Array) number of calls to LCASE$ in the first set of code.

Say there's 1,000,000 elements in that array...  The second routine calls LCASE$ 1,000,000 times...   The first calls it 2,000,000,000,000 times!!

Since it's already a generally slow process due to being string manipulation, can you imagine the difference in the speed and performance in those two routines??

Use LCASE$ and UCASE$ when you need them; just use them wisely and judiciously -- especially if your program is running slower than you'd like and you need to improve speed and performance with it.
Reply
#5
Great point Steve. Set a reminder when we get to MID$() replacing characters in a string is also much, much faster than replacing the entire string. I had a C/C++ keyinput routine, which I put together many years ago,  that proved that out. Foggy on the details now, for sure, but I can recall how happy I was to see the performance improvement. Now in regard to the extra coding, yes, that can be a royal PITA, but in a big project it always pays off. Nothing worse that putting together 10,000 lines of code, pressing F5 and watching an episode of Ben Stein Presents Word Processing. To you game developers here, that would boring enough without Ben

Pete
Reply
#6
(12-04-2022, 01:48 PM)SMcNeill Wrote: More coding for the programmer, but the difference in performance is going to be very noticeable.  Here, I simply made a temp array to hold all the elements of our normal Array, and I made them all lowercase *ONCE* for comparison usage.  The first routine made calls to LCASE$ twice each loop, and repeated that same process over again a second time for the outer loop.
:
Use LCASE$ and UCASE$ when you need them; just use them wisely and judiciously -- especially if your program is running slower than you'd like and you need to improve speed and performance with it.
In some cases might have to do two swaps and leave out the second transfer of array elements, discarding the "temparray" because otherwise the letter-case of the elements of "array" is destroyed only for the sake of doing fast comparisons. Only compare elements of "temparray" but swap things in both arrays to keep them in sync.
Reply
#7
So brainstorming a bit here... which reminds me, buy Flex Tape...

The QB64 IDE keeps your entire code as a string, if I remember correctly. So I wanted to see what would be the faster way to search our code with LCASE$()

Code: (Select All)
' Method #1
a$ = "Dog Cat Rabbit Moose Elephant Bear Tiger Fish "
search$ = "moose"

IF INSTR(LCASE$(a$), search$) THEN PRINT "Found: "; search$ ELSE PRINT "No search results.."

' Method #2
DO
    j% = INSTR(seed%, a$, " ")
    IF j% = 0 THEN EXIT DO
    IF LCASE$(MID$(a$, seed%, j% - seed%)) = search$ THEN EXIT DO
    REM PRINT "|" + LCASE$(MID$(a$, seed%, j% - seed%)) + "|", search$ ' Show parsing...
    seed% = j% + 1
LOOP

IF j% THEN PRINT "Found: "; search$ ELSE PRINT "No search results.."

So I did some speed tests and it appears Method #1 is the winner by a factor of 5 or more.

Waiting for Steve to post... Why Method #3, of course!... and give the IDE method or somethong he came up with. (Spelling mistake intentional).

Pete
Reply
#8
(12-04-2022, 05:57 PM)mnrvovrfc Wrote:
(12-04-2022, 01:48 PM)SMcNeill Wrote: More coding for the programmer, but the difference in performance is going to be very noticeable.  Here, I simply made a temp array to hold all the elements of our normal Array, and I made them all lowercase *ONCE* for comparison usage.  The first routine made calls to LCASE$ twice each loop, and repeated that same process over again a second time for the outer loop.
:
Use LCASE$ and UCASE$ when you need them; just use them wisely and judiciously -- especially if your program is running slower than you'd like and you need to improve speed and performance with it.
In some cases might have to do two swaps and leave out the second transfer of array elements, discarding the "temparray" because otherwise the letter-case of the elements of "array" is destroyed only for the sake of doing fast comparisons. Only compare elements of "temparray" but swap things in both arrays to keep them in sync.

Aye.  The way I have it would turn your original array all lowercase.  The alternative is, as you suggest:

Code: (Select All)
DIM tempArray(UBOUND(Array)) AS STRING
FOR i = 0 TO UBOUND(Array)
    tempArray(i) = LCASE$(Array(i))
NEXT

 FOR x = 0 TO UBOUND(tempArray)
    FOR y = 0 TO UBOUND(tempArray)
        IF tempArray(x) < tempArray(y) THEN SWAP tempArray(x), tempArray(y): SWAP Array(x), Array(y)
    NEXT
NEXT

If this is still slow for you (and it's not the bubble sorts fault...), then the trick would be the make an index to reference the arrays with, and then you just swap the index values while leaving the arrays intact until you exit the sort routine, and then you build the array in the order you like.  (But that's more of a lesson on data indexing/manipulation than it is on LCASE$, so we'll save going into it for another day.)  Wink
Reply
#9
(12-04-2022, 06:47 PM)Pete Wrote: So brainstorming a bit here... which reminds me, buy Flex Tape...

The QB64 IDE keeps your entire code as a string, if I remember correctly. So I wanted to see what would be the faster way to search our code with LCASE$()

Code: (Select All)
' Method #1
a$ = "Dog Cat Rabbit Moose Elephant Bear Tiger Fish "
search$ = "moose"

IF INSTR(LCASE$(a$), search$) THEN PRINT "Found: "; search$ ELSE PRINT "No search results.."

' Method #2
DO
    j% = INSTR(seed%, a$, " ")
    IF j% = 0 THEN EXIT DO
    IF LCASE$(MID$(a$, seed%, j% - seed%)) = search$ THEN EXIT DO
    REM PRINT "|" + LCASE$(MID$(a$, seed%, j% - seed%)) + "|", search$ ' Show parsing...
    seed% = j% + 1
LOOP

IF j% THEN PRINT "Found: "; search$ ELSE PRINT "No search results.."

So I did some speed tests and it appears Method #1 is the winner by a factor of 5 or more.

Waiting for Steve to post... Why Method #3, of course!... and give the IDE method or somethong he came up with. (Spelling mistake intentional).

Pete

Method #3:  LCASE$ Once only

Code: (Select All)
' Method #1
a$ = "Dog Cat Rabbit Moose Elephant Bear Tiger Fish "
search$ = "moose"

If InStr(LCase$(a$), search$) Then Print "Found: "; search$ Else Print "No search results.."


' Method #2
Do
    j% = InStr(seed%, a$, " ")
    If j% = 0 Then Exit Do
    If LCase$(Mid$(a$, seed%, j% - seed%)) = search$ Then Exit Do
    Rem PRINT "|" + LCASE$(MID$(a$, seed%, j% - seed%)) + "|", search$ ' Show parsing...
    seed% = j% + 1
Loop

If j% Then Print "Found: "; search$ Else Print "No search results.."

'method #3: 'the LCASE$ once method

b$ = LCase$(a$)
Do
    j% = InStr(seed%, b$, " ")
    If j% = 0 Then Exit Do
    If Mid$(b$, seed%, j% - seed%) = search$ Then Exit Do
    Rem PRINT "|" + LCASE$(MID$(a$, seed%, j% - seed%)) + "|", search$ ' Show parsing...
    seed% = j% + 1
Loop
b$ = "" 'free up all that massively backed up data, since this isn't in a SUB/FUNCTION which woul do so for us at exit time

If j% Then Print "Found: "; search$ Else Print "No search results.."

The QB64 IDE has a slightly more complex structure than what you're using.  It stores all your info as:  length, data, length

So your example data would be:

"3Dog33Cat36Rabbit68Elephant8..."

So to start with, it'd read your length of 3, then 3bytes for "Dog", and it'd skip forward one to bypass the second "3".
For the next line in the IDE, it'd read the length of 3, the 3 bytes for "Cat", and then skip forward over the next "3".

So why does it have that extra 3, or 6, or whatever the word length is in there, if it's just going to skip it??

For searching/reading in a backwards order.  Start from the right, read the count, then back up and read that number of letters.



One thing to keep in mind with your comparison methods -- you're comparing apples and apple sauce.

Method one simply looks for a pattern inside a whole string.  If you had your buddy, "Mooser", in the list, the search would find him and flag him for "moose"  Now, of course, you could change your search term to "moose ", but then you'd miss out on "moose, cat, dog"...   So now you have to expand to search for term + break characters, only to get glitched up when moose comes at the end of a line and is mo-ose separated..."  So now you have to look for hypens and determine if they're concatenators or just dashes or maybe subtraction or negative symbols...

Whereas method two breaks things down to individual elements to begin with.  In this case, you're using spaces as valid separators, but that's easily expanded to add what characters you need.  Space + ",.;-_" + any others you feel are valid...

So which method is faster at the end of the day?  I'd say that all depends on how complex your use-case is with your code.  Sometimes you can get away with something as simple as efficient as Pete's Example #1.  Other times, your data requires much more careful analysis, and in those cases, I'd find it impossible to declare *any* method the fastest, without having the specific data and test methods in front of me to compare actual results on.  Wink
Reply
#10
@SMcNeill

So something along the lines of this using indexing. Interesting...

Code: (Select All)
search$ = "moose"
a$ = "Dog Cat Rabbit Moose Elephant Bear Tiger Fish "

' Create the indexing for this demo...
FOR i = 0 TO LEN(a$)
    j = j + 1: last = j
    IF MID$(a$, i, 1) = " " OR i = 0 THEN
        cnt = cnt + 1
        REDIM _PRESERVE index(cnt), size(cnt)
        index(cnt) = i + 1
        size(cnt - 1) = j - 1: j = 0
    END IF
NEXT
size(cnt) = last - 1

' For fun, print the index and size values...
FOR i = 1 TO UBOUND(index)
    PRINT i; index(i); size(i)
NEXT

' Read and search using this indexing method.
i = 0
DO
    i = i + 1
    IF search$ = LCASE$(MID$(a$, index(i), size(i))) THEN found = 1: EXIT DO
LOOP
IF found THEN PRINT "Found: "; search$ ELSE PRINT "No search results.."


Pete
If eggs are brain food, Biden takes his scrambled.
Reply




Users browsing this thread: 4 Guest(s)