Does RPGLE has a ternary operator? - ternary-operator

I did find old posts on forums about this question. But as they were old, I was wondering if it changed.
I know that, back maybe in 2004, RPG did not have ternary operator. Having work a bit in C# and Java, I did use them and I like them. I was wondering if RPGLE eventually included them, somehow.
In C#, a ternary operation looks like this :
myvar = var1 > var2 ? var1 : var2;
So if var1 is greater than var2, myvar will get var1. This was a basic example.
So... do you guys know if there's a ternary operator in RPGLE and if there is, what's its syntax?

No, RPGLE does not have ternary-operators.
http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzasd/expropt.htm

No but you can use similar for setting an indicator (boolean) to true or false. I do it all the time. If below numberOne variable is defined as an integer with initial value of 1 and numberTwo with initial value of 2.
*in99 = numberOne < numberTwo
99 = true because above statement tests true, else 99 would equal false. This syntax replaces below
if numberOne < numberTwo
*in99 = *on
else
*in99 = *off
endif

Related

Does `if` work only in combination with `else` in Series.apply(lambda x)?

I'm getting a SyntaxError for:
housing['Lot Area'].apply(lambda x: x + 50000 if x > 20000)
When I add else, the code runs fine:
housing['Lot Area'].apply(lambda x: x + 50000 if x > 20000 else x)
Does if only work in combination with else here? I'd like to increment x with 50000 only if x > 20000 -- otherwise I'd like x to remain unchanged. I find the else part a bit redundant here. Besides the first question before, is there any way to write this code without the else part?
Base on your description , even apply is not need here
housing.loc[housing['Lot Area']>20000,'Lot Area']+=50000
Comment from Alex :
if the if statement resolves to False for a value, then apply() doesn't return and just lets the value in the Series as it is
you're getting a SyntaxError because you are typing invalid syntax. the ternary operator must be used like
expression if bool else other_expression

WITH statement for structures

In some languages there exist syntax to simplify method and property access of a single object. As an example from VB.NET:
With foo
.bar()
.reset(true)
myVar = .getName()
End With
Or as Jagger points out, in Pascal:
With myBookRec do
Begin
Title := 'Some Book';
Author := 'Victor John Saliba';
ISBN := '0-12-345678-9';
Price := 25.5;
End;
Now I often find myself setting a ton of properties on a given structure. Take for instance the classic case of setting a field catalog:
LOOP AT lt_fieldcat INTO ls_fieldcat.
CASE ls_fieldcat-fieldname.
WHEN 'COLUMN_1'.
ls_fieldcat-edit = ''.
WHEN 'COLUMN_2'.
ls_fieldcat-edit = 'X'.
ls_fieldcat-outputlen = 20.
WHEN 'COLUMN_3'.
ls_fieldcat-edit = abap_true.
ls_fieldcat-drdn_hndl = '2'.
ls_fieldcat-drdn_alias = 'X'.
ls_fieldcat-outputlen = 5.
ls_fieldcat-col_pos = 1.
ls_fieldcat-scrtext_s = text-f01.
ls_fieldcat-scrtext_m = text-f01.
ls_fieldcat-scrtext_l = text-f01.
ENDCASE.
MODIFY lt_fieldcat FROM ls_fieldcat.
ENDLOOP.
Is there any way to replicate WITH-like functionality in ABAP?
If not, is there a better way to replicate this behaviour?
I've actually considered replicating the "return this" method daisy chain for a field catalog class. This seems like a WITH-alternative but I'm not entirely sold on its usefulness, given that you'd have to maintain a class for every structure.
ls_fieldcat = zcl_fieldcat=>new( ls_fieldcat )->edit('')->outputlen(20)->col_post(1)->get().
With the new ABAP syntax you could simplify your code a bit with VALUE #( ... ) statement (or VALUE #( BASE ls_fieldcat ...) for modifying a structure with already assigned fields) which can be used for emulating WITH in ABAP. Not a perfect solution, especially if one has a CASE structure like yours but surely neat for initalizing a structured variable.
LOOP AT lt_fieldcat INTO ls_fieldcat.
CASE ls_fieldcat-fieldname.
WHEN 'COLUMN_1'.
ls_fieldcat = VALUE #(
BASE ls_fieldcat
edit = ''
).
WHEN 'COLUMN_2'.
ls_fieldcat = VALUE #(
BASE ls_fieldcat
edit = 'X'
outputlen = 20
).
WHEN 'COLUMN_3'.
ls_fieldcat = VALUE #(
BASE ls_fieldcat
edit = abap_true
drdn_hndl = '2'
drdn_alias = 'X'
outputlen = 5
col_pos = 1
scrtext_s = text-f01
scrtext_m = text-f01
scrtext_l = text-f01
).
ENDCASE.
MODIFY lt_fieldcat FROM ls_fieldcat.
ENDLOOP.
I don't think the daisy-chain approach is that useful - it's not much easier to read, and if anything goes wrong, it's awfully hard to debug. Personally, I'd probably use a "multi-setter" with multiple optional attributes:
lr_my_field->set_attributes( edit = abap_true
title = 'foo'
* ...
bar = 'baz' ).
That being said, I don't have a problem with the code in your question at all. Clearly readable, simple data handling. Just put it away into a method named PREPARE_FIELDCATALOG and everone knows what to expect.

