How to show an unknown list of variables and their values, if possible - variables

As mentioned before in some questions with "Progress-4GL" and "OpenEdge" tags, I'm working with AppBuilder and Procedure editor. As a result, the debugging possibilities are extremely limited: for knowing the value of a variable, I need to do show them on screen, something like this:
MESSAGE "temp1=[" temp1 "], temp2=[" temp2 "]" VIEW-AS ALERT-BOX.
I can also put that information in a logfile, but that's not the main point here.
I would like to write a procedure, which can handle this, something like:
PROCEDURE SHOW_VARIABLES_AND_VALUES (INPUT I1, INPUT I2, ...):
1. <put parameter names and values together inside one string> => """I1="" I1"
2. <do this for all input parameters (the number is unknown)> => """I1="" I1, ""I2="" I2, ..."
3. <how to use this (MESSAGE VIEW-AS ALERT-BOX, LOG, ...) there I'll know what to do>
Does anybody know how to handle the fist two points (put variable name and value together and handle an unknown number of input parameters)?
Thanks in advance

You can use SUBSTITUTE function.
MESSAGE SUBSTITUTE ("temp1=&1 ~ntemp2=&2 ~n temp3=&3",
temp1,
temp2,
temp3) VIEW-AS ALERT-BOX.
Unfortunately there is no dynamic access to variables or parameters. So there's no way to automatically add all input parameters to a message string. Also there is no anytype parameter type in the ABL - for user defined functions or procedures. So you'd have to use the STRING() function a lot to convert your input parameters to string as the best fit parameter for everything.
The built in SUBSTITUTE function on the other hand can handle anytype of arguments. So temp1, temp2 and temp3 can actually be variables or parameters of any datatype.
As mentioned in one of my comments on one of your earlier questions: Give the OpenEdge debugger a chance. The debugger outside of Progress Developer studio looks historic. But it does it's job.

Meanwhile I've decided to use following system (as my request seems to be impossible):
MESSAGE "temp1=[" temp1 "]~n" ~
"temp2=[" temp2 "]~n" ~
"temp3=[" temp3 "]~n" ~
"temp4=[" temp4 "]" ~
VIEW-AS ALERT-BOX.
In order to make it easy to work with, I've found out the following keyboard "shortcut" for the tilde character: ALT+0126.
As indicated by Stefan, this is far better (no tilde and no shortcut needed):
MESSAGE "temp1=[" temp1 "]" SKIP
"temp2=[" temp2 "]" SKIP
"temp3=[" temp3 "]" SKIP
"temp4=[" temp4 "]" SKIP
VIEW-AS ALERT-BOX.

Related

Raku zip operator & space

I found this one liner which joins same lines from multiple files.
How to add a space between two lines?
If line 1 from file A is blue and line 1 from file B is sky, a get bluesky,
but need blue sky.
say $_ for [Z~] #*ARGS.map: *.IO.lines;
This is using the side-effect of .Str on a List to add spaces between the elements:
say .Str for [Z] #*ARGS.map: *.IO.lines
The Z will create 2 element List objects, which the .Str will then stringify.
Or even shorter:
.put for [Z] #*ARGS.map: *.IO.lines
where the .put will call the .Str for you and output that.
If you want anything else inbetween, then you could probably use .join:
say .join(",") for [Z] #*ARGS.map: *.IO.lines
would put comma's between the words.
Note: definitely don't do this in anything approaching real code. Use (one of) the readable ways in Liz's answer.
If you really want to use the same structure as [Z~] – that is, an operator modified by the Zip meta-operator, all inside the Reduce meta-operator – you can. But it's not pretty:
say $_ for [Z[&(*~"\x20"~*)]] #*ARGS.map: *.IO.lines
Here's how that works: Z can take an operator, so we need to give it an operator that concatenates two strings with a space in between. But there's no operator like that built in. No problem – we can turn any function into an infix operator by surrounding it with [ ] (the infix form).
So all we need is a function that joins two strings with a space between them. That also doesn't exist, but we can create one: * ~ ' ' ~ *. So, we should be able to shove that into our infix form and pass the whole thing to the Zip operator Z[* ~ ' ' ~ *].
Except that doesn't work. Because Zip isn't really expecting an infix form, we need to give it a hint that we're passing in a function … that is, we need to put our function into a callable context with &( ), which gets us to Z[&(* ~ ' ' ~ *)].
That Zip expression does what we want when used in infix position – but it still doesn't work once we put it back into the Reduce/[ ] operator that we want to use. This time, the problem is due to something that may or may not be a bug – even after discussing it with jnthn on github, I'm still not sure whether this behavior is intended/correct.
Specifically, the issue is that the Reduction meta-operator doesn't allow whitespace – even in strings. Thus, we need to replace * ~ ' ' ~ * with *~"\c[space]"~* or *~"\x20"~* (where \x20 is the hex value of in Unicode/ASCII). Since we've come this far into obfuscated code, I figure we might as well go all the way. And that gets us back to
say $_ for [Z[&(*~"\x20"~*)]] #*ARGS.map: *.IO.lines
Again, I'm not recommending that you do this. (And, if you do, you could at least make it slightly more readable by saving the * ~ ' ' ~ * function as a named variable in the previous line, which at least gets you whitespace. But, really, just use one of Liz's suggestions).
I just thought this gives a useful window into some of the darker and more interesting corners of Raku's strangely consistent behavior.

Raku control statement to make numeric strings interpreted as numeric

