6

In Python 3, suppose you run a course and decide at the end of the semester that you’re going to drop the first and last homework grades, and only average the rest of them:

def drop_first_last(grades):
    first, *middle, last = grades 
    return avg(middle)

print drop_first_last([100,68,67,66,23]);

In Perl 6:

sub drop_first_last(@grades) {
    my ($first, *@middle, $last) = @grades;
    return avg(@middle);
}
    
say drop_first_last(100,68,67,66,23);

Leads to the error "Cannot put required parameter $last after variadic parameters".

So, what's the equivalent express in Perl 6 as star expressions in Python?

JM0
  • 340
  • 1
  • 13
chenyf
  • 5,048
  • 1
  • 12
  • 35
  • 2
    your python doesn't work either :) a list like [100,68,67,66,23] is required as input. – Vorsprung May 03 '18 at 15:29
  • 1
    You could also use an array slice, for example `@values[1..*-2]` – Håkon Hægland May 03 '18 at 15:34
  • @Vorsprung Sorry about typo, have fixed in my question. Thanks – chenyf May 03 '18 at 15:47
  • @@jjmerelo [Please see this meta comment](https://meta.stackoverflow.com/questions/367286/whats-current-thinking-about-tagging-questions-about-perl-perl-5-perl-6-and?noredirect=1#comment584914_367286) which I've confirmed provides correct guidance to SO consensus. (Search meta for "language X language Y" or something like that -- don't recall exactly.) The `python` tag was correctly removed by @cᴏʟᴅsᴘᴇᴇᴅ and should again be removed. I'm going to remove it for you but wanted to let you and other possible editors know why. – raiph May 04 '18 at 06:30

5 Answers5

6

Perl5:

sub drop_first_last { avg( @_[ 1 .. $#_-1 ] ) }     #this
sub drop_first_last { shift;pop;avg@_ }             #or this

Perl6:

sub drop_first_last { avg( @_[ 1 .. @_.end-1 ] ) }
JM0
  • 340
  • 1
  • 13
Kjetil S.
  • 3,468
  • 20
  • 22
6
sub drop_first_last(Seq() \seq, $n = 1) { seq.skip($n).head(*-$n) };
say drop_first_last( 1..10 );     # (2 3 4 5 6 7 8 9)
say drop_first_last( 1..10, 2 );  # (3 4 5 6 7 8)

The way it works: convert whatever the first argument is to a Seq, then skip $n elements, and then keep all except the last $n elements.

JM0
  • 340
  • 1
  • 13
Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
3

Use a slice.

sub drop_first_last (@grades) {
    return avg(@grades[1..*-2])
}
JM0
  • 340
  • 1
  • 13
Stephen M. Webb
  • 1,705
  • 11
  • 18
1

In case the first and last values are needed for some other reason, unflattened list structure inside slices maps across to the results in most cases, so you can do this (you have to use a $ sigil on $middle to prevent autoflattening):

my @grades = (1,2,3,4,5,6);
my ($first, $middle, $last) = @grades[0,(0^..^*-1),*-1];
$first.say; $middle.say; $last.say;
#1
#(2 3 4 5)
#6
JM0
  • 340
  • 1
  • 13
Brian Julin
  • 71
  • 1
  • 3
0

Workarounds such as have been shown in the rest of the answer are correct, but the short answer to your question is that there is no equivalent expression in Perl 6 to the * in Python.

This kind of arguments are called, in general, variadic, and *slurpy+ in Perl 6, because they slurp the rest of the arguments. And that's the key, the rest. There can be no argument declared after an slurpy argument in a subroutine's signature. This example below also uses a workaround:

sub avg( @grades ) {
    return ([+] @grades) / +@grades;
}

sub drop_first_last($first, *@other-grades) {
    return avg(@other-grades[0..*-1]);
}

my @grades = <10 4 8 9 10 8>;
say drop_first_last( |@grades );

but is first using the slurpy * in the signature to show how it works, and then, by calling it with |@grades, is flattening the array instead of binding it into an array argument. So the long answer is that there is actually an * or variadic symbol in signatures in Perl 6, and it works similarly to how it would do it in Python, but it can only be placed last in those signatures since it captures the rest of the elements of the expression.

JM0
  • 340
  • 1
  • 13
jjmerelo
  • 22,578
  • 8
  • 40
  • 86