Eugene Barsky
Eugene Barsky

Reputation: 6002

How to do something functionally with all the elements of a list in Perl 6?

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 Trues), 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

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

Answers (3)

raiph
raiph

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 .puting, 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. Lists 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.

Immutable wannabes

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

Jonathan Worthington
Jonathan Worthington

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

Elizabeth Mattijsen
Elizabeth Mattijsen

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

Related Questions