[Gambas-user] How to realize AND, OR etc.
Rolf-Werner Eilert
eilert-sprachen at ...221...
Thu Sep 2 15:40:39 CEST 2010
Am 02.09.2010 13:58, schrieb Doriano Blengino:
> 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
>
Thank you very much, Doriano, this will help a lot. Especially the idea
with the table was very enlighting. I guess I'll manage to fiddle
together something like that. If there are more questions, I'll be back :-)
Regards
Rolf
More information about the User
mailing list