[Gambas-user] How to realize AND, OR etc.

Doriano Blengino doriano.blengino at ...1909...
Thu Sep 2 13:58:42 CEST 2010


Rolf-Werner Eilert ha scritto:
> Am 02.09.2010 11:36, schrieb Doriano Blengino:
>   
>> Rolf-Werner Eilert ha scritto:
>>     
>>> This is just a general question about programming, it doesn't refer to
>>> Gambas specifically, but I would implement the results in Gambas.
>>>
>>> For some of my bigger projects I have had to implement IF and ELSE and
>>> similar functions. I tried to manage AND, OR etc. too, but I failed.
>>> Somehow I didn't find a proper way of implementing this logic. So up to
>>> now I have filled this gap by simply putting several IF ELSE IF ELSE and
>>> so on within each other. Of course this is somewhat tricky sometimes :-)
>>>
>>>       
I am not really sure about what you want. My first guess was that you 
wanted more information on *how* to use ANDs and ORs in a gambas program 
(and there are also XORs and NOTs). Now I doubt that you want to write a 
compiler able to interpret boolean expressions. I will try to explain a 
little below.

>   
>> Generally speaking, compilers (or parsers) don't use arrays to store
>> intermediate results. ANDs and ORs are like any other arithmetic
>> operation - they are evaluated left to right, giving precedence when
>> due. But! Especially when speaking about boolean expressions, you can't
>> assume a specific order of evaluation.
>>     
>
> Ok... But what about brackets? When the user of my program wants to make 
> sure the expressions ARE evaluated in a special order? How does this fit 
> in here?
>   
Brackets only force precedence in the classic way:

    2*3+4      -> left to right, because "+" has lower precedence than "*"
    2+3*4      -> first "3*4", because "*" has higher precedence than "+"
    (2+3)*4    -> again left to right, because the brackets.

>   
>> Some compiler can even arrange
>> code in such a manner that some computation is be omitted, when at a
>> certain point of the expression the result is already known. This is
>> called short-circuit, and Gambas does *not* use it. To clarify, if I write:
>>
>>      IF A>  3 AND B+5>  2  THEN...
>>
>> some runtime could evaluate "A>  3" and, if it founds it false,
>> completely avoid to compute "b+5" and "B+5>  2". Gambas does never do
>> so.
>>     
>
> And I would be glad if I managed my own program to be half as clever as 
> Gambas is :-)
>
>
>   
>> It evaluates "A>  3", obtaining a true/false value. Then it computes
>> "B+5", compares with 2, and obtains another true/false value. Then it
>> computes the AND between the two boolean values, and so it can decide to
>> execute the THEN part or not.
>>     
>
> Yep - and how does it organize this? Doesn't it have to look into the 
> whole line prior to seeing that there is a "> 2" following in order to 
> decide to compute "B+5" first? And doesn't it have to evaluate "A > 3" 
> and "B + 5 > 2" prior to computing the AND? So everything has to be put 
> apart into certain pieces, then evaluated, computed and so on to get a 
> final result.
>
> If there's no array way of doing it - how is it done then?
>
> If I go letter-by-letter through the formula text the user of my program 
> is giving me, I might at every moment stumble over new surprises :-)
>   
Actually you use a stack, which can also be seen as an array (unlimited 
array) with an index pointing in it.
An expression starts with a "value", followed by a number of couples of 
"operator-next_value".
You read the first value, which is "result".
Then you read the next couple of "operator-value". If there is nothing 
more, you evaluate "result operator value" and finish. If there is 
something more, peek further at the next "operator2". If it has lower 
precedence than the current operator, you evaluate the current step and 
go ahead. Otherwise, you add a position in the stack and store current 
result and current operator, set "result" equal to the current value and 
operator=operator2, and go on.
This is a quick and very simplified form, which does not explain how to 
"pop" the stack, and does not manage unary operators (like NOT, unary 
minuses, and so on). I only have to add that if you encounter an opening 
bracket, all the contents of the bracket have to be regarded as a value. 
If you know the HP hand calculators, which use RPN method, you can see 
that that method is nothing else than this. To calculate:

    2+3*6

you press in sequence "2" Enter "3" Enter "6" "x" "+". Each time you 
press Enter the stack is pushed. Each time you apply an operator, the 
stack is popped.

This could be another way to implement an expression compiler...

>   
>> All this is important if, when evaluating expressions, some collateral
>> result is expected. If, instead evaluating simple variables, you use
>> properties or function calls and, if these properties or function calls
>> do something behind the scenes (some collateral effect), then the order
>> of evaluation can be important, but you should not rely on it.
>>     
>
> As far as I can see, I wouldn't risk collateral results in my programs.
>
>   
>> The
>> solution in this case is to use temporary boolean variables to force the
>> evaluation of some piece of code, and have a predictable evaluation
>> order. The line of code above could be written like:
>>
>>      btemp1 = A>  3
>>      btemp2 = B+5>  2
>>      IF btemp1 and btemp2 THEN...
>>     
>
> This would make up for a nice array, wouldn't it? :-)
>   
Yes... but only because we wanted to use an array. And anyway, at last 
the array is combined into an expression!
The same would be;

    b1 = ...
    b2 = ...
    b3 = ...
    if b1 or b2 and b3 then...

I mean: we can not get rid of the final expression, so an array does not 
help :-)

