Reputation: 6002
For example I have an Array
with numbers:
> my @a = ^5
[0 1 2 3 4]
and I want to print their squares. I can use map
, but it will also return a modified List
(these five True
s), which I don't want:
> @a.map({ put $_**2 })
0
1
4
9
16
(True True True True True)
The only way I've found is to use hyper >>
:
> @a>>.&{ put $_**2 }
0
1
4
9
16
but
hyper
?So what is the right way to do it?
P.S. Of course, I can use map
and then put
the result:
.put for @a.map: {$_**2 }
but that's not what I want.
Upvotes: 5
Views: 273
Reputation: 32414
This answer is complementary to the other answers. I'll wander around your question on the assumption that you and other readers are interested in some possibilities of an FPish approach that eschews purism.
my @a = ^5; # @a bound to an Array
@a.map(* ** 2) .put; # 0 1 4 9 16
@a.put; # 0 1 2 3 4 <- Array unchanged
@a.map(* **= 2) .put; # 0 1 4 9 16
@a.put; # 0 1 4 9 16 <- Array changed
@a := @a.List; # rebind @a to a List
@a.map(* ** 2) .put; # 0 1 16 81 256 <- Same as if Array
@a.put; # 0 1 4 9 16 <- List unchanged
@a.map(* **= 2) .put; # Cannot assign to an immutable value
The Array
examples show how you can optionally mutate en passant. (It does so using a binary (infix) op in the form op=
instead of just op
but if that's confusing just note the effect and ignore the syntax.)
En passant mutation is a much more "evil" side effect than mere .put
ing, such that a purist would be kinda horrified if someone called it "functional" style. Not me of course. :)
And the List
example goes in the other direction, moving toward immutability. List
s are immutable wannabes.
Again, those into FP will likely laud their immutability. Purists will denigrate the wannabe aspect. Perl folk will likely see it both ways.
A List
is either entirely or mostly immutable.
You can never push or pop a List
.
It's length is always immutable.
A list of basic values is fully immutable:
my $really-immutable-list = (1,2,'foo', 1.5, (3,4,5));
The exception is that if an element of a list is itself mutable then you can mutate it and thus mutate the List
:
my $only-shallowly-immutable-list = (1,2,'foo', 1.5, [3,4,5]);
$only-shallowly-immutable-list[4][1] = 9;
say $only-shallowly-immutable-list; # (1 2 foo 1.5 [3 9 5])
More subtly:
class c { has $.foo; method waldo { $!foo = 42 } }
my $bar = c.new;
my $list = ($bar, 2);
say $list; # (c.new(foo => Any) 2)
$bar.waldo;
say $list; # (c.new(foo => 42) 2)
Upvotes: 6
Reputation: 29454
Using map
is fine for this purpose, since in sink context it will not produce a result list. In the REPL, the result of the map
is wanted, thus why it is produced. But in a case like:
@a.map({ put $_**2 });
say "That's all, folks";
Then the result of the map
is not wanted, and so no list of results will be assembled. If wishing to really spell this out, it's possible to write:
sink @a.map({ put $_**2 })
Be aware that the last statement in a routine is taken as an implicit return value. Using --> Nil
will suffice to ensure a final map
is in sink context instead.
sub put-squares(@a --> Nil) {
@a.map({ put $_**2 })
}
Upvotes: 13
Reputation: 26924
First of all, using >>.
is not a good idea, as the order in which the operations are performed, is not guaranteed to be in any specific order.
You could use a WhateverCode
:
.say for @a.map: * ** 2
which I personally find most readable. Alternately, you could use a placeholder variable:
.say for @a.map: { $^value ** 2 }
or write it out completely:
.say for @a.map: { $_ ** 2 }
But apparently you don't want that. Why?
Upvotes: 5