I have a large hash of arrays,
%qual<discordant> (~approx. 13199 values like '88.23', '99.23', etc.
which ranges from 88-100, and are read in from text files,
and when I print %qual<discordant>.min and %qual<discordant>.max I can see the values are clearly wrong.
I can fix this by changing how the data is read in from the text files:
%qual{$type}.push: #line[5]
to
%qual{$type}.push: #line[5].Num
but this wasn't intuitive, this took me a few minutes to figure out why Raku/Perl6 was giving clearly incorrect answers at first. It would have been very easy to miss this error. In perl5, the default behavior would be to treat these strings like numbers anyway.
There should be some control statement to make this the default behavior, how can I do this?
The problem / feature is really that in Raku when you read lines from a file, they become strings (aka objects of type Str). If you call .min and .max on an array of Str objects, then string semantics will be used to determine whether something is bigger or smaller.
There are special values in Raku that act like values in Perl. In Raku these are called "allomorphs". They are Str, but also Num, or Rat, or Int, or Complex.
The syntax for creating an appropriate allomorph for a string in $_ is << $_ >>. So if you change the line that reads the words to:
my #line = $line.words.map: { << $_ >> }
then the values in #line will either be Str, or IntStr or RatStr. Which should make .min and .max work like you expect.
However, if you are sure that only the 5th element of #line is going to be numeric, then it is probably more efficient to convert the Str to a number before pushing to the array. A shorter syntax for that would be to prefix a +:
%qual{$type}.push: +#line[5]
Although you might find that too line-noisy.
UPDATE: I had forgotten that there's actually a sub called val that takes an Str and creates an appropriate allomorph of it (or returns the original Str). So the code for creating #line could be written as:
my #line = $line.words>>.&val

Limitting character input to specific characters

I'm making a fully working add and subtract program as a nice little easy project. One thing I would love to know is if there is a way to restrict input to certain characters (such as 1 and 0 for the binary inputs and A and B for the add or subtract inputs). I could always replace all characters that aren't these with empty strings to get rid of them, but doing something like this is quite tedious.
Here is some simple code to filter out the specified characters from a user's input:
local filter = "10abAB"
local input = io.read()
input = input:gsub("[^" .. filter .. "]", "")
The filter variable is just set to whatever characters you want to be allowed in the user's input. As an example, if you want to allow c, add c: local filter = "10abcABC".
Although I assume that you get input from io.read(), it is possible that you get it from somewhere else, so you can just replace io.read() with whatever you need there.
The third line of code in my example is what actually filters out the text. It uses string:gsub to do this, meaning that it could also be written like this:
input = string.gsub(input, "[^" .. filter .. "]", "").
The benefit of writing it like this is that it's clear that input is meant to be a string.
The gsub pattern is [^10abAB], which means that any characters that aren't part of that pattern will be filtered out, due to the ^ before them and the replacement pattern, which is the empty string that is the last argument in the method call.
Bonus super-short one-liner that you probably shouldn't use:
local input = io.read():gsub("[^10abAB]", "")

How to split lines in Haskell?

I have made a program which takes a 1000 digit number as input.
It is fixed, so I put this input into the code file itself.
I would obviously be storing it as Integer type, but how do I do it?
I have tried the program by having 1000 digits in the same line. I know this is the worst possible code format! But it works.
How can assign the variable this number, and split its lines. I read somewhere something about eos? Ruby, end of what?
I was thinking that something similar to comments could be used here.
Help will be appreciated.
the basic idea is to make this work:
a=3847981438917489137897491412341234
983745893289572395725258923745897232
instead of something like this:
a=3847981438917489137897491412341234983745893289572395725258923745897232
Haskell doesn't have a way to split (non-String) literals across multiple lines. Since Strings are an exception, we can shoehorn in other literals by parsing a multiline String:
v = read
"32456\
\23857\
\23545" :: Integer
Alternately, you can use list syntax if you think it's prettier:
v = read . concat $
["32456"
,"24357"
,"23476"
] :: Integer
The price you pay for this is that some work will be done (once) at runtime, namely, the parsing (e.g. read).

Comma, ')',or valid expression continuation expected

I need my VB.net to write a file containing the following line
objWriter.WriteLine ("TEXTA " (FILEA) " TEXTB")
Unfortunatly the variable (FILEA) is causing problems i now get the error
Comma, ')', or valid expression continuation expected.
Could someone explain this please?
You're not concatenating (joining) the strings proerly...
objWriter.WriteLine ("TEXTA " & FILEA & " TEXTB")
A better style to get into the habit of using is:
objWriter.WriteLine (string.format("TEXTA {0} TEXTB", FILEA))
The FILEA variable replaces the {0} placeholder in the format string. Depending on what the writer you're using is, you may have a formatted overload so you could just do:
objWriter.WriteLine ("TEXTA {0} TEXTB", FILEA)
And since you asked for an explanation;
The compiler is asking you what exactly you want it to do - you've given it 3 variables (String, variable, String) and haven't told it that you want to join them together - It's saying that after the first string "TEXTA", there should either be the closing bracket (to end the method call), a comma (to pass another parameter to the method) OR a "valid continuation expression" - ie something that tells it what to do with the next bit. in this case, you want a continuation expression, specifically an ampersand to signify "concatenate with the next 'thing'".
Presumably you're looking for string concatenation? Try this:
objWriter.WriteLine("TEXTA" & FILEA & "TEXTB");
Note that FILEA isn't exactly a conventional variable name... which leads me to suspect there may be something else you're trying to achieve. Could you give more details?