Jan Bodnar
Jan Bodnar

Reputation: 11657

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;

Upvotes: 7

Views: 193

Answers (2)

codesections
codesections

Reputation: 9600

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.

Upvotes: 5

Elizabeth Mattijsen
Elizabeth Mattijsen

Reputation: 26979

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.

Upvotes: 11

Related Questions