Announcement

Collapse
No announcement yet.

Substrings from Offsets in a string

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Michael Mattias
    replied
    You know one sure sign a program has been changed "too many" times?

    A number of large IF ... ELSEIF...ELSEIF...ELSEIF blocks appearing multiple times.

    If you were going to have to support that many options to start with, surely you would have used either a SELECT CASE [ or EVALUATE in COBOL] block and/or arrays [tables in COBOL]

    That's where I got most of my savings when I reduced the program above. Not to mention, adding a few new items to a table is a heck of a lot easier than extending IF..ELSEIF in mutiple locations within the code.

    Leave a comment:


  • George Deluca
    replied
    Originally posted by Michael Mattias View Post
    > I believe all software has a finite 'maintainable life' measured in "changes." You reach a point where it is easier (read: less expensive) to just start over than it would be to insert additional changes AND know you haven't broken something else along the way.
    Very true, when I was assigned maintenance on a program I'd never seen before, I was faced with this. If the change was much more than trivial, I usually opted for a major cleanup or total rewrite. I'd been burned WAY too many times before debugging someone else's bugs.
    Never told the boss, I'm also a firm believer in "forgiveness is easier than permission".
    George

    Leave a comment:


  • Michael Mattias
    replied
    >can be eliminated by thinking ..

    Thinking solves a lot of problems.

    But only when you think about it.

    Leave a comment:


  • Mike Doty
    replied
    One of the biggest reasons for these rewrites is hard-coded screens.
    My screens are now html so applications may not have to be re-coded.
    Screen management/dialogs should not be hard-coded in my opinion.

    Hard-coding values in a program can be eliminated by thinking to the
    future and loading values at startup. The key is a skeleton/template
    that you follow when starting new projects. I mentioned this years
    ago and got flamed. Boy, we are certainly getting off subject.


    How long is an idea?
    Last edited by Mike Doty; 11 Apr 2008, 08:38 AM. Reason: Added my favorite quote

    Leave a comment:


  • Michael Mattias
    replied
    I really believe this "maintenance life" thing, and when that life is up you are better off just starting over, designing the applications to cleanly support all the features originally wanted, plus those which have been added since then, plus those you want to add now... AND allow for the possibility of still more modifications in the future.

    Too bad today's "managers" don't see this... and therefore refuse to budget resources (programming time) for these rewrites. Just another sign of how short-sighted many businesses are, thinking only of the current quarter's profits, unable to recognize that the object of the business is to have profits for many, many quarters into the future.

    Leave a comment:


  • Fred Harris
    replied
    Works for me Cliff! If it doesn't kill you it'll make you stronger!

    Leave a comment:


  • Cliff Nichols
    replied
    Here Herrre MCM :beerglass: :beerglass:

    I have one now that adding functionality, is playing HAVOC with what "Just works", and thinking "Leave functionality out, till I do a re-write" to add it in

    "Scope-Creep" may kill the programmer, but being able to read your own code, learn from it, and adapt it to the concepts that "Creep" in, is what makes the programmer stronger.

    Leave a comment:


  • Michael Mattias
    replied
    > the tendency is to leave working code alone. ...major tinkering.

    I've seen that enough myself.

    I remember once I had to modify some code (COBOL), which had obviously been updated mulitple times. The program was 1400 lines, many of which were IF.. ELSE.. ELSE.. ELSE... which were testing the same things over an over, and because COBOL does not have an "ELSEIF", the indenting made the code really hard to read...e.g.

    Code:
      IF  condition 
          dosomething
      ELSE
        IF condition
            dosomething
        ELSE
            IF condition
               dosomething
            ELSE
                IF condition
                   dosomething
                ELSE
                 ...
    So, starting with those 1400 lines of code, I added the client's changes and restructed the program to clean out all the duplicate code ..... new size was 360 lines. Just a bit easier to read and follow - AND maintain going forward. But it was quite an effort.

    I believe all software has a finite 'maintainable life' measured in "changes." You reach a point where it is easier (read: less expensive) to just start over than it would be to insert additional changes AND know you haven't broken something else along the way.

    Leave a comment:


  • George Deluca
    replied
    Originally posted by Bob Zale View Post
    George--

    The truth is, if you just continue to take a reasonable approach to a problem, PowerBASIC will give you extraordinarily good performance. There's very little need to ever try to "trick" the compiler -- that usually backfires. Just take the obvious approach and I'll think you'll be very pleased with the results.

    There's very little need to ever worry about an extra temp variable or two. PowerBASIC optimizes most out anyway... but even if there were ten or twenty per iteration, that would still only amount to a fraction of the time it takes to draw that text pixel-by-pixel on a graphical screen.

    And remember, a STRING and a FIELD are two entirely different animals -- one's 4 bytes long, the other is 16. Though you can assign one to the other at any time, they cannot be "substituted" arbitrarily at the binary level.

    Anyway, please ask questions! We'll try to help!

    Bob Zale
    PowerBASIC Inc.
    Thanks Bob, as I said, it seems this is mostly paranoia on my part, based on an ingrained disbelief of todays computer speeds. Obviously from now on I should just go ahead and try something and ask for help when itdoesn't perform well enough, rather than anticipating problems that might not even be there.

    Michael: Yes, maybe data restructuring might be in order, but when adding functions to an App, the tendency is to leave working code alone. In this case it would be major tinkering. And yes, I'm well aware of spaghetti code and its problems, but for me this is. a hobby.

    George

    Leave a comment:


  • Bob Zale
    replied
    George--

    The truth is, if you just continue to take a reasonable approach to a problem, PowerBASIC will give you extraordinarily good performance. There's very little need to ever try to "trick" the compiler -- that usually backfires. Just take the obvious approach and I'll think you'll be very pleased with the results.

    There's very little need to ever worry about an extra temp variable or two. PowerBASIC optimizes most out anyway... but even if there were ten or twenty per iteration, that would still only amount to a fraction of the time it takes to draw that text pixel-by-pixel on a graphical screen.

    And remember, a STRING and a FIELD are two entirely different animals -- one's 4 bytes long, the other is 16. Though you can assign one to the other at any time, they cannot be "substituted" arbitrarily at the binary level.

    Anyway, please ask questions! We'll try to help!

    Bob Zale
    PowerBASIC Inc.

    Leave a comment:


  • Bob Zale
    replied
    Mike--

    You're very perceptive, and I think you have the best approach.

    Best regards,

    Bob Zale
    PowerBASIC Inc.

    Leave a comment:


  • John Petty
    replied
    Originally posted by Fred Harris View Post
    Should this not be even more efficient than Mid$()?
    Unless DrawText() creates tmp strings in its processing, none were used by me.
    Fred you are correct, DrawText() does not use OLE Strings, just a buffer and a number of characters, depending on the use it can be slightly slower than TextOut (which PB actually uses) as it does formatting ie expanding tabs, justification etc inside a given rectangle.
    Mike Doty
    I think going with the compiler instead of the API is better because in
    the future if the compiler is updated the code is updated with it.
    To eliminate temp strings the best is?
    Actually Mike no, it is the other way around DrawText and TextOut are both API functions, upgrades to the compiler don't change API functions, changes to the OS do that, however one would expect the compiler to upgrade to match changes in the OS. As M$ has a pretty good policy on backward compatability the compiler has lots of time to catch up, ie in PB the very limited assembler instruction set (yes I know thats a hardware example not OS). Simple answer to your question, use the API.
    John

    Leave a comment:


  • Michael Mattias
    replied
    Is is possible that the design itself - using "one big string" and requiring substrings - could be changed to use an array() of the substrings? That would end the debate for sure.

    Leave a comment:


  • Fred Harris
    replied
    Well, you don't have to Thank me for this George, because as you say, programming challenges get our blood flowing. So I couldn't resist trying this what with all the talk and grief with temp string creation and so forth.
    Here's my technique with no new memory allocations, and no movement of bytes from the original 'given' string to some other location. Should this not be even more efficient than Mid$()?

    From your original post you said you had a big string composed of sub strings, and for each substring you had a length and offset.

    So in fnWndProc_OnPaint(...) below I build up a main string like this...

    Twenty five
    Twenty fiveFifty
    Twenty fiveFiftySeventy five
    Twenty fiveFiftySeventy fiveOne hundred etc

    I had code already to create the strings from integers. I just ran 25 to 500 through a loop concatenating all the way. During the concatenation in the loop I saved the lengths and offsets.

    Now, in the creation of the strings piles of memory thrashing and temp strings would have been involved - but you said in your program that that was a given or input.

    In the DrawText() output I just printed each string through its offset address and lengths. Unless DrawText() creates tmp strings in its processing, none were used by me.

    Try this..
    (it only took an hour)
    Code:
    #Compile Exe
    #Include "Win32api.inc"
    
    Type WndEventArgs
      wParam               As Long
      lParam               As Long
      hWnd                 As Dword
      hInst                As Dword
    End Type
    
    
    Function OneDigit$ (strNum As String)
      Select Case strNum
        Case "0"
          OneDigit$ = "zero"
        Case "1"
          OneDigit$ = "one"
        Case "2"
          OneDigit$ = "two"
        Case "3"
          OneDigit$ = "three"
        Case "4"
          OneDigit$ = "four"
        Case "5"
          OneDigit$ = "five"
        Case "6"
          OneDigit$ = "six"
        Case "7"
          OneDigit$ = "seven"
        Case "8"
          OneDigit$ = "eight"
        Case "9"
          OneDigit$ = "nine"
      End Select
    End Function
    
    
    Function TwoDigits$ (strNum As String)
      If Left$(strNum, 1) = "1" Then
         Select Case Right$(strNum, 1)
           Case "0"
             TwoDigits$ = "ten"
           Case "1"
             TwoDigits$ = "eleven"
           Case "2"
             TwoDigits$ = "twelve"
           Case "3"
             TwoDigits$ = "thirteen"
           Case "4"
             TwoDigits$ = "fourteen"
           Case "5"
             TwoDigits$ = "fifteen"
           Case "6"
             TwoDigits$ = "sixteen"
           Case "7"
             TwoDigits$ = "seventeen"
           Case "8"
             TwoDigits$ = "eighteen"
           Case "9"
             TwoDigits$ = "nineteen"
         End Select
      Else
         Select Case Left$(strNum, 1)
           Case "0"
             TwoDigits$=OneDigit$(Right$(strNum, 1))
           Case "2"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "twenty "
             Else
                TwoDigits$ = "twenty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "3"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "thirty "
             Else
                TwoDigits$ = "thirty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "4"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "fourty "
             Else
                TwoDigits$ = "fourty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "5"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "fifty "
             Else
                TwoDigits$ = "fifty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "6"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "sixty "
             Else
                TwoDigits$ = "sixty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "7"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "seventy "
             Else
                TwoDigits$ = "seventy " + OneDigit$(Right$(strNum, 1))
             End If
           Case "8"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "eighty "
             Else
                TwoDigits$ = "eighty " + OneDigit$(Right$(strNum, 1))
             End If
           Case "9"
             If Right$(strNum, 1) = "0" Then
                TwoDigits$ = "ninety "
             Else
                TwoDigits$ = "ninety " + OneDigit$(Right$(strNum, 1))
             End If
         End Select
      End If
    End Function
    
    
    Function ThreeDigits$ (strNum As String)
      Local strFirst As String
    
      Select Case Left$(strNum, 1)
        Case "1"
          strFirst="one hundred"
        Case "2"
          strFirst="two hundred"
        Case "3"
          strFirst="three hundred"
        Case "4"
          strFirst="four hundred"
        Case "5"
          strFirst="five hundred"
        Case "6"
          strFirst="six hundred"
        Case "7"
          strFirst="seven hundred"
        Case "8"
          strFirst="eight hundred"
        Case "9"
          strFirst="nine hundred"
      End Select
      If Right$(strNum,2)="00" Then
         ThreeDigits$=strFirst
      Else
         ThreeDigits$=strFirst & " and " & TwoDigits(Right$(strNum,2))
      End If
    End Function
    
    
    Function GetAlphaNumber(iNum As Long) As String
      Local strCulls As String, strNumberCulls As String, strTmp As String
      Local iLenStrCulls As Integer
    
      strCulls = Str$(iNum)
      iLenStrCulls = Len(strCulls)
      Select Case iLenStrCulls
        Case 2
          strTmp = OneDigit$(Right$(strCulls, 1))
        Case 3
          strTmp = TwoDigits$(Right$(strCulls, 2))
        Case 4
          strTmp = ThreeDigits$(Right$(strCulls, 3))
      End Select
      Mid$(strTmp, 1, 1) = UCase$(Mid$(strTmp, 1, 1))
    
      GetAlphaNumber = strTmp
    End Function
    
    
    Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long
      Local lpCreateStruct As CREATESTRUCT Ptr
    
      lpCreateStruct=Wea.lParam   'Can use GetModuleHandle() here instead, or, use Global (Hate Globals!)
      [email protected]
    
      fnWndProc_OnCreate=0
    End Function
                     
    
    Function fnWndProc_OnPaint(Wea As WndEventArgs) As Long
      Local lpPaint As PAINTSTRUCT
      Local strMain,strTmp As String
      Local iLen(),iOffset() As Long
      Register i As Long
      Local hDC As Long
      Local rc As RECT
      
      Redim iLen(20),iOffset(20) As Long
      For i=0 To 19
        strTmp=GetAlphaNumber((i+1)*25)      'For i=0 strTmp will hold "Twenty five"
        strMain=strMain+strTmp               'Keep concatenating each number converted to a string together
        iLen(i)=Len(strTmp)                  'end to end.  Store Len(i) of each string
        If i Then
           iOffset(i)=iOffset(i-1)+iLen(i-1)
        End If
      Next i
      hDC=BeginPaint(Wea.hWnd,lpPaint)
      Call SetBkMode(hDC,%TRANSPARENT)
      rc.nLeft=0 : rc.nRight=500
      For i=0 To 19
        rc.nBottom=rc.nTop+20
        Call DrawText(hDC,Byval (Strptr(strMain)+iOffset(i)),iLen(i),rc,%DT_SINGLELINE)
        rc.nTop=rc.nTop+20
      Next i
      Call EndPaint(Wea.hWnd,lpPaint)
      Erase iLen,iOffset
    
      fnWndProc_OnPaint=0
    End Function
    
    
    Function fnWndProc_OnDestroy(Wea As WndEventArgs) As Long
      Call PostQuitMessage(0)
      fnWndProc_OnDestroy=0
    End Function
    
    
    Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
      Static Wea As WndEventArgs
    
      Select Case As Long wMsg
        Case %WM_CREATE
          Wea.hWnd=hWnd : Wea.wParam=wParam : Wea.lParam=lParam
          fnWndProc=fnWndProc_OnCreate(Wea)
          Exit Function
        Case %WM_PAINT
          Wea.hWnd=hWnd : Wea.wParam=wParam : Wea.lParam=lParam
          fnWndProc=fnWndProc_OnPaint(Wea)
          Exit Function
        Case %WM_DESTROY
          Call PostQuitMessage(0)
          fnWndProc=fnWndProc_OnDestroy(Wea)
          Exit Function
      End Select
    
      fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
    End Function
    
    Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
      Local winclass As WndClassEx
      Local szAppName As Asciiz*16
      Local Msg As tagMsg
      Local hWnd As Dword
    
      szAppName="StrangeStuff"
      winclass.cbSize=SizeOf(winclass)
      winclass.style=%CS_HREDRAW Or %CS_VREDRAW
      winclass.lpfnWndProc=CodePtr(fnWndProc)
      winclass.cbClsExtra=0
      winclass.cbWndExtra=0
      winclass.hInstance=hIns
      winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
      winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
      winclass.hbrBackground=%COLOR_BTNFACE+1
      winclass.lpszMenuName=%NULL
      winclass.lpszClassName=VarPtr(szAppName)
      Call RegisterClassEx(winclass)
      hWnd=CreateWindow(szAppName,"StrangeStuff",%WS_OVERLAPPEDWINDOW,200,100,525,500,0,0,hIns,ByVal 0)
      Call ShowWindow(hWnd,iShow)
      While GetMessage(Msg,%NULL,0,0)
        TranslateMessage Msg
        DispatchMessage Msg
      Wend
    
      Function=msg.wParam
    End Function

    Leave a comment:


  • Mike Doty
    replied
    Actually, this is George's posting.
    I think going with the compiler instead of the API is better because in
    the future if the compiler is updated the code is updated with it.
    To eliminate temp strings the best is?

    Leave a comment:


  • Michael Mattias
    replied
    Which is best?

    Depends on what "best" means.

    Which in turn, depends on context.

    Which in turn means, "ALL OPTIMIZATION IS APPLICATION-SPECIFIC"

    In your case, since the substring you need must be passed to a GRAPHICS statement which requires a dynamic string as a parameter, at some point that dynamic string must be created, period.

    You can create it with your code with MID$() or PEEK$(). OR, you can let the compiler create it in the background as required from a working buffer. Six of one, half of dozen the other.

    Leave a comment:


  • Mike Doty
    replied
    Saying these are all correct. Which is the best?

    Code:
    'TestCopyMemory.bas
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "win32api.inc"
    FUNCTION PBMAIN () AS LONG
       LOCAL pTo       AS BYTE PTR
       LOCAL pFrom     AS BYTE PTR
       LOCAL sTo       AS STRING
       LOCAL sFrom     AS STRING
       LOCAL x         AS LONG
       LOCAL Length    AS LONG
     
       FOR x = 1 TO 5
         sFrom  =  "123"  'init value
         Length = LEN(sFrom)
         sTo    =   STRING$(9,"-")    'init value
         pFrom  = 0
         pTo    = 0
         ON x GOSUB Test1,test2,test3,test4,test5  'don't see this every day
         ? sTo
       NEXT
       WAITKEY$
    EXIT FUNCTION
    Test1:
       pFrom   =       STRPTR(sFrom)              'Method 1  CopyMemory
       pTo     =       STRPTR(sTo)
       CopyMemory      pTo, pFrom, Length
    RETURN
    Test2:
       MID$(sTo,1)    = MID$(sFrom,1,Length)      'Method 2  MID$
    RETURN
    Test3:
       pFrom = STRPTR(sFrom)
       sTo = PEEK$(pFrom, Length)                 'Method 3  PEEK$ (wrong)
    RETURN
    Test4:
       pFrom   =       STRPTR(sFrom)              'Method 4  POKE$/PEEK$
       pTo     =       STRPTR(sTo)                
       POKE$ pTo, PEEK$(pFrom, Length)
    RETURN
    Test5:
       LSET ABS sTo = sFrom                       'Method 5  Don't pad
    RETURN
    END FUNCTION
    Last edited by Mike Doty; 9 Apr 2008, 01:19 PM. Reason: All values were not init, STRPTR needed.

    Leave a comment:


  • George Deluca
    replied
    Originally posted by Bob Zale View Post
    Hi George--

    "Cloning my SUBs and altering the passed parameter from STRING to FIELD works perfectly, so obviously FIELDed variables act just like normal strings (which is what they're supposed to)."

    That is not a correct inference. At some point, this will fail. Dramatically.

    Bob Zale
    PowerBASIC Inc.
    OK Bob, I'll bow to the expert, but GRAPHIC PRINT seems to have no problem handling the passed FIELD variable as input. So far ... )

    I have the new code plugged into my main app now, and I almost hate to say this after all the work various people here have done, but I tried swapping from using FIELD, which seemingly worked OK, to the original method of using MID$ and frankly I can detect no external speed difference. Maybe PB's string handling is just too efficient.

    I guess growing up in the programming of 40 years ago, where we'd code critical routines with the hardware timing manual open, I can't get used to the incredible speed of todays systems and I'm always fretting over the 'cost' of various routines.

    For those who experimented with routines etc. many thanks, I learned even more. I'm also not imune to programming challenges, so I know why you did it.

    I'll heed Bob Zales warning, but I wish I understood better what he means by 'fail, disastously'.

    Thanks again to all of you.
    George

    Leave a comment:


  • John Petty
    replied
    Originally posted by Bob Zale View Post
    How soon they forget... {smile}

    [/B]The ASCIIZ method method above will use two (2) temp strings and three (3) copy operations of the text.

    Best of luck,

    Bob Zale
    PowerBASIC Inc.
    Bob what documentation did I forget?
    The following compiles fine
    Code:
    FUNCTION PBMAIN () AS LONG
    
        LOCAL t AS ASCIIZ *100000
        LOCAL s AS STRING
        s = SPACE$(1000)
        t = MID$(s, 100,100) + CHR$(0)
    END FUNCTION
    so I would need to decompile to determine that there was a surprising number of temp strings used in the middle, in which case Micheals code surely would be faster.
    The requirement for printing the text to a graphic to be an OLE string must be a PB implementation. TextOutA requires a pointer to a buffer and the number of characters not a pointer to an OLE string.
    If speed is the issue in this application then by using Windows API functions there isn't even any need to extract the substring, merley pass a pointer to the first character and the number of characters (obviously with the other paramaters such as Hdc etc after having set the text format)
    John

    Leave a comment:


  • jcfuller
    replied
    George,
    I'm not sure how your main string is parsed but this might give you another alternative especially if there are MANY calls to your subs.

    James

    Code:
    '=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
    'SED_PBCC
    '=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
    #COMPILE EXE
    #DIM ALL
    '==============================================================================
    SUB TestIt(S3 AS STRING )
        PRINT "TestIt -> "; S3 
    END SUB
    
    '==============================================================================
    FUNCTION PBMAIN () AS LONG
        LOCAL S1,S2 AS STRING
        LOCAL WhereIsIt() AS DWORD
        LOCAL idx,dwIdx,i AS LONG
        LOCAL bPtr1,bPtr2 AS BYTE PTR
        
        LOCAL dwPtr AS DWORD PTR
        DIM WhereIsIt(100)
        
        
        
        S1 = "One,Two,Three,Four,Five"
        S2 = STRING$(1000,0)
        bPtr1 = STRPTR(S1)
        dwPtr = STRPTR(S2)
        bPtr2 = dwPtr + 4
        
        WhereIsIt(0) = bPtr2
        
        DO      
            IF @bPtr1 = 44 OR @bPtr1 = 0 THEN
                @dwPtr = idx            
                dwPtr = bPtr2            
                bPtr2 = dwPtr+4
                idx = 0
                INCR dwIdx
                WhereIsIt(dwIdx) = bPtr2                        
                IF @bPtr1 = 0 THEN
                    EXIT LOOP
                END IF    
                INCR bPtr1
                ITERATE
            END IF
            @bPtr2 = @bPtr1
            INCR bPtr1
            INCR bPtr2
            
            INCR idx 
        LOOP
    
        FOR i = 0 TO 4
            TestIt BYVAL VARPTR(WhereIsIt(i))
        NEXT i
        WAITKEY$
    END FUNCTION

    Leave a comment:

Working...
X