IFELSE, IFELSE$ functions improvement suggestion
#24087
29 Jan 19 03:58 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Hi, Here is an easy one I'm trying to get rid of my FN'if'else'value$(), FN'if'else'value() that was in the origin of the A-Shell IFELSE(),IFELSE$() but I feel that mine still are simpler to use when the condition is to test an empty string or zero value :rolleyes: The additional curiosity here is that, it seems that those conditions (empty and zero) are in an huge percentage of usage in my programs. To be clear, my functions work like this: FN'if'else'value$(a$, b$) means IF a$="" THEN return b$ ELSE return a$ or FN'if'else'value(a, b) means IF a=0 THEN return b ELSE return a or FN'if'else'value(a$, b, ZERO_AS_BLANK) means IF (a$="" OR a$="0") THEN return b ELSE return a$ So, my suggestion is to adopt the same logic in IFELSE$/IFELSE omitting the first argument, like: IFELSE$(, a$, b$) / IFELSE(, a, b) or, if better: IFELSE$("", a$, b$) / IFELSE(0, a, b) IFELSE$(level'of'interesting$>="reasonable", "consider it", "ignore it") :p
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24088
29 Jan 19 11:44 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Thanks for the stimulating morning suggestion, although I'm not sure it's as "easy" as you say! :rolleyes: As you probably recall, several years passed between when you first showed me those routines and when I finally got my head around the concept. (Although I have to confess, that in the end, I don't think I ever did completely understand your logic, and instead modeled ifelse() after the "conditional operator" (a ? b : c) in C.) So probably I'm still missing some subtlety, but to start with the basics, the main attraction of all of these operators/functions is the ability to embed a common if/else condition within a larger expression so as to avoid having to split the logic into multiple statements and/or use temporary variables between them. It's not so much a question of less typing but just being able to express the logic more naturally and compactly. I can't find a copy of your routines here, but if understand it, the main difference between the built-in ifelse() / ifelse$() functions and your FN'if'else'value() / FN'if'else'value$() functions is that with the built-in versions you have to repeat the first argument to get the same effect as with yours. In other words... ifelse(a,a,b) equals FN'if'else'value(a,b) and ifelse$(a$,a$,b$) equals FN'if'else'value$(a$,b$) I'm not really sure that I understand the ZERO_AS_BLANK flag though, since in the case of the built-in functions, "", " ", and 0 in the first argument of either the numeric or string versions results in the condition failing and the last argument being returned. Note that the only difference between ifelse() and ifelse$() is the type of the return expression. Either version supports both string and numeric arguments, and in all cases, the first argument is evaluated as true/false, which is essentially a numeric condition, so any strings ("", " ", "0") are treated as numeric and thus null, blank, and zero all are the same as a false condition. So, assuming I have that right, what we are really debating is whether it is worth implementing a syntax to allow you to avoid repeating the first argument, i.e. treat ifelse(a,b) as if it were ifelse(a,a,b)? When expressed that way, it makes it seem silly to go to so much effort to eliminate one extra "a,", but it might seem different (and in fact, make a performance difference) if the "a" was really a complex expression. Technically I suppose it wouldn't be too difficult, but in terms of linguistic expectation, I'm not sure anyone (except you ) would know what to expect from the expression ifelse(a,b). The "normal" scenario with optional parameters is that they occur at the end of the parameter list, as in INSTR(spos, subj, pattern {,flags}) ; so it would be "normal" to assume that ifelse(a,b) was equivalent to ifelse(a,b,0), which gives a completely different result than ifelse(a,a,b). (And although I've been reprimanded in the past for trying to implement the "nanny compiler", i.e. to have it prevent you from making silly mistakes, this certainly seems like an invitation for hard-to-detect bugs to be introduced if you accidentally leave off the 3rd argument to to an ifelse() expression.) Another possibility would be to make a variation of the ifelse() function name for the 2 argument version, but I'm not sure what to call it. ifelse2(a,b)? if'not'blank'a'else'b(a,b)? Perhaps this all amounts to too much discussion over a minor detail, but with computer languages (like constitutions, but unlike natural languages) it's probably a good thing that we make them difficult to change, so we don't clutter them up with too much confusing baggage. :rolleyes:
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24089
29 Jan 19 04:16 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Hello Jack, This can be a very interesting and endless discussion Apologize for the lack of details in my previous post, my fault to think that my thoughts are obvious and understandable with mere examples. Let's start to explain the logic behind my "if'else'value"'s functions. The full translation for "if'else'value" is: IF the first argument is non empty or not zero then use it, ELSE use the VALUE on the second argument I don't have any official statistics but, I realized that an huge amount of IFs are to test if a string is not empty or a number is non zero, even in many cases where the condition is "if a>0" could be replaced by "if a#0" because the number couldn't be negative. Based on this assumption, I created the "if'else'value" functions in two flavors: a)FN'if'else'value$() b)FN'if'else'value() - In both the condition is omitted.
In both, in the first argument is the result for TRUE (THEN) and the FALSE is on the second argument (ELSE). In a) it's TRUE when the string on the first argument is not empty. In b) it's TRUE when the value on the first argument is not zero. In both, the function returns the value of the resulting argument. Now, the reason for my suggestion about IFELSE is not for cases like IFELSE(a#0, a, b) where the difference to FN'if'else'value(a, b) is not significant and even has less characters to type but for something like this (not extreme) case: IFELSE(FN'evaluate'this(a, b, c)*100, FN'evaluate'this(a, b, c)*100, d) now with my function: FN'if'else'value(FN'evaluate'this(a, b, c)*100, d) I would be happy to search for my most exotic examples in programs, here is one I found in a quick search:
elseif FN'if'date'belong(FN'if'else'value$(amostra.entrega'previsto$, amostra.entrega'solicitado$), listaam'prazoi$, listaam'prazof$)=0 then
In the example above, using IFELSE$ the variable amostra.entrega'prevista$ has to be repeated for the condition, which is significant while typing, now imagine if one of those Stephen's huge variable are in place At this point, I must say that A-Shell IFELSE are of great value, they put away my FN'if'then'else'value$() which I've created to use when the value to return on TRUE is not the first argument, like: FN'if'then'else'value$(a$, b$, c$) Again, the condition only evaluates empty strings on the first argument, if TRUE returns b$ else returns c$. CONCLUSION: 1. My goal is to simplify and speed up the programming of conditions to evaluate empty strings and zero values 2. My initial suggestion was to improve the existing IFELSE$/IFELSE allowing ""/0 on the first argument instead of a full condition, like: IFELSE$("", a$, b$) instead of: IFELSE$(a$="", a$, b$) 3. Another option could be to create a new pair of functions w/o the first argument, like I've done with FN'if'else'value$() and FN'if'then'else'value$(), a possible naming could be IFVALUE$/IFVALUE but, like I've done, the condition here is inverted, like: IFVALUE$(a$, b$) In this case a$ is returned if not empty. I'm afraid to have complicated more than desired due constant interruptions while writing for so long and not enough strength to review so, hopefully understandable, I hit [Add Reply]
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24090
30 Jan 19 01:36 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
I found an example which illustrates the typical case where a simple function to test empty strings is very handy.
? #gf'canal, "//textout,1020,250,";strip(FN'if'else'value$(FN'get'season$(version.season$), FN'get'season$(model.season$))); &
" / "; &
FN'if'else'value$(version.year$, model.year$)
What the above statement does is to print the "season / year" of a shoe but each value can be provided from two possible origins "version" or "model" where "version" has precedence over "model". So, on the fly, FN'if'else'value$() provides that. In the first one FN'get'season$(version.season$) grabs the name of the version.season$ which will be empty if version.season$ is empty, in that case it will grab the name for model.season$. The same logic applies to the "year" where version.year will be printed if not empty otherwise print model.version. The alternative using IFELSE$ could be: #gf'canal, "//textout,1020,250,";strip(IFELSE$(FN'get'season$(version.season$)#"", FN'get'season$(version.season$), FN'get'season$(model.season$))); &
" / "; &
IFELSE$(version.year$#"", version.year$, model.year$)
The worst here is not the extra typing but the additional execution of the FN'get'season$(version.season$) when version.season$ is not empty; it's not exactly a drama but not so elegant. By the way, I prefer by far the IFELSE$ name instead of FN'if'else'value$, obviously
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24091
30 Jan 19 11:41 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
It did occur to me that the primary concern here was performance, i.e. avoiding the need to evaluate a complex condition multiple times. And that does seem like a good motivation for a 2-argument variation of the existing 3-argument IFELSE(). (As an aside, using a function call for the expression in IFELSE$(expr,a,b) is probably only a good idea if the function doesn't have side effects, although knowing you, that might be part of the attraction. :rolleyes: Obviously, evaluating such a function twice only compounds that danger / attraction.) I actually woke up last night trying to think of a good name for it, since Ty would never allow a function like IF'THE'FIRST'ARGUMENT'IS'NOT'...'USE'IT() in the documentation because it would mess up the formatting of the tables. So, considering that the existing IFELSE(a,b,c) might really have been called IFTHENELSE(a,b,c), might we logically call the 2-argument version IFNOTTHEN(a,b) (pronounced "if not a then b")? Perhaps we can drop the THEN and just make it IFNOT(a,b) ( "if not a, b" i.e. "if not a, {then} b"). In that case your example becomes: ? #gf'canal, "//textout,1020,250,";strip(IFNOT$(FN'get'season$(version.season$), FN'get'season$(model.season$))); &
" / "; &
IFNOT$(version.year$, model.year$) So we'd have: a) IFELSE(expr,value1,value2) (if expr then value1 else value2)
b) IFELSE$(expr,value1$,value2$) (if expr then value1$ else value2$)
c) IFNOT(value1,value2) (if value1 then value1 else value2)
d) IFNOT$(value1$,value2$) (if value1$ # "" then value1$ else value2$) I must admit that I'm not super pleased with the fact that in IFELSE$(expr,a$,b$), the expression is effectively BOOLEAN and thus "", "0" and even alphanumeric values like "A" would all be considered FALSE, thus returning value2$ instead of value1$. Whereas in IFNOT$(a$,b$), we have to treat the conditional test as if a$ # "", since otherwise it would always return b$ except in the odd case where a$ started with a digit 1-9, which surely is not what you want. But I guess as long was we make that clear in the documentation, it won't be problem (as most people brave enough to use it would probably assume correctly how it works.) What do you think?
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24092
30 Jan 19 01:26 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Great! I'm happy with the IFNOT/IFNOT$ it's short and after start using it, the meaning will be automatic in the mind not in the name of the function. By the way, I forgot to explain the logic behind ZERO_AS_BLANK That flag informs the string functions to evaluate the condition for (argument$="" OR argument$="0") instead of argument$="". Examples: ? #9999, FN'if'then'else'value$(user'locked, "Not allowed", "", ZERO_AS_BLANK) ? #9999, FN'if'else'value$(user'locked, "Locked", ZERO_AS_BLANK) If you prefer to ignore this option, not a problem, or if you have a better suggestion, great! So, zero urgency for implementation, I think we have decided about the fundamentals, maybe the perfect task for a rainy day or a sunny one near the pool drinking Caipirinhas Talking about that, time to go to the beach before the meeting to distill new Gin recipes. Thank you for considering this
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24093
31 Jan 19 12:43 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Wow, I'm sorry I missed that meeting! But as for ZERO_AS_BLANK, on the one hand, it seems pretty self-documenting, which is good. On the other hand, it's somehow inelegant to have an optional argument to a built-in function that has only one possibility, which nevertheless must be DEFINEd as a symbol in order for it to be understood. By comparison, the INSTR() function has an optional flags argument, but in that case, aside from the many possibilities it offers, the mere existence of the optional parameter, even if 0, has meaning. Plus, if we are going to allow an optional parameter, then one of the main arguments for splitting IFELSE from IFNOT (avoiding the possibility of a misinterpretation caused by accidentally omitting, or adding, a parameter) is lost. I need to ponder this some more. A couple of possible alternatives would be to create yet another function variation to split out the ZERO_AS_BLANK version i.e. IFNOT(a,b) ! numeric version
IFNOTEMPTY$(a$,b$) ! if a$ # "" then a else b
IFNOTZB$(a$,b$) ! same but treat "0" as "" Another thought was to use the expression type of the first parameter to decide whether the test was for "empty" or "value zero", in which case you could force the latter by replacing the a parameter with val(a). But the problem with val(a) is that it treats any non-numeric a as zero (i.e. "A", "GIN", etc.) (Which reminds me of the periodic query/complaint about why IF A = "" is true when A actually contains a string of blanks. That's a backwards compatibility quirk needed to match the behavior of AlphaBasic from the beginning; whether it is a good idea is now besides the point.) That leads to another idea of adding some more built-in functions to make it easier to test for some of these variations of empty strings ... ISEMPTY(), ISBLANK(), ISZERO(). But the dividing line between those kinds of tests and other similar ones that are properly handled in user functions (see for example the SOSFUNC fnistype.bsi ) is pretty fuzzy. Maybe your original idea of an optional ZERO_AS_BLANK is the best approach after all? Or maybe it can be slightly improved by expanding it to a series of related flags that might affect the way the initial argument was evaluated... define IFNOTF_ZERO = &h0001 ! treat "0" as 'not'
define IFNOTF_ZEROS = &h0002 ! treat numeric zero as 'not' ("000", "0.0", ...)
define IFNOTF_SPACES = &h0004 ! treat spaces as 'not'
define IFNOTF_WHITE = &h000C ! treat all whitespace (blank, tab, CR, LF) as 'not'
define IFNOTF_VAL = &h0010 ! treat val()=0 as 'not'
define IFNOTF_UNQUOTE= &h0020 ! ignore outer quotes (Some of those variations could be handled by using the existing EDIT$() function though.) Somewhere in all of that is the perfect balance between simplicity, elegance, flexibility and offering a significant performance advantage. Just not sure exactly where.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24094
31 Jan 19 02:21 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
You're right, maybe we could ponder for a while and try to build something that covers more cases or, at least, well structured to cover other options in the future. I'm tempted to accept your excellent suggestions but, based on such good ideas is easy to decide, better would be if you join us, next week, on the next round in our sophisticated distillery To taste our #1 recipe To produce the #2. And to discuss what are we talking about? To guarantee that we drink well during the experiments, we have three portuguese samples from the author of our future Gin, that are under rigorous tasting proofs to decide which will go to production Have fun
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24095
31 Jan 19 03:03 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
The number of reasons for a visit to Brasil have just increased by at least two!
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24096
02 Feb 19 02:36 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
I thought about this some more while doing some of my own "product testing" and am now leaning towards avoiding the creation of several new functions and instead figuring out how to fit your logic into the existing ifelse(). I think what we need is just some syntax to indicate that the 2nd argument (i.e. what the function returns if the condition is true) is the same as the first argument, but without having to evaluate it again. Some possibilities would be: ifelse(expr1,*,expr2) ! various special symbols for 2nd arg
ifelse(expr1,~,expr2)
ifelse(expr1,=,expr2)
ifelse(expr1,'',expr2) ! (ditto mark?)
ifelse(expr1,|::|,expr2) ! (musical repeat?)
ifelse(expr1,,expr2) ! or simply omit it (The ditto mark would in some ways be the most natural, except that syntactically a single " would be too troublesome for editors, and "" is ambiguous as it could mean just a null string. Also, ditto is usually applied to something above, not to the left.) I know you had suggested a dummy first argument, but since that is the condition that is being tested, it seems more logical/natural to me to have a special syntax for the 2nd argument to indicate that it should be the same as 1st argument, but without any reevaluation. I'm not sure any of the above really accomplishes that in any any obvious way, but all of them would be reasonably distinct from the normal 3-argument ifelse(a,b,c), and would also allow for the possibility of an optional but unambiguous 4th argument for the flags, i.e. ifelse$(a$,b$,c$,flags). Yet another possibility would be for the compiler to recognize when the 1st and 2nd arguments were identical expressions and short-circuit the re-evaluation of the 2nd one. But for complicated expressions, that forces you to type them out again, which seems to invite typos, not to mention making the source harder to read later. Are you starting to regret bringing this up in the first place? :rolleyes: And I haven't even started on the natural extension of this logic to functions combining expression evaluation with conditional tests against multiple values, e.g. IF EQUALSOR(UCS(FNAME$[-3,-1]),"CSV","XLS") THEN ! if file extension is CSV or XLS...
XCALL SHLEXC,FNAME$
ENDIF
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24097
03 Feb 19 11:46 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
No, I don't regret at all As a matter of fact, it seems we are thinking on the same wave, are you also distilling stuff? After brainstormed all the ideas, even before read your last posts, I've decided to convince you to bet on the original idea to improve IFELSE$ :rolleyes: The difference on our approaches (maybe related to the kind of distilled stuff) is that I'm still stuck on handling the condition on the first argument instead of the THEN value on the second argument. Occurred to me that we could use #1 and #2 in the condition to reference THEN and ELSE values, for example: IFELSE$(#1="", this'is'a'big'name$, else'this$) IFELSE(#1>#2, this'is'a'bigger'value, 1) It can look weird to use #1 and #2 for arguments 2 and 3 of the function but, at least in my mind, makes more sense. Maybe it must be documented like values 1 and 2 for the condition instead of arguments 2 and 3 of the function. Thanks
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24098
03 Feb 19 03:42 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
At the moment I'm only distilling ideas, but that's surely the gateway to the hard stuff! Before I start decocting your latest ideas, I'm going to throw out a tactic from Let's Make A Deal and show you what's behind door #1. (Last night I decided to create a working model, even though I'm still not sure about the syntax.) So here's what you can have right now (using the pseudo-ditto syntax for the 2nd arg)... define IFELF_NULL = &h0000 ! treat "" as empty (default)
define IFELF_BLANK = &h0001 ! treat " " (string of blanks) as empty
define IFELF_ZERO = &h0002 ! treat "0", "000", "0.", "00.0" as empty
define IFELF_NZB = &h0003 ! any of above
x = ifelse$(val1$, '', val2$) ! same as ifelse$(len(val1$)=0,val2$,val1$)
x = ifelse$(val1$, '', val2$, IFELF_BLANK) ! same as ifelse$(val1$="",val2$,val1$) *
x = ifelse$(val1$, '', val2$, IFELF_ZERO) ! same as ifelse$(len(val1$)=0 or val1$="0",val2$,val1$)
etc. (* Note that the relative expression val1$ = "" is true even if val1$ contains spaces.) I don't really like the look of that '' 2nd argument though, and am nearly ready to just drop it, except that I can't see any solid way to distinguish the original 3-way version... ifelse$(relexpr,val1$,val2$) from the new 2-way version... ifelse$(val1$,val2$,flags)They may look different based on the above choice of argument variable names, but based on the existing syntax rules for the 3-way ifelse$(), the second version above is a perfectly valid 3-way version. (Even though we would normally expect the 3rd argument to be a string, given that the return value of the ifelse$() function is a string, a number is perfectly legal and will be stringified.) The only case where we could be sure that ifelse$(expr,b,c) was the 3-way version was if the expr contained an explicit comparison operator (e.g. ifelse$(b>c,b,c)). But since a simple value or variable is also a valid relative expression, there's no way to know for sure if a simple argument, or a function call, is meant to be treated as a truth test, or simply a value. I also considered your idea of using some kind of named parameter syntax to identify the parameters, but that seems like it opens up another door into a large dark room, since no other built-in function requires parameter naming to resolve ambiguity. So today, unlike yesterday, although I'm ready to accept your preference for the meaning of the first and second arguments, I'm back to leaning towards the advantage of a separate function name for the 2-way ifelse$(val1$,val2${,flags}). (Ask me again tomorrow and I may be back to the yesterday's opinion.) Side note: for the 2-way numeric version, we can merge it with the existing 3-way version, because there is no need to use the flags argument with the numeric version, and thus the two variations are easily distinguishable by the number of parameters... x = ifelse(relexpr,val1,val2) ! if relexpr then x=val1 else x=val2
x = ifelse(val1,val2) ! if val1 then x=val1 else x=val2 I'm not thrilled though with my earlier suggestion of IFNOT(), i.e. ifnot$(a$,b$). In some ways, ifelse2$(a$,b${,flags}) would be the most clear (where the 2 was meant to indicate a 2-argument expression (not counting the optional flags), rather than the 2nd version of the function (although both would be accurate). I'd have to get over my aversion to using numbers in the names of built-in functions though (since that also seems to open the door for a never-ending series of function upgrades). Maybe tomorrow...
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24099
04 Feb 19 06:13 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Good morning,
Even today I still think that stay on the 3 arguments function using #1 and #2 is the best choice, arguments: 1. It's the most complete function that covers all the simple and complex cases 2. Using #1 and #2 keeps it simple even for complex cases
But we still have space for refinements: 1. Keep IFELSE$/IFELSE to guarantee existing code. By the way, ANYBODY is using this? :rolleyes: 2. Create a new function IFTE(condition, value1, value2) (IT Then Else) 3. If you prefer, maybe adopt %1% and %2% instead of #1 and #2. I still prefer #1 and #2 :p 4. The new IFTE covers both numeric and string cases 5. Instead of a 4th argument for flags, we could create a collection of functions for conditions, similar to .isnull(), to use on the first argument, like: IFTE(IFBZ(a$), "it's an empty string handled as zero", "not zero")
Apologize for the flood in the morning, hopefully you had your coffee already :p
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24100
04 Feb 19 10:18 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
IF you're willing to trade in your ZERO_AS_BLANK flag for a new IFBZ(a$) function applied to the first argument, THEN we can handle all the variations with the existing IFELSE$/IFELSE functions, without breaking backward compatibility: ! existing...
1) ifelse(relexpr,a1,a2) ! if relexpr then a1 else a2
2) ifelse$(relexpr,a1$,a2$) ! if relexpr then a1$ else a2$
! new...
3) ifelse(a1,a2) ! if al then a1 else a2
4) ifelse$(a1$,a2$) ! if a1$ # "" then a1$ else a2$
5) ifelse$(IFBZ(a1$),a2$) ! if a1$ # "" and a1$ # "0" then a1$ else a2$ A couple of notes / clarifications: - ifelse$() differs from ifelse() only in the type of expression returned by the function. Normally you would use numeric arguments (a1,a2) with ifelse() and string arguments (a1$,a2$) with ifelse$() but there is actually no restriction as the argument types will be converted as needed to match the return type. - In the 2-argument version (examples 3-5 above), type conversion will be applied to the first argument so as to match the function type, before the argument is evaluated to determine if it is blank/zero. So for example if you used ifelse(a$,b), then a$ will first be converted to a number and then compared against zero. Similarly in ifelse$(a,b$), if a is numeric, it will be first converted to string before testing for empty. (Which means that it wouldn't make sense without the IFBZ(a) function because a numeric value converted to string will never be blank or null, but might be "0".) - I think for the ifelse$(a$,b$) version, we can use the normal A-ShellBASIC test for a$ = "" (ignoring trailing blanks, so that " " is considered the same as ""). In a case where you really feel the need to test for "" but not allow blanks, perhaps another specialized function may be needed. After one cup of coffee, I have to say that it seems like a pretty good compromise to me. Or do you want to see what tomorrow brings? :p
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24101
04 Feb 19 11:03 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Uh oh, half way through my second cup of coffee, I can see the problem with the IFBZ(a$) idea... The universal rule with all expressions, including functions, is that there can be only one overall value of the expression (or return value of a function), and it replaces all of the arguments that went into the expression. So by the time we evaluate IFBZ(a$), we've lost a$. The only way that could work is if IFBZ() wasn't really a function but instead was some kind of specialized syntax that changed the behavior of the ifelse$() function. (In other words, either a flag, or a different variation of the function.) For obvious reasons, I don't like that. We've already gone over the problem with flags argument (i.e. it would make it necessary to have another variation of ifelse$, like ifelse2$). Another option would be to introduce a ifzelse$, or ifzeroelse$ to eliminate the IFBZ(a$). In that case, example (5) from my previous post would be: ifzelse$(a1$,a2$) ! if a1$ not blank and not zero then a1$ else a2$
!or
ifzeroelse$(a1$,a2$) ! if a1$ not blank and not zero then a1$ else a2$ Or, here's a new bottom-of-the-second-cup idea: instead of a function like IFBZ(a$) that returns true/false, we could use a string function, perhaps ZSTRIP$(a$), or a new flag on the EDIT$(a$,flags) function, that removed the leading/trailing zeroes from a string. So our problem case (5) would become: ifelse$(edit$(a1$,EDITF_ZL),a2$) ! if a1$ # "" after removing leading zeroes, then a$ else a2$ The only downside of the above would be if a1$ contained something like "0-DARK-30", in which case the ifelse$() function would end up returning "-DARK-30" rather than "0-DARK-30". But considering that the need to treat "0" as blank is a special case to start with, I'm guessing that you would know in advance whether leading or trailing zeroes were meaningful, and if so, you wouldn't be using that option in the first place. And note that if in doubt, you could always fall back to the original 3-way ifelse$ ... ifelse$(edit$(a1$,EDITF_ZL),a1$,a2$) Don't panic, but I'm going for a third cup... :rolleyes:
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24102
04 Feb 19 01:19 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Don't worry, I've just finished my 10th espresso, or so, and I'm cooling down from the busy day before compile all the data here and make a statement
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24103
11 Feb 19 08:19 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Let's try again What about: a) Don't change IFELSE/IFELSE$ because, it's very useful as is and the goal is to simplify the IFTHENELSE for empty and zero values b) IFEMPTY$(then$, else$) c) IFZERO(then, else) d) IFEORZ$(then$, else$) I think that only d) has something new but nothing more then considering in the conditional expression two possible values for then$, "" OR "0" map1 a$,s,1,"0" ? "a$ = [";IFEORZ$(a$, "neither empty or zero");"]" a$ = [] Anyway, I reinforce that this is all not vital considering that I'm using it already with functions and, only in extreme cases where those functions are called dozens times in loops the performance can be an issue. On the other hand, maybe you just see some merit to have this stuff embedded in A-Shell and decide to go ahead to implement it. This said, I think we have discussed more than the basics, I think you understood what and where I would like to simplify so, be my guest to tweak to your flavor and go ahead when you want or, if you prefer, will be a pleasure to keep refining the details under this topic.
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24104
11 Feb 19 11:31 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
While still on my first cup of coffee, I was tempted to say that IFEMPTY$(then$,else$) and IFZERO(then,else) were the same as my Feb 4 suggestions (3) and (4), i.e. 3) ifelse(a,b) ! if a then a else b
4) ifelse$(a$,b$) ! if a$ # "" then a$ else b$ But now part way into my second cup, I see that they are actually reversed... a1 = 0
a2 = 3
b = 7
ifelse(a1,b) ! 7 (if a1 then a1 else b)
ifzero(a1,b) ! 0 (if a1=0 then a1 else b)
ifelse(a2,b) ! 3 (if a2 then a2 else b)
ifzero(a2,b) ! 7 (if a2=0 then a2 else b) Ignoring the IFEOZ$() issue for a minute, the question is whether it's better to expand the existing ifelse(expr,then,else) to support a 2-way version, i.e. ifelse(thenexpr,else) where thenexpr serves as both the expr and the then. Or introduce two keywords/functions which are perhaps more intuitive in the case where you are testing for the zero/empty condition rather than the nonzero/nonempty condition. I guess it's sort of like having both an = and <> operator so you can have a choice between if a=0 then b else c or if a<>0 then c else bThe main troublemaker (aside from the two of us) here is the if-blank-or-zero case. But it occurs to me that the likelihood of wanting to test a string expression for blank or zero would apply equally to both the 2-way and 3-way versions. So maybe creating a separate function just to test that condition, perhaps .EOZ(expr), would be the most flexible, even at the cost of forcing you to use the 3-way ifelse instead of the 2-way, e.g. !instead of: IFEOZ$(a$,b$)
? ifelse$(.eoz(a$),a$,b$)
if .eoz(phone$) then call ask'for'phone'number The first statement is more clumsy than your IFEOZ$(a$,b$), but the second one is perhaps a useful case which couldn't be handled by IFEOZ$(). Not that any law of nature or cybernetics prevents us from having both, but it's kind of like pie and cake - if we give you both, you're going to end up with belly ache. :rolleyes: But in looking at your example ... map1 a$,s,1,"0"
? "a$ = [";IFEORZ$(a$, "neither empty or zero");"]" You say the result is "[]", while I would have said it was "[0]", since IFEORZ$(a$,b$) should simply return a$ if a$ is empty or blank, which it is (i.e. "0"). Was that a caffeine-deprivation-induced error? Or did you intend that the IFEORZ$(a$,b$) would act like: IFELSE$(.EOZ(a$),"",b$) instead of: IFELSE$(.EOZ(a$),a$,b$) The fact that it could possibly go either way seems like another advantage for the combination of the 3-way version and the .EOZ() expression, i.e. it makes up for its verbosity with additional flexiblity.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24105
11 Feb 19 12:45 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
I think we need another new function but to use on this post IFCOFFEELEVEL() and depending on "too low" or "too high" would block our login I have a few comments regarding your last reply but I'm feeling we can get an agreement very soon. 1. Correct, my last suggestion was not absolutely innovative, it was a compilation of everything we have discussed 2. Correct, IFEOZ$(a$, b$) is a two step conditional expression: step 1 if a$="0" then a$ = "" step 2 if a$="" then IFEOZ$="" Or another interpretation could be: - if (a$="" OR a$="0") then IFEOZ$="" 3. I can see the merit of .EOZ(), like many other dot functions that crossed my mind, but I still think that IFEOZ$() guarantee the same that .EOZ() does and adds more. In your example: if .eoz(phone$) then call ask'for'phone'number we can have if IFEOZ$(phone$) then call ask'for'phone'number Finally, I'm fine to just have the 3-arguments functions IFELSE$/IFELSE, in fact I really like that idea but, we must have some syntax to reference the arguments in the conditional expression to not force to repeat complex arguments in the expression to evaluate. Maybe .1 and .2 ? Example: a) IFELSE$(.1="", FN'very'complex'string$(…), FN'even'more'complex'string$(…)) b) IFELSE(.1>.2, FN'very'complex'calculation(…), FN'even'more'complex'calculation(…)) c) IFELSE$(.1="", IFELSE$(.2$="0", "", a$), b$) As you can see, in c) we have the IFEOZ$() or .EOZ() case solved using the 3-arg function and, besides c) it's far away from simplicity, I'll be fine to handle it this way, I must say that in a quick search over my development areas, I didn't found dozens of EOZ cases. It's on your cup now
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24106
11 Feb 19 01:51 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
I think you're right that we're getting close. (And if we can reach an agreement here, perhaps we can offer our services to the US government to help them reach an agreement on The Wall and avoid Return of the Shutdown.) On your point 2, it has to be the second interpretation, since we definitely don't want IFEOZ$(a$,b$) to have a side-effect of modifying one of the arguments. But I think we're both fine with that interpretation. You also make a interesting (although slightly Jorgesque :rolleyes: ) suggestion about IFEOZ$(a$) being able to replace .EOZ(a$) by making the 2nd argument optional and recognizing that any expression is a valid conditional expression in an IF statement, even an IFEOZ$(a$) or IFELSE$(expr,a$,b$) expression. But there's a complication here. IFEOZ$(a$,b$) returns "" if a$ is blank or zero, and "" has a true/false value of false, so if .eoz(phone$) then call ask'for'phone'number
! is NOT equivalent to
if ifeoz$(phone$) then call ask'for'phone'number
! but it would be equivalent to
if ifeoz$(phone$,.FALSE,.TRUE) then call ask'for'phone'number Or, we have to interpret the 1-way IFEOZ$(a$) as returning .TRUE if a$ is blank or zero, and false otherwise. However, in that case, it should be IFEOZ(a$), without the $, because it would be a numeric function. So does this mean we might have 1-way, 2-way, and 3-way versions on the table? And if so, shouldn't that apply to the entire family of IFxxx() functions? (I'm starting to get a bloated feeling in my stomach.) You're also right in pointing out that I seemed to have forgotten that the main problem with 3-way versions wasn't so much the verbosity as the inefficiency (or worse, side effects) of evaluating the argument expressions twice. I'm afraid the .1 and .2 syntax doesn't really work though, since those are valid numbers (thus .1>.2 will never be true). Fearing that I may have exhausted the cognition-enhancing capabilities of caffeine, I'm going to switch to carbohydrates in the form of a sandwich for lunch, in the hopes that the resulting digestion will have beneficial effects on multiple planes.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24107
11 Feb 19 05:26 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Maybe we can start a countdown from 3 and close the agreement on zero, no fear, you will slam the hammer Back to my point 2, I don't understand your reluctance because all the arguments are inputonly so, the value of the argument will never change top the outside. Anyway, the given options are merely an interpretation of how the function could handle the arguments to evaluate the condition and give the desired result. ConclusionIt's not important what's inside considering that IFEOZ$(a$,b$) returns "" if a$="" or a$="0". (consider this my first concession for the deal) As for the IFEOZ$() complication when used in a condition, you're right (here goes my second concession), I should have used: if ifeoz$(phone$, phone$)="" then call ask'for'phone'number ConclusionNo need to adapt IFEOZ$() for an 3-arg function, the above test is enough simple and readable. This said, 2 is on your side in the countdown, it's up to you to make me sweat on 1, my final chance, or try to start closing the ends
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24108
12 Feb 19 12:44 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
I like your gamesmanship, but since you've now limited the moves without limiting the time, I'm going to take advantage of the loophole and sleep on it. Maybe the sand man will be able to provide some guidance into the optimum balance between the competing factors of: - flexibility - elegance (ease of use, ease of understanding) - economy (minimum number of new keywords or other elements) - performance The 3-way version wins in the flexibility department, but trails in the elegance and performance departments. The 2-way version is more elegant and give better performance, but isn't as flexible and/or economical (especially if we have to have a whole family of ifxxx() variants. The idea of using special symbols to re-reference the expressions without re-evaluating them preserves flexibility and improves performance, but is less elegant or economical. User functions are the most flexible, but the least performant. Throw in the possibility of helper functions, like .EOZ(), into the matrix and the possibilities are well, enough for at least one restless night of sleep-coding.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24109
12 Feb 19 06:15 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Here is an easy one. - Jorge Tavares, Jan 29 (starting this thread) (My first concession will be to not mention that again.) :p So I've gotten the quote out of the way, but before making my second-to-last move, a proper epic work of prose also needs a preface, for which I offer the following... The existing IFELSE() implementation, while appropriate for the original intended use, is not well designed to handle complex result expressions. As with most A-ShellBASIC operations, it uses the postfix logic of first pushing the arguments on to the stack (and in the process evaluating them), and then dealing with the operation. So in the RUN code, IFELSE(expr1,expr2,expr3) looks like: ,
which means that both and get evaluated before we decide which one to return as the result of the function.
That makes sense for simple arguments, like:
? IFELSE$(qty'on'hand > qty'ordered, "in stock", "backordered") But it doesn't make much sense for complex arguments like...
Phone = IFELSE(Fn'Get'Phone(Cus), Fn'Get'Phone(Cus), Fn'Find'Phone'In'Order'History(Cus)) ... not just because of the redundant Fn'Get'Phone(Cus) operation, but because it would evaluate the Fn'Find'Phone'In'Order'History() function before even knowing if the result was going to be used.
There may be sufficient justification to re-design the way IFELSE() is compiled and executed, but it would obviously cause backwards compatibility concerns, so for now I'm inclined to leave it as is, and focus on making an efficient, elegant, flexible, and economic alternative function for use in cases where the existing IFELSE() isn't well suited.
With that introduction, here's my proposal:
We create a new pair of functions:
IFE(expr1,expr2) IFE$(expr1$,expr2${,flags})
(IFE obviously is short for IF Else, and was given a slight edge over IFTE and IFELSE2 for its brevity, which aside from being its own reward, is consistent with the fact that the new function is a more compact version of the original.)
IFE(expr1,expr2) is similar to IFELSE(expr1,expr1,expr2), and IFE$(expr1$,expr2$,) is similar to IFELSE$(expr1$#"",expr1$,expr2$) except:
a) The new versions would be compiled and executed in such a way as to avoid both the redundant evaluation of expr1, and the unnecessary evaluation of expr2 (unless it really is necessary);
b) In the numeric version, expr1 can be any type of expression, but has to make sense both as a .TRUE/.FALSE condition and as a return value. So while a relative expression, i.e. IFE(A>B,C) is legal, it would return .TRUE (-1) if A>B. So if probably doesn't make sense to specify a relative expression for expr1.
(I realize this isn't as flexible as some of the preceding ideas involving general expressions and special symbols to pass values from the test expression to the result expressions, but I think it's a reasonable trade-off between flexibility and elegance/economy.)
c) The same limitation applies, even more so, to the string version, where expr1$ has to serve both as a truth value and a return value. So it would make even less sense to use a relative expression here, e.g. IFELSE$(A$>B$,C$), since the return value if A$>B$ would be "-1".
d) The truth value of a numeric expression is simply 0=FALSE and anything else is TRUE. But it probably doesn't make sense to use this standard for the string expression in IFE$(expr1$,expr2$). Instead, by default we'll consider expr1$ TRUE if not empty (i.e. expr1$<>"", i.e. not blanks or null).
e) For the problem case of zero-same-as-empty, we can use the optional third argument to specify one of the following flags:
define IFE_ZE = &h0001 ! expr1$ true if zero or empty
define IFE_NZE = &h0002 ! expr1$ true if not zero and not empty So...
IFE$(a$,b$) ! if a$ # "" then a$ else b$
IFE$(a$,b$,IFE_ZE) ! if a$ = "" or a$ = "0" then "" else b$
IFE$(a$,b$,IFE_NZE) ! if a$ # "" and a$ # "0" then a$ else b$ Note that as written above, the IFE_ZE case (previously referred to by different function names, such as IFEOZ$ above) shows it returning "" rather than a$ (which could be "0" or " "). I'm not completely sure I like that, due to quirkiness and implied lack of symmetry with the IFE_NZE case, but you can consider that my second concession.
Also note that the syntax/design allows for additional flags to be defined, hopefully eliminating the desire for additional functions to handle additional variations.
Also, just to address your IFEOZ$ example...
! Instead of:
! if ifeoz$(phone$, phone$)="" then call ask'for'phone'number
! Use:
if ife$(phone$, 1, IFE_ZE)="" then call ask'for'phone'number
! Also consider:
phone1$ = ife$(phone2$,Fn'AskForPhone(),IFE_ZE)
phone$ = ife$(phone$,phone$,IFE_ZE) With either IFE$ or IFEOZ$, in the first statement you could use anything for the 2nd expr as long as it wasn't "". Your version is more compact, but requires a new function, whereas mine doesn't. They're about equal in that regard, but the flags argument gives the IFE$() version a flexibility advantage for additional cases, whereas he IFEOZ$() would require a new function for each new variation.
That said, as my third concession, I'll offer to throw in a new flag on the EDIT() function to convert a string containing blanks or "0" to "", which might be more straightforward for the last example above:
! instead of:
! phone$ = ife$(phone$,phone$,IFE_ZE)
! use:
phone$ = edit$(phone$,EDITF_SPZ) (It's a safe concession, since I'm guessing you prefer the ife$() version, but the ability to remove spaces and/or a "0" from a string seems like a sensible addition to the edit$() function, and the edit$() function, for most people, would be the more straightforward way to clean up the string.)
And now the world awaits your (final) move!
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24110
13 Feb 19 09:10 AM
|
Joined: Jun 2001
Posts: 713
Steven Shatz
Member
|
Member
Joined: Jun 2001
Posts: 713 |
Is this correct:
phone1$ = ife$(phone2$,Fn'AskForPhone(),IFE_ZE)
i.e., if phone2$ is zero or null, return null; else ask for phone number.
Shouldn't it be:
phone1$ = ife$(phone2$,Fn'AskForPhone(),IFE_NZE)
i.e., if phone2$ is neither zero nor null, then return the phone number (phone2$); else (if it is empty) ask for phone number.
I point this out because if even you are getting this backwards, what chance do we mere mortals have of getting this right?
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24111
13 Feb 19 11:14 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Hey Jack, this is a reply to Steven so, my move in the countdown is still available The exact meaning for: phone1$ = ife$(phone2$,Fn'AskForPhone(),IFE_ZE) is: does phone2$ is zero or empty? if so ask for the phone number otherwise return it. But your doubt is very pertinent because the name of the function is not enough meaningful about the logic behind it. With all the discussion around this (easy) topic, analyzing all the possible variants to make it more powerful and worth any development, probably we lost the focus in one of the main goals, which is: evaluate zeros and empty strings. I tried to translate that to the name of my functions using FN'if'else'value$() and FN'if'else'value() Maybe a slight change in the proposed name IFE$ to IFVE$ (IF Value Else) could cause less confusion? Because this is about evaluate zeros and blanks, not a generic condition, to that purpose we have already IFELSE$/IFELSE. I thought this clarification could be useful now but I'm saving my last allowed move to add more substance, if needed
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24112
13 Feb 19 11:39 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Jorge: Like yours, this is a sidebar comment to Stephen, and not to be considered a move taken out of turn! It was also mostly written before yours, so doesn't directly respond, but I did insert a couple of relevant comments, including this one, before clicking "Add Reply"Stephen: Wow, you passed that test! (And I failed it!) And your point is well-taken, as I struggled (and obviously continue to struggle) with the getting the logic/direction right in the 2-expression string version. Initially I tried to avoid the if-zero test for that reason, because it works backwards from the normal if expr logic which is true if expr is not zero (and by extension for strings, not empty). And that was also why I initially wanted to call this new variation IFNOT(). And that's also why IFE$(a$,b$) is equivalent to if a$ NOT EQUAL to "" then a$ else b$. But at some point I guess I gave in (and should have noted that as yet another concession) to Jorge's idea that it was sometimes useful to test if a string expression was zero-or-blank (as opposed to not-zero-and-not-blank). Maybe the IFE_ZE and IFE_NZE flags don't help improve the readability/understandablity, but the problem didn't seem any less insidious, at least to me, with the variations in which the function name and condition were bound together, e.g. IFEOZ$(), IFEMPTY$(), etc. And for that matter, I'm not even sure how you even accomplish the same operation with the IFEOZ$() version, since... phone1$ = ifeoz$(phone2$,Fn'AskForPhone()) ... has the same problem as my IFE_ZE version. We'd need to have yet another function, say, IFNEOZ$(), for not empty or zero, i.e. phone1$ = ifneoz$(phone2$,Fn'AskForPhone()) Or go back to Jorge's version... if ifeoz$(phone2$, phone2$)="" then call Fn'AskForPhone() Note: after reading Jorge's post, it appears that I may have again reversed, or re-reversed, his if/then and/or else/if semantics. So maybe we're all confused on the interpretation of IFE_NZE and IFE_ZE, and I may have been reading Jorge's IFEOZ$() backwards from the way he intended it. To me, the 2-expression IFE() should merge the first 2 expressions of the 3-expression IFELSE, i.e. if expr1-meets-condition then expr1 else expr2 But it sounds like Jorge sees it the other way, at least in the case of zero-as-blank.Without question, of the four functions that encapsulate if-then logic within an expression -- IFELSE(), IFELSE$(), IFE() and IFE$() -- the last one is definitely the trickiest to use properly, and doubly so with the zero-as-empty variation thrown in to the mix. Other than intellectual stimulation provided by deciphering IFE$(), the logic in question here would be more clearly rendered as: if edit$(phone2$,EDITF_SPZ)="" then
phone1$ = Fn'AskForPhone()
else
phone2$ = phone1$
endif But to be fair to IFE$(), let's reconsider the more complex example I gave previously for the shortcomings of IFELSE$(). And let's make it slightly more interesting: the task is to print the phone number retrieved from the central contact list, unless it comes back blank or 0, in which case we print one found by searching the order history. And to make it more realistic, it's just one of several variables in a formatted print statement. And to acknowledge the way these things evolve, let's imagine that we had an earlier version which was simply this... ? #ch using mask1, cno, cname, Fn'GetContactPhone$(cno,cname), czip To insert the upgraded logic, we could do something like: map1 tempphone,s,10
temphone = Fn'GetContactPhone$(cno,cname)
if tempphone = "" or tempphone = "0" then
tempphone = Fn'Find'Phone'In'Order'History$(cno)
endif
? #ch using mask, cno, cname, temphone, czip That's a lot of extra logic, and requires defining a temporary variable, compared to... ? #ch using mask, &
cno, cname, &
ife$(Fn'Get'Contact'Phone(cno,cname), Fn'Find'Phone'In'Order'History$(cno), IFE_NZE), &
czip That's still an eyeful, but the technique meshes well with the natural programming process, especially when upgrading older logic, where the natural tendency it to minimize disruptions to the surrounding code. And assuming reasonable formatting, it isn't any harder to decipher later than the traditional approach. I'd certainly be open to dropping the IFE_ZE flag entirely, eliminating the possibility of mistaking it for IFE_NZE as I did. And I don't think we'd lose much utility there, because it seems that in nearly every case where you are testing for zero-or-blank, the idea is to use the expression being tested if it's NOT zero-or-blank, otherwise to use some alternate. (This may be why Jorge's IFEOZ$(e1,e2) seems to act like ifeoz$(e1) then e2 else e1, rather than ifeoz$(e1) then e1 else e2 as I would prefer, for consistency with the other variants.) Anyway, for most of the other variations, especially the numeric ones, both IFELSE() and IFE() are, I think, very convenient to use, generally easy to understand (if not abused), and therefore helpful to the programming process. But like all of the tools available, ultimately it's up to you to wisely choose and use those that you feel most comfortable with. (And are able to master! For us mortals, that may not include IFE$! :rolleyes: )
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24113
26 Feb 19 11:41 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Hello Jack, Hopefully you have solved already the (easy) LSX improvement so, your brain is now relaxed and ready to process my last move on this (easy) topic. Starting from the bottom and thanking for your concession on this off topic improvement, I buy the new flag to remove "zeros and blanks" from strings in edit$(). Regarding the proposed new functions, I think there is nothing completely new to say by now - did I hear a sigh of relief? :p The only thing I'm going to do here, is an attempt to clean up the decisions and removing all the diversions around the discussion to make it understandable for someone starting to read this post from here. Goals: 1. Embed in A-Shell an numeric function with 2 arguments that returns the 1st argument if non-zero otherwise returns the 2nd argument. Example: map1 a,b,1 map1 b,b,1,100 ? numeric'function(a, b) 100 ----- I think the above should be 1, since the 1st argument (a=1) is non-zero and thus the function should return it rather than the 2nd argument. - [jm] Today I didn't distilled any Gin neither had any Caipirinha so, it must be my glasses not allowing me to see where a=1[jt] 2. Embed in A-Shell an string function with 3 arguments that returns the 2nd argument if the 1st argument is empty otherwise returns the 1st argument. Example: map1 a$,s,0 map1 b$,s,0,"hello world" ? string'function$(a$, b$) hello world 3. The 3rd argument in the function described in 2. adds a variant; if set to 1, the function returns the 2nd argument if the 1st argument is zero or empty otherwise returns the 1st argument. Example: map1 some'flag,b,1,0 map1 a$,s,0 map1 b$,s,0,"hello world" ? string'function$(some'flag, b$) hello world some'flag = 1 ? string'function$(some'flag, b$) 1 ? string'function$(a$, b$) hello world <----- It's a bit confusing to refer to the above as a string function with 3 arguments since, since in all cases the function is only passed two arguments. If the distinction is meant to be based on the type of the 1st argument, then "zero or empty" wouldn't apply because the numeric form doesn't support "empty". It seems like the flag argument should really be a 3rd argument, or a different function, but now I may be going down the rabbit hole again. - [jm] I agree, it was a confusing example, I shouldn't have separated the examples in 2. and 3. so, the 3 arguments functions is string'function$(arg1$, arg2$, flag) where the current possible values for flag is 0 or 1. a) flag=1 : arg2$ is returned if arg1$="0" or arg1$="" otherwise returns arg1$ b) flag=0 (or omitted) : arg2$ is returned if arg1$="" otherwise returns arg1$
In my initial examples, I've used the numeric some'flag variable for arg1$ just to show the typical usage for flag=1 in the 3rd argument.
[jt] Arguing: At some point I realized two things along my programs: 1. An huge percentage of IF_THEN_ELSE were to check if a string is empty or a number is zero 2. Very often, the result of the IF_THEN_ELSE is to embed in the middle of some command executed after the condition and, sometimes, forcing the use of additional variables only to receive that result, like: map1 a$,s,0 if a$#"" then a$ += CRLF$ endif a$ += "there are negative values" … if a$#"" then a$ += CRLF$ endif a$ += "there are products w/o prices" … if a$#"" then msg$ = a$ else msg$ = "everything is fine." endif ? "End of processing: ";CRLF$;msg$ Using FN'if'else'value$() to evaluate if the string is empty, the above example could be replaced by: map1 a$,s,0 a$ += IFELSE$(a$#"", CRLF$);"there are negative values" … a$ += IFELSE$(a$#"", CRLF$);"there are products w/o prices" … ? "End of processing: ";CRLF$;FN'if'else'value$(a$, "everything is fine.") ----- Are you sure the third test isn't supposed to be if a$#"" then msg$=a$ else msg$="everything is fine"? In other words, you print the warnings if applicable, else print "everything is fine"? But if so, then all of these examples reinforce my assumption that overwhelmingly these tests involve using the first argument if not zero/blank, else using the 2nd argument. It's difficult for me to come up with a good example of where you want to test the first argument and then use it if empty, and that difficulty holds me back from really embracing the need for an option to reverse the logic, i.e. "if A blank then A else B".
I fully acknowledge that your FN'if'else'value$(a$, "everything is fine.") example cannot easily be replaced by the existing IFELSE$() without duplicating the first argument, which would be undesirable if it were a function, i.e. we wouldn't want to do this -- IFELSE$(fn'foo(a$),fn'foo(a$),"everything is fine")). But in the reverse case, where you want to use the 1st argument if it is blank or zero, there's no need to duplicate it for the result because we can just use the literal "". (We do however need an easy way to test for zero or blank.) -[jm] thank you for the correction on the third test, I've changed it in place.
I think the main reason we didn't have a deal on the first minute about this is explained by your sentence "... the need for an option to reverse the logic, i.e. "if A blank then A else B"" because I never want that, I don't care about the THEN, I'm just interested in 'values' not blanks'
[jt] (as noted, I'm using in the example the existing IFELSE$() to reinforce the benefits of such kind of functions) So, my first step was to write a collection of functions to handle this kind of simplifications. Considering the proliferation of this functions in my programs they almost merged in the ABasic language and, in some heavy procedures, an embedded function should perform better so, that's the reason why I ask to reproduce my functions in native A-Shell functions. Naming: I must confess that, the names I've chosen for my functions was not the most brilliant FN'if'else'value$() so, now it's the moment to do it right. We can find easily how difficult is to combine meaningful names with short names so, I think we should opt for short names that are abbreviations of meaningful usage, here are my proposals: a) ELSEIFB$(a$, b$) - ELSE IF Blank ELSEIFB$(a$, b$, ORZ) - ELSE IF Blank OR Zero ELSEIFZ(a, b) - ELSE IF Zero b) IFBE$(a$, b$) - IF Blank Else IFBE$(a$, b$, ORZ) - IF Blank Else OR Zero IFZE(a, b) - IF Zero Else c) EIFB$(a$, b$) - Else IF Blank EIFB$(a$, b$, ORZ) - Else IF Blank OR Zero EIFZ(a, b) - Else IF Zero This said, I'm sure you will pick the best from here and join with your own ideas and I completely trust in the final solution which is now in your hands for the final round. Consider my post closed so, if you think safer you can ignore what's next because in contradiction to what I said at the beginning, it brings new stuff into the table - did I hear a sigh of "here we go again"? :p Several times during the current discussion I was about to bring this up but decided to keep it away until we reach an agreement on the initial question. In my "if'values" collection I have another very useful function FN'concatenate'if'value$(a$, a'preffix$, a'suffix$): If a$ is not empty, return (a'preffix$+a$+a'suffix$) otherwise return empty. Example: ? "Name: ";name$; FN'concatenate'if'value$(age$, " [", "]"); FN'concatenate'if'value$(state$, ", "); FN'concatenate'if'value$(profession$(1), ", "); FN'concatenate'if'value$(profession$(2), " / ") DATA name$,age$,state$,prof1.,prof2 George,53,,Programmer,Distiller Joaquim,34,LA,Surfer Maria,,,, Ana,23,,,Model, Result: George [53], Programmer / Distiller Joaquim [34], LA, Surfer Maria Ana [23], Model I brought this in case you see any benefit to embed this one or you may consider better keep it as function I'm out of the game at this point and waiting for your final move
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24114
26 Feb 19 05:31 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
I'm afraid your final move is less like "Checkmate!" or "Gin!" and more like the CD NASA sent into outer space (with enough information to keep even the super-intelligent aliens busy for years trying to decipher it. Which may have been part of the NASA's objective -- delaying the inevitable Earth invasion -- but surely can't be part of yours.) :rolleyes:
Unfortunately I haven't yet finished the (easy) LSX enhancement, so my brain isn't quite in condition to absorb all of the ramifications of your move, but after reading it once, I do have the sensation that there may be a couple of typos, so I'm going to take the liberty of inserting a couple of comment/corrections directly into your post (marked with -[jm]) which you can accept or reject without violating the rules. (Consider it that we're in the penalty time at the end of the soccer match.)
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24115
26 Feb 19 08:11 PM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Thanks for the chance to reply, I've added the bold sentences ended by [jt] after your comments.
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24116
01 Mar 19 10:44 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Having taken some time to distill all these ideas, I'm ready to present my plan. It doesn't necessarily cover every possibility, but as the saying goes, we don't want to make the perfect be the enemy of the good. (And we don't want the weekend ruined by the suspense and uncertainty of not knowing how the series will end.) First, a recapitulation for those who might have missed some prior episodes. Here's what we have now: I think the main reason we didn't have a deal on the first minute about this is explained by your sentence "... the need for an option to reverse the logic, i.e. "if A blank then A else B" because I never want that, I don't care about the THEN, I'm just interested in 'values' not blanks'
I'm reading that as the offer to ignore the 'reverse case', which eliminates the need for a lot of variants (either flags or differently named functions) to handle the equivalent of the various logical conditions equal, not equal, greater, less than, etc. So in other words, what ALL of these functions have in common: a) We test the 1st argument b) If the test "passes", we return it c) Else we return the 2nd argument There is still some ambiguity though over what it means for the test to "pass". In the numeric cases -- ifelse() and ife() -- it's simple: zero fails, non-zero passes. This is consistent with the traditional handling of IF EXPR THEN ... statements. (If EXPR is a relative expression, then it returns .TRUE, i.e. -1, if true, else .FALSE, i.e. 0. And if EXPR is just a value, then likewise 0 is considered false and anything else is treated as true.) The string case is where we wander into new territory. The following is not legal... IF A$ THEN ... ... because here is no standard mapping from string values to truth conditions. As you point out though, informally and analogously, it seems reasonable and intuitive to treat the above as equivalent to ... IF A$ # "" THEN ... So... ? ife$(a$, b$) ! if a$ # "" then ? a$ else ? b$ Again, note that we are not handling the reverse condition, i.e. the one where you want to actually return the "" when a$ = "". The assumption here is that, as you say, we are interested in values, not blanks. Also note, that consistent with the way strings are compared in A-ShellBASIC, trailing blanks are not significant, so " " is treated the same as "". So far, I don't think there is any controversy. But there is one remaining problem: that for various reasons, including the tendency for values to get converted back and forth between numeric and string representations, it may make sense, in some case to treat zero the same as blank, i.e. "0" (with optional trailing blanks) the same as "". For that we need an option, either a new function, or a flag. I considered IFEZ(a$,b$), but eventually decided to go with the flag instead. Even though it is a bit more verbose, it provides more flexibility while avoiding proliferation of new language functions. So the complete syntax of the new functions is: ife(a, b {,flags})
ife$(a , b${flags}) There are no flags defined for the numeric version, but it leaves open the possibility of additional ways of evaluating the first argument in the future; maybe even implementing the reverse condition. For the string version, the only flag is IFE_NZB (=&h0001; Not Zero or Blank), which means to treat "0" same as blank, e.g. ? ife$(a$, b$, IFE_NZB) ! if (a$#"" and a$#"0") then ? a$ else b$ Hopefully that covers the "easy" :rolleyes: in a way that is acceptable and useful to all. The only thing left is the implementation (which hopefully will be a lot easier than the design). Also, a couple of postscripts: 1) The implementation will be such that the second argument will not be evaluated unless it is actually needed, i.e. unless the test fails. (Currently, IFELSE(expr,a,b) acts like a normal expression, meaning that the arguments are all evaluated and pushed onto the stack before the operator, or in this case the function logic, is executed. Obviously that results in both a and b being evaluated when in the end, only one is needed. After implementing new IFE(a,b), I may go back and change the IFELSE implementation to use the same "short-circuit" optimization. 2) I don't want to ignore your concatenate function, which is also very useful. But I don't want it to get in the way of the more important IFE(). Once that's done, we can revisit an IFCAT$(a$, prefix$, suffix$), possibly in conjunction with a review of other string-manipulation shortcuts to improve A-ShellBASIC's suitability for modern string-processing tasks.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24117
01 Mar 19 01:16 PM
|
Joined: Sep 2002
Posts: 5,471
Frank
Member
|
Member
Joined: Sep 2002
Posts: 5,471 |
I think you have finally solved this age-old dilemma... Now a clever man would put the poison into his own goblet, because he would know that only a great fool would reach for what he was given. I am not a great fool, so I can clearly not choose the wine in front of you. But you must have known I was not a great fool. You would have counted on it, so I can clearly not choose the wine in front of me. You've beaten my giant, which means you're exceptionally strong, so you could've put the poison in your own goblet, trusting in your strength to save you, so I can clearly not choose the wine in front of you! But you've also bested my Spaniard, which means you must have studied, and in studying you must have learned that man is mortal, so you would have put the poison as far from yourself as possible, so I can clearly not choose the wine in front of me.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24118
01 Mar 19 04:29 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Uh, exactly. It's too bad you didn't speak up earlier Frank - you could have saved us a lot of time! (Then again, as Yogi Berra might have said, the journey is often the best part of the trip.)
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24120
09 Mar 19 04:32 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
A week ago, inspired by Frank, I wrote here a nice epic poem to celebrate the end of the saga but when hit the [Add reply] button the poor internet connection gone, the browser frozen and I lost the post as well as my inspiration so, I picked my cell phone from the top of the tree where it was struggling to get some 1G signal and decided to only enjoy the nature around me for the rest of the week Now, back to half-civilization but with nice 4G signal, I had the chance to put my hands on this precious and I'm really happy. It's not exactly a new feature in my programs, considering that I was already using the corresponding Functions, but now I know I'm doing it the right way. This said, Jack, I leave here my big thank you for all the energies, time and patience you gave to this "simple" project and I hope that others can enjoy and benefit with this kind of stuff as much as I do.
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24121
09 Mar 19 10:57 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Hopefully one day in the distant future, some digital archeologists will discover your long lost epic poem drifting about in the 0.25G space and enrich the world with their discovery. For now, we'll content ourselves by coding one step closer to perfection. And not to belabor the point, but lest we forget what is truly "right" about the IFE(a,b) refinement (compared to your pioneering original), is that, aside from its brevity, and from the elimination of the duplicate evaluation of the first argument if it turns out to be the one needed for the return, is that it avoids any evaluation of the second argument at all, except when actually needed. In this, the b argument is like the ELSE clause in an IF ... ELSE b statement, and much better than your original user-function implementation (which evaluates both arguments first before passing the result to the function). The situation where I've already started using it is in a scenario where there are two approaches to returning a particular value. For example, in one case we have two possibilities for determining the price of an item - either the price that was agreed to as part of a negotiated contract, or, lacking such a contract, we use the price that was item was last sold to this customer at. The latter routine is costly because it requires a search of the sales history, so we want to avoid it unless necessary. Previously that would have required a separate IF/THEN/ELSE statement and temporary variable; now I can encapsulate both into a print statement like...
print using mask1, &
cus, &
sku, &
qty, &
ife(fn'contract'price(cus,sku),fn'last'sales'price(cus,sku))*qty, &
shipdate (Now it's time to place our side bets as to when Herman gets over his new infatuation with functions and declares his love for the IFE!)
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24122
10 Mar 19 12:06 PM
|
Anonymous
Unregistered
|
Anonymous
Unregistered
|
I'm so confused My concern now is that since Jack asked about the size of pricing variables in the standard I have to work with, they changed it from s9(6)v99 to s9(9)v99. I have tried to change everything to I6 and b6 variables, but now have to go back thru the code and follow thru a ton of variables to be sure they all fit. Jack, I wish you hadn't asked! Will ife(a,b) do this for me?
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24123
10 Mar 19 01:01 PM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
IFE(a,b) may be the cat's pajamas, but I'm not sure it can overcome problems of variable size/precision overflow. But any case I think the issue falls more in the thread "L" where I'm going to respond.
Last edited by Ty Griffin; 16 Aug 19 02:44 PM.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24124
26 Mar 19 12:55 AM
|
Joined: Jun 2001
Posts: 3,406
Jorge Tavares - UmZero
OP
Member
|
OP
Member
Joined: Jun 2001
Posts: 3,406 |
Hello Jack, I don't know if I'm confusing things but, can you please run the code below and confirm if it's the expected result on the second trace for "preco"?
map1 a,b,1,1
map1 b$,s,10,"234"
map1 c$,s,10
map1 preco,f,6
preco = ifelse(a=1, ife(c$, b$), 0)
Trace.Print "if.bas preco=["+preco+"] ifelse(a=1, ife(c$, b$), 0)=["+ifelse(a=1, ife(c$, b$), 0)+"] a=["+a+"] ife(c$, b$)=["+ife(c$, b$)+"] c$=["+c$+"]"
Trace.Print "if.bas b$=["+b$+"]"
preco = ifelse(a=1, ife(b$, c$), 0)
Trace.Print "if.bas preco=["+preco+"] ifelse(a=1, ife(b$, c$), 0)=["+ifelse(a=1, ife(b$, c$), 0)+"] a=["+a+"] ife(b$, c$)=["+ife(b$, c$)+"] b$=["+b$+"]"
Trace.Print "if.bas c$=["+c$+"]"
PS: Hopefully it's not the European air confusing my brain Thanks
Jorge Tavares
UmZero - SoftwareHouse Brasil/Portugal
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24125
26 Mar 19 09:46 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
The good news is that it's not the European air. The bad news is that it appears to be a bug. Or maybe the California air? You're pushing the boundaries a bit by embedding an IFE() inside of an IFELSE(), and further trying to confuse me by passing string parameters to the numeric IFE() function. But ultimately the problem just seems to be that the embedded IFE() is messing up the stack for the IFELSE(). If you assign the result of IFE(C$, B$) to another variable and then use it in the IFELSE(), the problem goes away, e.g. iferes = ife(c$, b$)
trace.print iferes
preco = ifelse(a=1, iferes, 0)
trace.print preco The above traces would then show the correct: I'll get to the bottom of it sometime today. In the meantime, you may have to stoop to using an intermediate variable.
|
|
|
Re: IFELSE, IFELSE$ functions improvement suggestion
#24126
27 Mar 19 12:27 AM
|
Joined: Jun 2001
Posts: 11,794
Jack McGregor
Member
|
Member
Joined: Jun 2001
Posts: 11,794 |
Ok,I think the problem is resolved in this beta update: ash-6.5.1659.0-w32c-upd.zip compil-6.5.897-w32.zip ash65notes.txt Note that programs compiled using the updated compiler, which contain IFE(), will require 6.5.1659.0 to run. (Normally for a patch like this we would have updated the patch level, i.e. 6.5.1658.2, but the new compiler minimum version feature only works at the edit level, i.e. 1659.) I'm going to let it shake out for another day or two before posting updates for other platforms.
|
|
|
|
|