Eardrum has asked for the wisdom of the Perl Monks concerning the following question:

Hey guys! Edited: Write a program that calculates the average number from 1 to the number that the user inputs (in jumps of 2) and prints it to the screen.

example: input: Please enter a number 6 output: (1+3+5)/3 = 3
Here is the fixed code - it works perfectly - but would you guys have done anything differently?
print "Please enter a number: \n"; my $limit = <STDIN>; chomp $limit; my $sum = 0; my @array = (); for (my $i = 1; $i <= $limit; $i+=2) { $sum += $i; push(@array,$i); #Just a note: print "\n i: $i, sum: $sum, array: @array, scalar: ", scalar(@ +array), "\n"; } my $x = scalar(@array); my $average = $sum/$x; my $final = join("+",@array); print "\n($final)/$x=$average\n";
Second question: Write a program that prints (in a downgrading order - 10 each time) the numbers, starting with the number that the user inputs (until it reaches 0).
example: input: Please enter a number 52 output: 52 42 32 22 12 2
Here's my code (would you guys have done anything differently?):
print "Please enter a number: \n"; my $limit = <STDIN>; chomp $limit; my $number = $limit; my @array = (); for (my $i = 0; $i <= $limit; $i+=10) { push(@array,$number); $number -= 10; } print "@array\n";
Third question: Please enter a program that receives 4 number from the user. The program will put in an array only the numbers that are above 50. Print to the screen the array variables, seperated by "-".
example: input: Enter 4 numbers: 20 55 72 100 output: 55-72-100
I wrote the code using "if", and it works as it should - but I'm required to use "for" loop. Can't seem to figure it out.
print "Please enter 4 numbers: \n"; my @numbers = <STDIN>; chomp @numbers; my @array = (); if ($numbers[0] > 50) { push (@array,$numbers[0]) } if ($numbers[1] > 50) { push (@array,$numbers[1]) } if ($numbers[2] > 50) { push (@array,$numbers[2]) } if ($numbers[3] > 50) { push (@array,$numbers[3]) } my $final = join("-", @array); print "$final\n";
Fourth question: Write a program that receives 4 numbers from the user, and print to the screen only the even numbers (use operator % and "for" loop):
example: input: Please enter 4 numbers: 1 2 4 9 output: 2 4
-Same as the previous question. ~~~~~~~~~~~~~~~~~~~~~~~ Fifth question: Write a program that receives from the user a number from the command line (@ARGV). The program will print to the screen a "triangle" with the same size as the number from the input:
example: input: 6 output: * ** *** **** ***** ****** input: 3 output: * ** ***
-With this one I don't even know where to start. Help would be much appreciated. Thank you!

Replies are listed 'Best First'.
Re: New Perl user - help with my homework
by haukex (Archbishop) on Dec 24, 2018 at 12:24 UTC

    Welcome to Perl and the Monastery, Eardrum!

    Thank you for letting us know this is homework. We're happy to help, if you're willing to learn and show your efforts - the latter you've done by posting your code so far, thank you for that. Also take Corion's suggestions into account - for example, the other homework problems don't really have anything to do with this first question, you might consider removing them, or at least placing them inside <readmore>...</readmore> tags. See also How do I post a question effectively? and How do I change/delete my post?.

    First, a couple of comments on your code:

    • You're missing at least one semicolon, another line is missing an optional semicolon, it's a good habit to place them all - I hope you can see which two lines I'm talking about?

    • It's very strongly recommended to Use strict and warnings, as they will help you catch potential problems.

    • Although you've indented your code somewhat consistently, I'd suggest not indenting the for itself. Have a look at indentation styles on Wikipedia - pick one (such as the K&R style, personally I lean towards the "1TBS") and the most important thing is to consistently stick with that style. The tool perltidy can help with that. Normally you should be able to install it with the command cpanm Perl::Tidy (or cpan instead of cpanm, if you don't have App::cpanminus installed yet).

    • In my ($limit)= <STDIN>;, you don't need the parens on the left hand side. They actually do something you might not expect: Instead of just reading one line from STDIN, the statement will read all lines from STDIN, and save only the first - if you enter a number and press return, your program will appear to not be doing anything, but it's really just waiting for more input (on a *NIX system, you can use Ctrl-D to end the input, as haj pointed out). If you use my $limit = <STDIN>;, it will read only one line.

      (What's going on here is that with parentheses on the left of an assignment, the right hand side will be evaluated in list context, which in the case of <filehandle> will try to read all lines, while without the parentheses, the right hand side is in scalar context, which reads one line. You can read more about this for example in the Context tutorial.)

    As for your task, I found the problem statement "the average number from 1 to the number that the user inputs (in jumps of 2)" a little unclear, but the example (1+3+5)/3 = 3 helps. In this case, I think it helps to work out the steps "on paper":

    1. The first step is to get all the numbers from 1 to 6 in steps of two. To see what your loop is doing, try putting a print statement at the top of the loop, such as print "i=$i\n"; - see also the tips on the Basic debugging checklist.

    2. The next thing to do is sum up all of these numbers. You've got a variable $sum, which you're starting out at 1 and incrementing by 2 in the loop. I suggest you think about this as the main point: how could you use this variable to sum up the numbers from the previous step? Try adding another print statement or two to show how $sum changes.

    3. Finally, you'll want to divide by the count of numbers that you summed up. In your code, you're pushing the string '1' into an @array and then using scalar(@array) to get the number of elements in the array after the loop. Although this works, consider what happens when $limit is a large number, such as in the millions or more: @array will contain a lot of elements. There's a more efficient way to count the number of times the loop runs - I'm sure you can come up with an idea :-)

    A side note: you're using what's usually known as the C-style for loop. This is fine for cases like this one, where you need to count up by something other than one. For cases where you need to count up by ones, Perl offers another, nicer-looking mechanism which has the advantage of being less error prone (less chances for typos and off-by-one errors): the foreach loop (which can also be written for), and the Range Operators. The following prints all the numbers from 1 to 10:

    for my $i (1..10) { print "i=$i\n"; }

    (In Perl, There Is More Than One Way To Do It, so there are ways to count up by two's in this example too, but let's leave that for another time :-) )

Re: New Perl user - help with my homework
by haj (Vicar) on Dec 24, 2018 at 11:55 UTC

    I'll not comment on the errors in the algorithm - after all, it's your homework and not mine. So, here are some Perl tips:

    • Read your variable like this:
      print "Please enter a number: \n"; my $limit= <STDIN>; chomp $limit;
      Note there are no parentheses around $limit in the declaration. With the parentheses, you enter "list context" - which means you'll read line after line from STDIN, until you hit Ctrl-D for "end of file".
    • Every statement ends with a semicolon; this is missing in your line $sum += 2. That's why you get a syntax error.
    • If your output should contain the phrase (1+3+5), then take a look at the join function. It should give you a hint what to push into your @array.
Re: New Perl user - help with my homework
by Corion (Patriarch) on Dec 24, 2018 at 11:27 UTC

    Hello and welcome!

    You've shown us your homework, but you haven't told us where you have problems.

    Please show us the exact code you have already written and tell us what it should do. Also tell us what it does and how that differs from what you expect. This helps us give you answers that address your situation.

    You're already showing us the program you have written, but you don't tell us how it fails to do what you want. Please tell us what data you enter, what it does, and what you expect it to do.

    Please also edit your post and separate your code from the rest of the homework into separate sets of <code>...</code> tags.

Re: New Perl user - help with my homework
by soonix (Chancellor) on Dec 24, 2018 at 12:24 UTC
    print "Please enter a number: \n"; my ($limit)= <STDIN>; chomp $limit; my $sum = 1; my @array = (); for (my $i=1; $i<=$limit; $i+=2) { $sum += 2 push(@array,'1') } my $average = $sum/scalar(@array); print "$average\n";
    • Syntax terror: there's a semicolon missing between $sum += 2 and push(@array,'1'). Most of us would end push(@array,'1'); with a semicolon, too.
    • Context: my ($limit) indicates "list context", which forces you to explicitly enter an EOF after your input. A very good explanation to this topic is in Modern Perl by chromatic, here, but I recommend reading the complete book, eventually.
      Here, it should be my $limit = <STDIN>; without the parens.
    • Algorithm: You are computing the average of n times 2, and you calculate it wrong. I recommend that you put
      print "i: $i, sum: $sum, array: @array, scalar: ", scalar(@array), "\n +";
      after the push (which means you need the aforementioned semicolon) to see what is going on:
      Hey, and thank you all for your help. I appreciate it. I have edited my original message, if you could take a look, I'd much appreciate it. Thank you!
Re: New Perl user - help with my homework
by BillKSmith (Monsignor) on Dec 24, 2018 at 19:03 UTC
    It is important (and surprisingly difficult) to specify sufficiently complete requirements. In your first example, I can think of several "corner cases" where the desired output is not specified.
  • Input is not an integer
  • Input is not an odd integer
  • Input is less than '1'
  • Output is to long to print on one line
  • Etc. - I doubt that I thought of all of them
  • I assume that you mean "Compute the sum of the odd integers less than 'n' when 'n' is in the range 1 through 50. Display the result as shown."

    It may be beyond the scope of your current assignment, but it is worth noting that CPAN modules can be helpful even in small programs.

    use strict; use warnings; use integer; use IO::Prompt::Hooked; use List::Util qw(sum0); my $max = prompt("Enter Limiting number (1-50)"); my @terms = grep { $_ % 2 } 1 .. $max; my $sum = sum0 @terms; my $expression = join '+', @terms; print "($expression)=$sum\n";
    Bill
      Thank you very much, I appreciate it!
Re: New Perl user - help with my homework
by choroba (Cardinal) on Dec 25, 2018 at 21:27 UTC
    If you tried to calculate the average from the first task for several input numbers, you might noticed there's no need to sum any numbers. So, I'd implement it as
    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; print 'Please enter a number: '; chomp( my $limit = <> ); say int(($limit + 1) / 2);

    The C-style for loops are randomly used in Perl. Simple while can be used to solve the second task, I tried to show a solution without an array. A little trickery was needed to separate the numbers by spaces.

    #! /usr/bin/perl use warnings; use strict; print 'Please enter a number: '; chomp( my $limit = <> ); my $n = $limit + 10; print ' ' x ($n != $limit), $n while ($n -= 10) >= 0; print "\n";

    To filter an array, we usually use grep in Perl. Also, I tried to really ask for 4 input values, not 3, not 12.

    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; say "Please enter 4 numbers:"; my @numbers; push @numbers, scalar <> for 1 .. 4; chomp(@numbers); say join '-', grep $_ > 50, @numbers;

    For the last task, the repetition operator can hide one level of looping:

    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; my $size = shift; say '*' x $_ for 1 .. $size;

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: New Perl user - help with my homework
by soonix (Chancellor) on Dec 27, 2018 at 15:43 UTC
    Since your teacher wants you to use for, I think the first two tasks are solved OK. But for your own projects, definitely look at the other solutions, too.

    Third and fourth task: you wrote:

    if ($numbers[0] > 50) { push (@array,$numbers[0]) } if ($numbers[1] > 50) { push (@array,$numbers[1]) } if ($numbers[2] > 50) { push (@array,$numbers[2]) } if ($numbers[3] > 50) { push (@array,$numbers[3]) }
    That's 4 times the (nearly) same statement. And if it were not 4 numbers, but 10 - what then? And earlier, you saw that you can't write
    $sum += 2; push(@array,2); $sum += 4; push(@array,4); $sum += 6; push(@array,6);
    This is the stage for the other type of "for": either
    for (@numbers) { push @array, $_ if $_ > 50; }
    or - for learning purposes more verbose:
    for my $current (@numbers) { if ($current > 50) { push @array, $current; } }
    Of course, this is possible with a C-style for, too, but this one is one of Perl's strengths.

    The last task is made for Perl-style for, too.