Whats wrong with this VB code? [closed]

Function nameTonumber(name As String)
Dim number As Integer
number = 0
If (name = "Aa") Then
number = 1
ElseIf (name = "Bb") Then
number = 2
ElseIf (name = "Cc") Then
number = 3
Else
number = 0
End If
End Function
Change the first line to: Function nameTonumber(name As String) As Integer
Above End Function add:
nameTonumber=number 'If you are using VB6.
return number 'If you are using VB.NET
The parentheses around the If conditionals are redundant.
The Else is redundant – number is already zero.
You do not return any value.
You did not specify a return type.
You can use Select Case to make multiple tests of this form marginally more readable.
And a lot more if this is VB.NET code.
On a stylistic note, your code takes up too much space: remove the unnecessary empty lines, they do not serve to make the code more readable – on the contrary.

How do you label output variables in an IDL FOR loop for further processing outside the loop in the same program?

I have a FOR loop like this:
FOR k = 1,216 DO atom = G[*,0:*:(215+k)] END
What I would like to be able to do is to store in memory the array for each atom, say, atom_k and then call these different variables to perform further operations outside the FOR loop.
Conceptually, I want to label the "atom" variable with the "k" counter somewhat like this:
FOR k = 1,216 DO atom(k) = G[*,0:*:(215+k)] END
Of course, this doesn't work because "k" is no longer a label in this case. Does anyone know?
I assume you're talking of IDL, the language developped by ITTVis.
I don't understand the way you index G nor the END instruction, but I'm not working with latest version.
Try the EXECUTE command. This allows you execute statements at runtime.
FOR k = 1,216 DO status = EXECUTE('atom_'+strtrim(k,2)+' = G[,0::(215+'+strtrim(k,2)+')]') END
STRTRIM converts integers to string without blanks around.
Looks like you're trying to create a ragged array. What you want is a data structure like a list of lists, where the sublists have a different number of elements. This is version-dependent, but it looks like the list() function introduced in 8 would do exactly this.
atoms = list()
FOR k = 1,216 DO atoms.add, G[*,0:*:(215+k)] END
I'm actually not positive that list() is the proper way to create an empty list. Can't debug it because I'm not running 8.
There are a few options I'm aware of:
Of the following, #1 is the closest to what you requested, but I believe that #3 offers the best blend of usability for your mentioned goal, speed, and compatibility with different versions of IDL.
1) Using scope_varfetch with the /enter keyword:
pro foo, G
for k = 1, 216 do begin
varname = 'atom' + strtrim(k, 1)
(scope_varfetch(varname, level=-1, /enter)) = G[*, 0:*:(215 + k)]
endfor
end
This creates variables named atom1 through atom216 in the calling routine, or in $MAIN$ if executed interactively. I think this is the closest to what you requested. You can also access those variables directly from the calling function by name using syntax either like print, atom5 or like:
for k = 1, 216 do begin
varname = 'atom' + strtrim(k, 1)
print, scope_varfetch(varname)
endfor
2) Using an IDL 8.0+ List object:
atom = list(length=216)
for k = 0, 215 do atom[k] = G[*, 0:*:(216 + k)]
Note that the list is 0-indexed, meaning that the first element is zero, instead of one. Use atom[0] to access the first atom, and so on. To access the first index of the first atom, surround with parentheses and use an additional set of brackets to index: (atom[0])[0, 0]. The parentheses are necessary due to IDL's unusual order of operations.
3) Using a pointer array:
atom = ptrarr(216)
for k = 0, 215 do atom[k] = ptr_new(G[*, 0:*:(216 + k)])
Or with slightly different syntax:
atom = ptrarr(216, /allocate_heap)
for k = 0, 215 do *atom[k] = ptr_new(G[*, 0:*:(216 + k)])
This is the most efficient and highly compatible way to do it. Pointers and the associated ptrarr and ptr_new functions have been around since IDL 5.0, and they are much more efficient than lists, hashes, or scope_varfetch.
Note that like the list, this is 0-indexed. Also, to access these values you have to "deference" them with a *, as in print, *atom[0] to print the first atom array, or print, (*atom[0])[0, 0] to print the first element of the first array and so on. The parentheses are required for the same reason as with the list. Setting values is also possible with syntax like (*atom[0])[1, 15] = new_values.
4) Using an IDL 8.0+ hash:
atoms = hash()
for k = 1, 216 do begin
name = 'atom' + strtrim(k, 1)
atoms[name] = G[*, 0:*:(215 + k)]
endfor
These can be referenced like atoms['atom0']. I don't think this is probably the best option in this case, since an array is more efficient, but it is very useful if there are additional string names you need to use, or if the data indexes are sparse.
5) Building a structure with create_struct:
atoms = !null
for k = 1, 216 do begin
tagname = 'atom' + strtrim(k, 1)
atoms = create_struct(atoms, tagname, G[*, 0:*:(215 + k)])
endfor
This is very slow, but relatively easy to understand once it's done. Get each array like atoms.atom1 and get an element like atoms.atom1[0, 0].