>   
>> This version of code assures that A and B are always evaluated, and in
>> that given order.
>>
>> That said, when there is more than one possibility, it's a question of
>> style. I personally don't like long sequences of ANDs and ORs, but
>> sometimes they are expressive:
>>
>>      IF age<18 or sex=female or state=married or religion<>catholic THEN
>> you_are_not_a_catholic_priest()
>>     
>
> Ok. Let's use my printing forms as an example. This is where my need for 
> this kind of logic is most urgent.
>
> If I want to have a certificate printed for the number of language 
> courses a student is taking, I have to decide how to use the free space 
> for the marks. So if it's only 2 languages, there is more space and I 
> can keep the places for the marks more apart than if it's 4 languages.
>
> So the line of code in the printing form could read somewhat like (I 
> write pseudo code, that's easier here)
>
> IF Instr "courses" = "Spanish" AND Instr "courses" = "Russian" THEN
>   print special 4 languages version
> ELSE IF Instr "courses" = "Spanish" OR Instr "courses" = "Russian" THEN
>   print 3 languages version
> ELSE
>   print 2 languages version
>
> Of course it is possible to do this without any ANDs and ORs, but it 
> would make things easier to read. It is really complicated to read now.
>
> Another case which came about yesterday and reminded me to this whole thing:
>
> For example, if a student has booked a certain main course which is more 
> expensive than the ordinary one but then books in for another special 
> language course, there will be a discount on the main course.
>
> If I have to indicate on a sheet how much the students pay, I have to 
> decide:
>
> IF expensive main course THEN
>    IF plus special course THEN
>      x Euros for main course
>      z Euros for special course
>    ELSE
>      y Euros for main course only
>    END IF
> ELSE
>    x Euros for main course
>    IF special course booked THEN
>      z Euros for special course
>    END IF
> END IF
>
> If there were ANDs and ORs it would make life somewhat easier ;-)
>
> Furthermore, there are other places in the program where a logical 
> sequence with ANDs and ORs could help, and if I managed to program the 
> whole thing in a way to be able to use it everywhere, this would open 
> more ways of organisation.
>
> There is a filter function for instance to build lists of students in 
> different classes and courses. It is very primitive now, it can only 
> look for Instr in a single field, then put the name into the list or 
> not. If I could use genuine logic, many things would be easier.
>
> Hope I could make this clearer. Your answer already helped, but if using 
> arrays is unusual, which way is better then?
>   
Uhm... a few considerations.

If you must implement boolean logic in your program, but you don't need 
to let your users input boolean expressions into your program:
You should express a series of "rules" like:

    1. Course cost is X
    2. Special cost is Y (zero means no special course)
    3. if X>expensive and Y<>0 then X=X-discount
    4. Total cost is X+Y

or,

    1. Course cost is X
    2. Special cost is Y
    3. Discount_applied = X>expensive and Y<>0
    4. if Discount_applied then X=X-discount
    5. print "Total cost is " X+Y
    6. if Discount_applied then print "(discount)"

Anyway, there is no single way to organize tests (IFs, AND, NOTs...) - 
we could read hundreds of different books on the same argument. But you 
was true when about gambas you wrote "look at the whole line". You 
should collect all the possible data, and group them in temporary 
(better say high level) variables, like "Discount_applied". Examine 
every rule one after another. Your rule is "If the main course is 
expensive, and there is another course, then there is a discount". Ok. 
Now suppose that now, or in the future, a new rule is added: "If the 
scholar is rich, then no discount can be applied". In a single piece of 
code, where you determine Discount_applied, you modify it:

    ...
    3. Discount_applied = X>expensive and Y<>0
    4. if Scholar_is_rich then Discount_applied = false
    ...

May be that to determine if a scholar is rich you must examine the 
family income, and the number of members in the family... another piece 
of code that has a single purpose: determine the value of 
Scholar_is_rich. May be that you don't want to collect data about 
richness of scholars, if not needed (the scholar takes only one course). 
Then you must can do like this:

    ...
    3. Discount_applied = X>expensive and Y<>0
    4. Scholar_is_rich = false
    4. if Discount_applied then calculate_if_scholar_is_reach()
    5. if Scholar_is_rich then Discount_applied = false
    ...

Another kind of problem is if you want to "interpret" boolean 
expressions inputted by users. Textual representation of boolean 
expressions is powerful, but can be difficult both to interpret and to 
input. A table can be easier:

    cond1   -and-   cond2   -and-   cond3   -and-   cond4
                   -or-
    cond1   -and-   cond2   -and-   cond3   -and-   cond4
                   -or-
    cond1   -and-   cond2   -and-   cond3   -and-   cond4

The words "-and-" and "-or-" are fixed in the logic of the program; the 
size of the table is also fixed.
Empty rows and empty fields are ignored. For every possible result 
(record), you start by saying "this record is ok". Then you scan all the 
rows, ignoring empty ones. If a row succeeds, the record passes.
Every single row has 4 condition, which must be met all together. If one 
fails, the row fails.
Every condition is a test in the form "value1 operator value2"; value1 
is a combobox containing fields of the recors (name, surname, ZIP, 
course and so on); operator is a combobox containing "=", "<>". Value2 
is inputted by the user.

Try to use "Search messages..." in your email client (which is 
Thunderbird/Icedove, right? :-)). It does something very similar.

I think to have been exhaustive... but if not, ask for more.

Regards,
Doriano




More information about the User mailing list