Announcement

Collapse
No announcement yet.

Confessions of a sloppy programmer

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

  • Bernard Chapman
    replied
    Charles,

    You are right. Immediately I posted this code I tried Lance's suggestion of UBOUND() immediately after the call to the procedure that redimensioned it. With REDIM, UBOUND() gave 10 and 20. With DIM it was 10 and 10.

    I tried to edit my posting to include this information but infuriatingly I could not get back on to the site except at incredibly slow speed that effectively stopped downloading of pages.

    By the way does anyone know how to implement word wrap in this edit box I am typing in in this BBS.

    Regards,

    Bern


    ------------------
    Bern

    www.insighttrading.com.au

    Leave a comment:


  • Charles Dietz
    replied
    Bern,

    The reason your example doesn't work with DIM is that the array can only
    be dimensioned once, and it was dimensioned at ns = 10. When elements
    of the array greater than 10 are referenced in the second call, then a GPF
    error occurs. If ns is reset to something less than 10, say 8, it works fine.

    ------------------


    [This message has been edited by Charles Dietz (edited August 29, 2001).]

    Leave a comment:


  • Bernard Chapman
    replied
    Lance,

    Further to the REDIM question above:

    Code:
    #COMPILE EXE
    #DEBUG ERROR ON
    
    GLOBAL myarray() AS STRING*15,ns AS LONG
    
    FUNCTION PBMAIN() AS LONG
    LOCAL i AS LONG,txt AS STRING
    
    ns=10
    
    CALL myprocedure
    
    txt=""
    FOR i=1 TO ns
        txt=txt+"*"+myarray(i)+"*"+$CRLF
    NEXT
    
    MSGBOX txt
    
    ns=20
    
    CALL myprocedure
    
    txt=""
    FOR i=1 TO ns
        txt=txt+"*"+myarray(i)+"*"+$CRLF
    NEXT
    
    MSGBOX txt
    
    END FUNCTION
    '....................................................................
    SUB myprocedure
       LOCAL i AS LONG
    
       REDIM myarray(ns)
    
       FOR i=1 TO ns
           myarray(i)="myarray"+STR$(i)
       NEXT
    END SUB
    The above code works perfectly. If you replace REDIM with DIM the second message box shows that something is different with the elements of the array from 11 to 20.

    The difference is that they contain some nulls that prevent the display of what comes after in the txt string. These could be seen if you were to PRINT the elements to file rather than displaying on the message box.

    If you change the #DEBUG ERROR to OFF you will get a GPF which is reassuring as the code is defective.

    Regards,

    Bern

    ------------------
    Bern

    www.insighttrading.com.au

    Leave a comment:


  • Bernard Chapman
    replied
    Tom,

    Many thanks for that in depth explanation. PB has certainly added flexibility here as elsewhere.

    I don't know whether I just have an early version of the manual but it might be worth adding something along these lines to the description of the IF statments.

    I appreciate the response.

    Regards,

    Bern

    ------------------
    Bern

    www.insighttrading.com.au

    Leave a comment:


  • Tom Hanlin
    replied
    Originally posted by Bernard Chapman:
    I would have thought that the integer expression in the first case was:

    LEN(mainstring) AND INSTR(mainstring,mystring)

    and in the second case was:

    (LEN(mainstring) AND INSTR(mainstring,mystring))

    both of which should evaluate to the same integer.
    In early-out evaluation, or short-circuit evaluation, the point is that
    the AND expression doesn't evaluate to an integer. Such evaluation takes
    place in conditional statements (IF, WHILE, etc).

    In most BASICs, there is no early-out evaluation, and conditionals are
    always evaluated fully. This is only guaranteed to be true in PowerBASIC
    if you enclose the conditional in parentheses. Otherwise, the expression
    is handled as Boolean (rather than bitwise) in regards to ANDs, allowing
    certain optimizations.

    Consider the following:

    IF LEN(mainstring) AND INSTR(mainstring,mystring) THEN

    In PowerBASIC, if the length of mainstring is zero... since zero
    ANDed with anything is going to be zero, the compiler knows it doesn't
    have to look any further. At this point, there is no need to evaluate the
    INSTR expression at all-- the result of the IF is going to be FALSE. In
    essence, the IF here translates as:

    IF LEN(mainstring)THEN IF INSTR(mainstring,mystring) THEN

    ...which is not only a convenient optimization, it can be very important
    in cases where the second conditional must not be tested if the first
    failed. For example:

    IF pSource AND @pSource <> 32 THEN

    ...with traditional BASICs, the whole expression would be evaluated,
    causing a GPF if pSource was a null pointer. Of course, traditional BASICs
    don't have pointers, either, but that's the general idea.


    ------------------
    Tom Hanlin
    PowerBASIC Staff

    Leave a comment:


  • Lance Edmonds
    replied
    One more thing, just to clarify the "scanned" statement behavior slightly...

    Consider this code:
    Code:
    FUNCTION MyFunc&()
      DIM A(100) AS GLOBAL LONG
      ...
    END FUNCTION
    Now, if the DIM statement cleared & redimensioned the array every time the Function was called, there would effectively be no retained {global} data because the array would be cleared at the start of each call.

    What actually happens is that the array is created just once (at runtime), and the array is maintained between each call without being cleared/redimensioned or cleared.

    I hope this helps!

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Lance Edmonds
    replied
    There is another aspect that we need to consider here... the DIM statement is probably acting as a "scanned" statement... telling the compiler about the array for the compilation process, but having no effect at runtime.

    Comparitively, REDIM is an executed statement that will create an array (or erase and recreate an existing array) at runtime.

    We would really need to see the exact code to see what you mean by "corrupt" (and explain the behavior that you are seeing), but I'm picking that the code is expected a resized array without verifying the array boundaries.

    Can you post compilable code that demonstrates the effect? However, before doing so, add some code to check the UBOUND() of the array at the point where you think the array corruption is occuring and retest the code.

    Thanks!



    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Bernard Chapman
    replied
    Lance,

    Thanks for the suggestions. I tried various things and here are the results:

    DIM AS STRING leads to a corrupted array
    DIM AS GLOBAL STRING also leads to a corrupted array

    REDIM AS STRING is fine and
    REDIM AS GLOBAL STRING is also fine

    Since the array is scoped as GLOBAL outside of the SUB and not scoped at all inside of the SUB it would be assumed to be the GLOBAL array already declared.

    Seems like you cannot DIM an array again without erasing it first (as you would expect) otherwise the array corrupts.

    Regards,

    Bern


    ------------------
    Bern

    www.insighttrading.com.au

    Leave a comment:


  • Lance Edmonds
    replied
    Regarding the DIM vs REDIM "problem"... it looks to me like you simply had a variable scope problem.

    The "DIM ArrayName()" statement does not include a scope clause, so it defaults to LOCAL scope. However, when you use REDIM, then the compiler picks up on the previously declared GLOBAL array of the same name.

    What you should have done was use the format thus:

    DIM arrayname(isize&) AS GLOBAL STRING

    ...this explicitly sets the scope as GLOBAL. It is advisable to use that format in the REDIM statement too, to ensure that both the compiler and you are 100% in agreement as to the scope.

    When I get back to my DEV PC, I'll dig up some information for you on how short-circuit evaluation works with the AND and OR operators in IF/WHILE/DO statements.


    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Bernard Chapman
    replied
    Lance,

    You are right on both counts. I have been even sloppier than I originally thought:

    2. I did have brackets around:

    LEN(mainstring) AND INSTR(mainstring,mystring)

    as part of a much more complex logical expression. When I simplified it for illustration I left the brackets out. The code below hits the first "hit" message box but not the second so the IF statements are different.


    Code:
    #COMPILE EXE
    
    FUNCTION PBMAIN() AS LONG
    LOCAL mainstring AS STRING, mystring AS STRING
    
    mainstring=" ABC DEF GHI JKL"
    
    mystring = " ABC "
    
    MSGBOX STR$(LEN(mainstring) AND INSTR(mainstring,mystring))
    
    IF LEN(mainstring) AND INSTR(mainstring,mystring) THEN
      MSGBOX "First hit"
    END IF
    
    IF (LEN(mainstring) AND INSTR(mainstring,mystring)) THEN
      MSGBOX "Second hit"
    END IF
    
    END FUNCTION
    However, I am still a bit perplexed as the syntax as described in the documentation states:

    Code:
    IF integer expression [,] THEN
    	{statements}
    .
    .
    END IF
    Remarks: In executing IF block statements, the truth of the integer expression in the initial IF statement is checked first.
    .
    I would have thought that the integer expression in the first case was:

    LEN(mainstring) AND INSTR(mainstring,mystring)

    and in the second case was:

    (LEN(mainstring) AND INSTR(mainstring,mystring))

    both of which should evaluate to the same integer.

    If this not the case I would be interested to learn of the way that "short circuit" evaluation occurs. I do not recall coming across that one before.


    1. Apologies as again you are correct. The simplified pseudocode I posted previously left out the all important brackets on the GLOBAL array MYSTRING and furthermore I should not have had the array in the argument list of myprocedure(). It should have read (in more detail):

    Code:
    '.....................................
    FUNCTION PBMAIN() AS LONG
    GLOBAL mystring() AS STRING*10
    .
    call myprocedure()
    .
    call myprocedure()
    
    END FUNCTION
    ........................................
    SUB myprocedure()
    LOCAL ns AS LONG
    .
    {calculate a new value of ns}
    .
    DIM mystring(ns)
    .
    {fill the string array}
    .
    END SUB
    I should say that the corruption of the array was only partial. Some elements of the array were fine while others had other characters from other arrays including nulls. For the corruption to occur the new value of ns before the second DIM has to be different from the previous one.

    Substituting REDIM for DIM fixed the problem completely.

    This was unequivocally incorrect code and I am surprised that it worked at all. But if you are still interested I could forward you the actual code.

    I appreciate that you have taken the time to field questions of this nature and apologise that my original posting was incorrect.

    Regards,

    Bern

    ------------------
    Bern
    www.insighttrading.com.au

    [This message has been edited by Bernard Chapman (edited August 28, 2001).]

    Leave a comment:


  • Lance Edmonds
    replied
    I'm afraid that I have to ask for some clarifications here, Bernard, because your comments do not match the behavior of PowerBASIC as you indicate.

    <U>Point 1:</U> I tried your code and I don't see any corruption occuring at all, whether you use DIM or REDIM... no difference at all. In either case, your array is a LOCAL, and your scalar string is global. No confusion seems evident. Maybe if you could post or email the exact code you used to confirm the problem, I'de be very interested in seeing it.

    Note that using non-unique names between LOCAL and GLOBAL variables is quite legal and possible, but not recommended as it leads to scope confusion (of the programmer, not the compiler! ).

    <U>Point 2:</U> Your real test code must have included parenthesis around the terms which prevents short-circuit evaluation from occurring... your example, as posted, definitely uses short-circuit evaluation of the AND, not bitwise. Therefore your comments do not match the code you posted. Sorry!

    That said, if you have code that shows this to be incorrect, then post or email it too...

    Thanks!




    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Bernard Chapman
    started a topic Confessions of a sloppy programmer

    Confessions of a sloppy programmer

    Two more traps that I have fallen into recently that might help someone else avoid the same pitfalls.:

    1.)

    No error message is generated by the code below but the array is corrupted. It took a bit of finding. The bad code seemed to work OK under PBDLL 5 but not under PBDLL 6. DIM should of course have been REDIM.

    '.....................................
    FUNCTION PBMAIN() AS LONG
    GLOBAL mystring AS STRING*10
    .
    call myprocedure(mystring)
    .
    call myprocedure(mystring)

    END FUNCTION
    ........................................
    SUB myprocedure(mystring AS STRING*10)
    .
    .
    DIM mystring(100)
    .
    END SUB


    2. Logic shortcuts we frequently use can become second nature to us so we often do not check through the code carefully enough as I was recently caught out.

    To check if a string is not empty we often use the following:
    Code:
    IF LEN(mystring) THEN
    .
    END IF
    To check if a substring is in a main string we often use:
    Code:
    IF INSTR(mainstring,mystring) THEN
    .
    END IF
    However, these cannot be safely combined with an AND to test whether both conditions are satisfied eg.
    Code:
    IF LEN(mystring) AND INSTR(mainstring,mystring) THEN
    .
    END IF
    If LEN(mystring) returned 10 and INSTR(mainstring,mystring) returned 1 then:

    LEN(mystring) AND INSTR(mainstring,mystring) actually returns 0 even though both our requirements are satisfied because of the way that the AND operator works.

    We need to use a structure like that below with both components returning -1:

    Code:
    IF LEN(mystring)>0 AND INSTR(mainstring,mystring)>0 THEN
    .
    END IF
    Regards,

    Bern

    ------------------
    Bern
    www.insighttrading.com.au

    [This message has been edited by Bernard Chapman (edited August 26, 2001).]
Working...
X