Difference between print, put and say? - raku

In Perl 6, what is the difference between print, put and say?
I can see how print 5 is different, but put 5 and say 5 look the same.

put $a is like print $a.Str ~ “\n”
say $a is like print $a.gist ~ “\n”
put is more computer readable.
say is more human readable.
put 1 .. 8 # 1 2 3 4 5 6 7 8
say 1 .. 8 # 1..8
Learn more about .gist here.
———
More accurately, put and say append the value of the nl-out attribute of the output filehandle, which by default is \n. You can override it, though. Thanks Brad Gilbert for pointing that out.

Handy Perl 6 FAQ: How and why do say, put and print differ?
The most obvious difference is that say and put append a newline at the end of the output, and print does not.
But there's another difference: print and put converts its arguments to a string by calling the Str method on each item passed to, say uses the gist method instead. The gist method, which you can also create for your own classes, is intended to create a Str for human interpretation. So it is free to leave out information about the object deemed unimportant to understand the essence of the object.
...
So, say is optimized for casual human interpretation, dd is optimized for casual debugging output and print and put are more generally suitable for producing output.
...

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.

Perl 6 interpreting the output of the split function as an integer list / array

say "1 10".split(" ")
returns (1,10)
When I use those 1 and 10 as arguments to the sequence operator [...]
say [...] "1 10".split(" ")
returns just (1) while it's supposed to return (1 2 3 4 5 6 7 8 9 10) I guess it's because the output of the split function is interpreted as string.
How to solve that problem? Thank you.
If you want numeric behavior then coerce to numerics:
say [...] +<< "1 10".split(" "); # (1 2 3 4 5 6 7 8 9 10)
This uses a << hyperop to apply a numeric coercion (prefix +) to each element of the sequence generated by the split.
Regarding sequence and range behavior with string endpoints:
SO Why does the Perl 6 sequence 'A' … 'AA' have only one element?. What's described in the linked SO applies to the sequence you've specified, namely "1"..."10".
The open Rakudo issue Sequence operator with string endpoints and no explicit generator produces unintuitive/undocumented results.
SO Why are some of my ranges insane?.
What you've written is equivalent to:
put gist "1"..."10";
(say is equivalent to put gist.)
A gist of "1"..."10" is (1).
That's because the gist of List.new("1") is (1) just like the gist of List.new("a") is (a).
And "1"..."10" evaluates to a List.new("1").
Why? I'm not sure yet but I'm exploring the available info.
Let's start with the doc. The doc for the infix ... op says:
The default generator is *.succ or *.pred, depending on how the end points compare
Well:
say "1" cmp "10"; # Less
which presumably means the sequence starts calling *.succ.
And then:
say "1".succ; # 2
and:
say "2" cmp "10"; # More
It seems this results in the sequence immediately terminating after the "1" rather than including the "2" and continuing.
I'm continuing to search the bug queues and examine the code around the area that #wamba++ linked in their answer to the above linked SO "Why does the Perl 6 sequence 'A' … 'AA' have only one element?".
As usual, raiph is giving the correct answer, but I find something missing about why it really does not work.
Main thing is that [] is a reduce operator, it's not applying whatever is inside it as an infix operator except as a side effect. For instance, this works:
say [+] <4 8>.words; # OUTPUT: «12␤»
But only because there are two components, and the reduce [] is applied to them, having the same effect. Ditto for ...
say [...] <4 8>.words; # OUTPUT: «(4 5 6 7 8)␤»
However that's not what you are looking for. You have two operands, a single operator, you want to call the operator itself. Which you can of course do by using its fully qualified name
say infix:<...>( | <3 5>.words ); # OUTPUT: «(3 4 5)␤»
as long as, of course, you flatten (with | ) its arguments to make it match the signature of an infix operator.
As usual, TIMTOWTDI. So do whatever suits you the best.

Is a /start/,/end/ range expression ever useful in awk?