VBScript conditional short-circuiting workaround

I have a large classic ASP app that I have to maintain, and I repeatedly find myself thwarted by the lack of short-circuit evaluation capability. E.g., VBScript won't let you get away with:
if not isNull(Rs("myField")) and Rs("myField") <> 0 then
...
...because if Rs("myField") is null, you get an error in the second condition, comparing null to 0. So I'll typically end up doing this instead:
dim myField
if isNull(Rs("myField")) then
myField = 0
else
myField = Rs("myField")
end if
if myField <> 0 then
...
Obviously, the verboseness is pretty appalling. Looking around this large code base, the best workaround I've found is to use a function the original programmer wrote, called TernaryOp, which basically grafts in ternary operator-like functionality, but I'm still stuck using a temporary variable that would not be necessary in a more full-featured language. Is there a better way? Some super-secret way that short-circuiting really does exist in VBScript?
Maybe not the best way, but it certainly works... Also, if you are in vb6 or .net, you can have different methods that cast to proper type too.
if cint( getVal( rs("blah"), "" ) )<> 0 then
'do something
end if
function getVal( v, replacementVal )
if v is nothing then
getVal = replacementVal
else
getVal = v
end if
end function
Nested IFs (only slightly less verbose):
if not isNull(Rs("myField")) Then
if Rs("myField") <> 0 then
I always used Select Case statements to short circuit logic in VB. Something like..
Select Case True
Case isNull(Rs("myField"))
myField = 0
Case (Rs("myField") <> 0)
myField = Rs("myField")
Case Else
myField = -1
End Select
My syntax may be off, been a while. If the first case pops, everything else is ignored.
Or perhaps I got the wrong end of the question. Did you mean something like iIf() in VB? This works for me:
myField = returnIf(isNothing(rs("myField")), 0, rs("myField"))
where returnIf() is a function like so:
function returnIf(uExpression, uTrue, uFalse)
if (uExpression = true) then returnIf = uTrue else returnIf = uFalse : end if
end function
If you write it as two inline IF statements, you can achieve short-circuiting:
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then ...
But your then action must appear on the same line as well. If you need multiple statements after then, you can separate them with : or move your code to a subroutine that you can call. For example:
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then x = 1 : y = 2
Or
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then DoSomething(Rs("myField"))
Would that there were, my friend -- TernaryOp is your only hope.
Yeah it's not the best solution but what we use is something like this
function ReplaceNull(s)
if IsNull(s) or s = "" then
ReplaceNull = " "
else
ReplaceNull = s
end if
end function
Two options come to mind:
1) use len() or lenb() to discover if there is any data in the variable:
if not lenb(rs("myField"))=0 then...
2) use a function that returns a boolean:
if not isNothing(rs("myField")) then...
where isNothing() is a function like so:
function isNothing(vInput)
isNothing = false : vInput = trim(vInput)
if vartype(vInput)=0 or isEmpty(vInput) or isNull(vInput) or lenb(vInput)=0 then isNothing = true : end if
end function
You may be able to just use Else to catch nulls, ""s, etc.
If UCase(Rs("myField")) = "THING" then
'Do Things
elseif UCase(Rs("myField")) = "STUFF" then
'Do Other Stuff
else
'Invalid data, such as a NULL, "", etc.
'Throw an error, do nothing, or default action
End If
I've tested this in my code and it's currently working. Might not be right for everyone's situation though.

Resources