Junction ~~ Junction behavior - raku

I want to check if all elements of an Array have given type.
$ raku -e 'my #t = 1,2,3; say all(#t) ~~ Int'
True
$ raku -e 'my #t = 1,2,3,"a"; say all(#t) ~~ Int'
False
Works as expected so far. Now I want to allow two types:
$ raku -e 'my #t = 1,2,3,"a"; say all(#t) ~~ Int|Str'
False
Why is so? If 1 ~~ Int|Str is True for single element why does it fail for all() elements junction?
BTW: This question is about understanding Junction ~~ Junction behavior (which is also a bit undocumented), not about alternative way of performing check from example (which I know is possible).

A few additional lines may help clarify what's going on:
say all(1, 2, 3) ~~ Int|Str; # OUTPUT: «True»
say all('a', 'b', 'c') ~~ Int|Str; # OUTPUT: «True»
say all(1, 2, 'c') ~~ Int|Str; # OUTPUT: «False»
That is, all(1, 2, 'c') ~~ Int|Str is asking "is it the case that all of 1, 2, 'c' are Ints or, alternatively, is it the case that all of 1, 2, 'c' are Strs?" Since neither of those is the case, it returns False.

Related

Raku vs. Perl5, an unexpected result

When I run this script in Raku I get the letter A with several newlines.
Why do I not get the concatenated strings as expected (and as Perl5 does) ?
EDIT BTW can I compile in commaIDE the file with the Perl5 compiler, where can I change this compiler to be that of Perl5 ?
say "A";
my $string1 = "00aabb";
my $string2 = "02babe";
say join ("A", $string1, $string2);
print "\n";
my #strings = ($string1, $string2);
say join ("A", #strings);
print "\n";
The solution I suggest is to drop the parens:
say "A";
my $string1 = "00aabb";
my $string2 = "02babe";
say join "A", $string1, $string2; # Pass THREE arguments to `join`, not ONE
print "\n";
my #strings = $string1, $string2;
say join "A", #strings;
print "\n";
(The elements in #strings in the second call to join are flattened, thus acting the same way as the first call to join.)
The above code displays:
A
00aabbA02babe
00aabbA02babe
When I run this script in Raku I get the letter A with several newlines.
Your code calls join with ONE argument, which is the joiner, and ZERO strings to concatenate. So the join call generates null strings. Hence you get blank lines.
Why do I not get the concatenated strings
The two say join... statements in your code print nothing but a newline because they're like the third and fourth say lines below:
say join( " \o/ ", "one?", "two?" ); # one? \o/ two?␤
say join " \o/ ", "one?", "two?" ; # one? \o/ two?␤
say join ( " \o/ ", "one?", "two?" ); # ␤
say join( " \o/ one? two?" ); # ␤
The first and second lines above pass three strings to join. The third passes a single List which then gets coerced to become a single string (a concatenation of the elements of the List joined using a single space character), i.e. the same result as the fourth line.
The my #strings = ($string1, $string2); incantation happens to work as you intended, because an assignment to a "plural" variable on the left hand side of the = will iterate the value, or list of values, on the right hand side.
But it's a good habit in Raku to err on the side of avoiding redundant code, in this instance only using parens if you really have to use them to express something different from code without them. This is a general principle in Raku that makes code high signal, low noise. For your code, all the parens are redundant.
The problem is the whitespace after the function name. If you leave a space there, raku will treat the expression in paranthesis as a List which is single thing. Here join will think you want to use the list as a joiner.
Observe:
>raku -e "sub foo( $arg ) { say $arg.WHAT }; foo( 'abc', )"
(Str)
>raku -e "sub foo( $arg ) { say $arg.WHAT }; foo ( 'abc', )"
(List)
So in short, if you want to use paranthesis to call a sub, don't put a whitespace between the sub name and the opening paren.
see Traps to avoid,
try:
my #s=("00aabb", "02babe");
say join "A", #s;
say join |("A",#s);
say join("A",#s);
say #s.join: "A";
#or some crazy
say join\ ("A",#s);
say "A".&join: #s;
say join #s: "A";
say "A" [&join] #s;
say #s R[&join] "A";
#…
A: "do not put whitespace between a function name and the opening paren '('" ♎️KISS

Why do + and ~ affect Perl 6 junctions differently?

Add one to a junction of Ints:
put any( 1, 3, 7 ) + 1;
Now you have a junction of those Ints increased by one:
any(2, 4, 8)
So, 2 == any(2, 4, 8) is true.
Make a junction of strings and append to those strings:
put any( <h H> ) ~ 'amadryas';
You get a different result that doesn't equal 'hamadryas' or 'Hamadryas':
any("h", "H")amadryas
I expected something like:
any( 'hamadryas', 'Hamadryas' );
What's the difference in these operations that gives them different behavior even though they should be similar?
on the High Sierra 10.13, put fails with:
put any( 1, 3, 7 ) + 1
This type cannot unbox to a native string: P6opaque, Junction
in block at line 1
perl6 -v
This is Rakudo Star version 2017.10 built on MoarVM version 2017.10
implementing Perl 6.c.
Quoting the filed bug report, as progressed by Zoffix++:
Thank you for the report. lizmat++ fixed this.
The put routine does not explicitly handle Junction arguments. As per design, the end result is therefore a call to put for each of its elements:
put any( 1, 3, 7 ) + 1; # 2␤4␤8
put any( <h H> ) ~ 'amadryas'; # hamadryas␤Hamadryas
Per design, the call order of the puts is indeterminate. So other runs of the same code, perhaps with later compilers, may result in:
put any( 1, 3, 7 ) + 1; # 4␤8␤2
put any( <h H> ) ~ 'amadryas'; # Hamadryas␤hamadryas
In contrast to put, the say routine does special case Junctions. So the end result is just a single call to say:
say any( 1, 3, 7 ) + 1; # any(2, 4, 8)
say any( <h H> ) ~ 'amadryas'; # any(hamadryas, Hamadryas)

Postgresql regexp_replace 'g' flag

i have string
[[good|12345]] [[bad1 [[bad2 [[bad3 [[bad4 [[bad5 [[good|12345]]
i need to kill [[ if word havent | after it.
what i do:
select regexp_replace('[[good|12345]] [[bad1 [[bad2 [[bad3 [[bad4 [[bad5 [[good|12345]]',
'\[\[([^\|]+?(\[\[|\Z))', '\1', 'g')
what i get:
[[good|12345]] bad1 [[bad2 bad3 [[bad4 bad5 [[good|12345]]
what i want to get:
[[good|12345]] bad1 bad2 bad3 bad4 bad5 [[good|12345]]
it looks like the last 2 symbols of my regexp [[ doesn't exists in next iteration of regexp
You should use a look-ahead instead of a group:
select regexp_replace('[[good|12345]] [[bad1 [[bad2 [[bad3 [[bad4 [[bad5 [[good|12345]]', '\[\[([^\|]+?(?=\[\[|\Z))', '\1', 'g')
See demo SQL fiddle
The (?=\[\[|\Z) look-ahead only checks the presence of [[, but does not consume the characters (i.e. matches and moves on through the string). Thus, the following [[ remain available for the next match.

How to use parameters with RPostgreSQL (to insert data)

I'm trying to insert data into a pre-existing PostgreSQL table using RPostgreSQL and I can't figure out the syntax for SQL parameters (prepared statements).
E.g. suppose I want to do the following
insert into mytable (a,b,c) values ($1,$2,$3)
How do I specify the parameters? dbSendQuery doesn't seem to understand if you just put the parameters in the ....
I've found dbWriteTable can be used to dump an entire table, but won't let you specify the columns (so no good for defaults etc.). And anyway, I'll need to know this for other queries once I get the data in there (so I suppose this isn't really insert specific)!
Sure I'm just missing something obvious...
I was looking for the same thing, for the same reasons, which is security.
Apparently dplyr package has the capacity that you are interested in. It's barely documented, but it's there. Scroll down to "Postgresql" in this vignette: http://cran.r-project.org/web/packages/dplyr/vignettes/databases.html
To summarize, dplyr offers functions sql() and escape(), which can be combined to produce a parametrized query. SQL() function from DBI package seems to work in exactly same way.
> sql(paste0('SELECT * FROM blaah WHERE id = ', escape('random "\'stuff')))
<SQL> SELECT * FROM blaah WHERE id = 'random "''stuff'
It returns an object of classes "sql" and "character", so you can either pass it on to tbl() or possibly dbSendQuery() as well.
The escape() function correctly handles vectors as well, which I find most useful:
> sql(paste0('SELECT * FROM blaah WHERE id in ', escape(1:5)))
<SQL> SELECT * FROM blaah WHERE id in (1, 2, 3, 4, 5)
Same naturally works with variables as well:
> tmp <- c("asd", 2, date())
> sql(paste0('SELECT * FROM blaah WHERE id in ', escape(tmp)))
<SQL> SELECT * FROM blaah WHERE id in ('asd', '2', 'Tue Nov 18 15:19:08 2014')
I feel much safer now putting together queries.
As of the latest RPostgreSQL it should work:
db_connection <- dbConnect(dbDriver("PostgreSQL"), dbname = database_name,
host = "localhost", port = database_port, password=database_user_password,
user = database_user)
qry = "insert into mytable (a,b,c) values ($1,$2,$3)"
dbSendQuery(db_connection, qry, c(1, "some string", "some string with | ' "))
Here's a version using the DBI and RPostgres packages, and inserting multiple rows at once, since all these years later it's still very difficult to figure out from the documentation.
x <- data.frame(
a = c(1:10),
b = letters[1:10],
c = letters[11:20]
)
# insert your own connection info
con <- DBI::dbConnect(
RPostgres::Postgres(),
dbname = '',
host = '',
port = 5432,
user = '',
password = ''
)
RPostgres::dbSendQuery(
con,
"INSERT INTO mytable (a,b,c) VALUES ($1,$2,$3);",
list(
x$a,
x$b,
x$c
)
)
The help for dbBind() in the DBI package is the only place that explains how to format parameters:
The placeholder format is currently not specified by DBI; in the
future, a uniform placeholder syntax may be supported. Consult the
backend documentation for the supported formats.... Known examples are:
? (positional matching in order of appearance) in RMySQL and RSQLite
$1 (positional matching by index) in RPostgres and RSQLite
:name and $name (named matching) in RSQLite
? is also the placeholder for R package RJDBC.

checking if a variable value is null on bash script

i have this script that retrieves a parameter (a number) from a SQL query and assigns it to a variable.
there are two options - either the SQL query finds a value and then the script preforms
echo "the billcycle number is $v_bc", or it doesnt find a value and it suppose to
echo "no billcycle parameter found".
im having a problem with the if condition.
this is what i came up with:
#!/bin/bash
v_bc=`sqlplus -s /#bscsprod <<EOF
set pagesize 0
select billcycle from bc_run
where billcycle not in (50,16)
and control_group_ind is null
and billseqno=6043;
EOF`
if [ -z "$v_bc" ]; then echo no billcycle parameter found
else echo "the billcycle parameter is $v_bc"
fi
when billseqno=6043, then it means that v_bc=25, and when i run the script, the result is:
"the billcycle parameter is 25". which is what i ment it to do.
when i set billseqno=6042, according to the above SQL query, v_bc will get no value, therefore what i want it to do is echo "no billcycle parameter found".
instead i get
"the billcycle parameter is
no rows were selected".
any suggestions ?
thanks very much
Assaf.
Your code is correctly checking for an empty value -- v_bc is just not empty, even with -s.
You may either:
parse the output of sqplus when no rows are returned, so add this:
if [[ "$v_bc" == "no rows were selected" ]]; then v_bc=""; fi
This uses the bash [[ ]] command with "==" for pattern matching so we don't need to worry about leading/trailing whitespace. This is not as robust as dogbane's SET FEEDBACK OFF since it's entirely possible for "no rows were selected" to be valid data.
write a better query which always returns data, like this:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:594023455752#followup-221571500346463844
The trick being to reformulate your query and use select/union with a fallback query that conditionally provides output when your query is empty:
with data as
(select billcycle from bc_run where [...])
select * from data
union all
select 'NA', null from dual where not exists (select null from data);
(see also What is the dual table in Oracle? )
Try turning feedback off in sqlplus so that you don't get any output if no rows are selected:
v_bc=`sqlplus -s /#bscsprod <<EOF
SET FEEDBACK OFF
set pagesize 0
select billcycle from bc_run
where billcycle not in (50,16)
and control_group_ind is null
and billseqno=6043;
EOF`
Try something like
if [ "${v_bc:-SHOULDNTHAPPEN}" = "SHOULDNTHAPPEN" ]; then
echo no billcycle parameter found
else......
The idiomatic way to assign a value to a variable if that variable is empty is with an = in a parameter expansion. In other words, this:
: ${v_bc:=no billcycle paramter found}
is equivalent to:
test -z "$v_bc" && v_bc='no billcycle paramter found'
In your case, it would also be easy to do:
echo ${v_bc:-no billcycle parameter found}
but the question asked in the title is not really the problem in your case. (Since the problem is not that v_bc is the empty string, but rather that it is a value you do not expect.)