I've always contended that you should never use a range expression like:
/start/,/end/
in awk because although it makes the trivial case where you only want to print matching text including the start and end lines slightly briefer than the alternative*:
/start/{f=1} f{print; if (/end/) f=0}
when you want to tweak it even slightly to do anything else, it requires a complete re-write or results in duplicated or otherwise undesirable code. e.g. if you want to print the matching text excluding the range delimiters using the second form above you'd just tweak it to move the components around:
f{if (/end/) f=0; else print} /start/{f=1}
but if you started with /start/,/end/ you'd need to abandon that approach in favor of what I just posted or you'd have to write something like:
/start/,/end/{ if (!/start|end/) print }
i.e. duplicate the conditions which is undesirable.
Then I saw a question posted that required identifying the LAST end in a file and where a range expression was used in the solution and I thought it seemed like that might have some value (see https://stackoverflow.com/a/21145009/1745001).
Now, though, I'm back to thinking that it's just not worth bothering with range expressions at all and a solution that doesn't use range expressions would have worked just as well for that case.
So - does anyone have an example where a range expression actually adds noticeable value to a solution?
*I used to use:
/start/{f=1} f; /end/{f=0}
but too many times I found I had to do something additional when f is true and /end/ is found (or to put it another way ONLY do something when /end/ is found IF f were true) so now I just try to stick to the slightly less brief but much more robust and extensible:
/start/{f=1} f{print; if (/end/) f=0}
Interesting. I also often start with a range expression and then later on switch to using a variable..
I think a situation where this could be useful, aside from the pure range-only situations is if you want to print a match, but only if it lies in a certain range. Also because it is immediately obvious what it does. For example:
awk '/start/,/end/{if(/ppp/)print}' file
with this input:
start
dfgd gd
ppp 1
gfdg
fd gfd
end
ppp 2
ppp 3
start
ppp 4
ppp 5
end
ppp 6
ppp 7
gfdgdgd
will produce:
ppp 1
ppp 4
ppp 5
--
One could of course also use:
awk '/start/{f=1} /ppp/ && f; /end/{f=0}' file
But it is longer and somewhat less readable..
While you are right that the /start/,/end/ range expression can easily be reimplemented with a conditional, it has many interesting use-cases where it is used on its own. As you observe it, it might have little value for processing of tabular data, the main but not only use case of awk.
So - does anyone have an example where a range expression actually adds noticeable value to a solution?
In the mentioned use-cases, the range expression improves legibility. Here are a few examples, where the range expression accurately selects the text to be processed. These are only a hand of examples, but there is countlessly similar applications, demonstrating the incredible versatility of awk.
Filter logs within a time range
Assuming each log line starts with an ISO timestamp, the filter below selects all events in a given range of 1 hour:
awk '/^2015-06-30T12:00:00Z/,/^2015-06-30T13:00:00Z/'
Extract a document from a file
awk '/---- begin file.data ----/,/---- end file.data ----/'
This can be used to bundle resources with shell scripts (with cat), to extract parts of GPG-signed messages (prepared with --clearsign) or more generally of MIME-messages.
Process LaTeX files
The range pattern can be used to match LaTeX environments, so for instance we can select the abstracts of all articles in our directory:
awk '/begin{abstract}/,/end{abstract}/' *.tex
or all the theorems, to prepare a theorem database!
awk '/begin{theorem}/,/end{theorem}/' *.tex
or write a linter ensuring that theorems do not contain citations (if we regard this as bad style):
awk '
/begin{theorem}/,/end{theorem}/ { if(/\\cite{/) { c+= 1 } }
END { printf("There were %d bad-style citations.\n", c) }
'
or preprocess tables, etc.

How to print multiple outputs in Smalltalk

|X Y A B C D|
Y:= 7.
X:= 6.
(X = Y)
ifTrue: [X := 0]
ifFalse:[X := 1].
B:=2.
C:=5.
D:=1.
A:= (B squared)*(C-D).
"print both A and X to screen here"
Simple enough little smalltalk example. I'm just curious how I can get this to print X and A as outputs? is there any way to do it w/o having to perform a 'print it' on the top 6 lines and a seperate 'print it' on the bottom 5 lines? if it could print out on just a 'do it' or a single 'print it' please let me know!
You should define what is "printing" and what is X and A.
If "printing" is a result of the "print it" action, then you are talking in general about returning X and A, as "print it" prints the return result of the selected code. This way you have to think about an object which will represent X and A. For this object you can define a printString method or printOn: and get the result printed. Or you can cheat a bit and return a point by doing X#A.
If you are talking about actually printing the thing somewhere then you have to tell more about where do you want to do it. You can print it in Transcript or similar, but there you have to explicitly send a message to the Transcript with what you want to be printed.
Now if you want to use this for "debugging/testing" reasons, it can be easier to go with "inspect it". In your code you can send inspect messages to the objects that you want to look at, and during the execution inspectors will open showing this objects.
Also I encourage you to follow conventions and make your variable names start with lowercase letter.
Smalltalk has no equivalent of print() or println() or the like, since most Smalltalk environments live in a window environment. There are ways to write output to stdout or std error, but this is very dialect specific.
One of the places that somehow replaces stdout in most dialects is a place/stream/window called Transcript, in most dialects this is the window that launches first when your start the IDE.
To write something there you simple do:
Transcript show: 'A=', A asString, ' ; X=', X asString.
(please note that in Smalltalk, Strings and Collections are concatenated with a comma)
You can also write a newLine by sending the message cr to the Transcript like so:
Transcript cr.
Does this answer your question?
A hint for further learning/investigation: Transcript is just a Variable that holds a Stream object. show: is a message that writes some String onto that Stream. asString is a method that returns a String representation of an object.

In awk, is there a functional difference between putting conditions outside the brackets or inside?

As far as I can tell, this:
$2 > 50{print}
functions exactly the same way as this:
{if ($2 > 50) print}
When should I use one vs the other, or is it a matter of style?
2nd one is much more readable in my opinion (especially for non awk gurus who may have to maintain you code).
It's mostly style. The first example is canonical AWK. There are, of course, times when an if statement is required.
Consider that:
$2 > 50 {foo; print}
takes 20 characters versus 27 for the following:
{if ($2 > 50) {foo; print}}
If you remove spaces, it would be 16 versus 22 characters.
So the former is more desirable if you are interested in economy (both in terms of actual characters and visually).
As potong points out, the if form requires curly braces if there are more than one statement (as I have shown in my second example above). If there is only one statement, they are optional. This is the source of subtle bugs if it's written without and additional statements are later added without adding braces. Also, for the reader of a script, the intent is much clearer if the braces are included.
Yes, they are the same. As with most syntax alternatives, there are trade offs.
I mostly agree with John3136 that 2nd form has a higher likelyhood of being understood by someone with background in c, c++, C#, java, and others.
And as Dennis Williamson points out, there are times you have to use the if (...) form.
For one-lines, I think the first form is useful, acceptable, and helps reinforce the primary design of awk, i.e. [pattern]-[action] (being optional or providing a default behaviour).
If you look at comp.lang.awk, you'll see that the long-timers mostly prefer style 1.
Large blocks of code in style 1, to my eye, are difficult to read, but do have the look of lex code.
So.... depends ;-) .... are you writing code that will ultimately be maintained by likely non-awk experts? Then stick w style 2. Writing for yourself? Use both so you don't get rusty!
I hope this helps.
Yes they are the same.
On the first form, you can omit { print } because it's the default action. So, that works too:
$2 > 50