1

My @array will not stop taking in STDIN...

my @array = undef;
while (@array = undef){
    @array = <STDIN>;
    for (@array[x]=5){
        @array = defined;
    }
}
Soup
  • 45
  • 5

2 Answers2

3

As clarified, limit the STDIN to five lines

use warnings;
use strict;
use feature 'say';

my @input;

while (<STDIN>) {
    chomp;
    push @input, $_;
    last if @input == 5;
}

say for @input;

There are other things to comment on in the posted code. While a good deal of it is cleared up in detail in Dave Cross answer, I'd like to address the business of context when reading from a filehandle.

The "diamond" operator <> is context-aware. From I/O Operators (perlop)

If a <FILEHANDLE> is used in a context that is looking for a list, a list comprising all input lines is returned, one line per list element. It's easy to grow to a rather large data space this way, so use with care.

In the usual while loop the <> is in the scalar context

while (my $line = <$fh>)

and it is the same with while (<$fh>) since it assigns to $_ variable, a scalar, by default.

But if we assign to an array, say from a filehandle $fh with which a file was opened

my @lines = <$fh>;

then <> operator works in the list context. It reads all lines until it sees EOF (end-of-file), at which point it returns all lines, which are assigned to @lines. Remember that each line has its newline. You can remove them all by

chomp @lines;

since chomp works on a list as well.

With STDIN this raises an issue when input comes from keyboard, as <> waits for more input since EOF isn't coming on its own. It is usually given as Ctrl+D on Unixy systems (Ctrl+Z on Windows).

So you can, in principle, have @array = <STDIN> and quit input with Ctrl+D but this may be a little awkward for input expected from keyboard, as it mostly implies the need for line by line processing. It is less unusual if STDIN comes from a file,

script.pl < input.txt

or a pipe on the command line

some command with output | script.pl

where we do get an EOF (courtesy of EOT).

But I'd still stick to a customary while when reading STDIN, and process it line by line.


  The Ctrl+D is how this is usually referred to but one actually types a low-case d with Ctrl. Note that Ctrl and c (labeled as Ctrl+C) does something entirely different; it sends the SIGINT signal, which terminates the whole program if not caught.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Thanks, this helped a lot, but do you know of any other way of doing this? Is this the only way to do it? Because from articles that I have read, I believe that there is another way to do it? I am not sure – Soup Jul 27 '17 at 22:32
  • @Soup Well, what kind of "_other way_"? There are _many_ ways to do things. But when it comes to `STDIN` this is pretty much how it's done. Take it line by line, remove newline from each (`chomp`). Quit when done – zdim Jul 27 '17 at 22:34
  • I want to ask one more question, if I can? – Soup Jul 27 '17 at 22:34
  • Does this only apply to this Array, or does this apply to every Standard Input in my whole file? – Soup Jul 27 '17 at 22:35
  • 1
    I am not sure I understand, so here's a little bit. The `STDIN` refers to the input to the program from the terminal, and `< >` is an operator that reads from a "filehandle" inside. So whenever your program has `while ()` it stops at that line and waits for a user to type in something. It waits at the standard input (STDIN). Once the user hits return after what they've typed, the line is returned from `<>` operator, the body of `while` runs, and then it's back to waiting for more. This process goes line-by-line, no array involved. – zdim Jul 27 '17 at 22:42
  • Let me know whether this helps? – zdim Jul 27 '17 at 22:43
  • ok ok ok ok I think I am starting to get it now. Much thanks @zbdim!!! – Soup Jul 27 '17 at 22:43
  • @Soup Alright :) Let me know if/as more comes up – zdim Jul 27 '17 at 22:44
  • 1
    Why does everyone want to [read five lines from `STDIN`](https://stackoverflow.com/q/45192445/100754)? – Sinan Ünür Jul 27 '17 at 23:17
  • 1
    @SinanÜnür Hah ... :) At least here it's just a number, 5 (in code that is) – zdim Jul 27 '17 at 23:20
  • @Soup Added a (long) comment on reading from filehandles and _context_. Just letting you know. Hopefully it's useful. – zdim Jul 31 '17 at 22:48
  • @Soup Need to amend my comment about `STDIN`. It focused on terminal based input but that _is not_ the only way for a program to get it. The `STDIN` is a ["standard stream"](https://en.wikipedia.org/wiki/Standard_streams), always set up by the system, from which a program can read, get "input." It can come from a terminal but it can come from other programs as well. Added section in my post mentions this. See more for instance [in scripting manual for Bash](http://www.tldp.org/LDP/abs/html/io-redirection.html) – zdim Aug 01 '17 at 19:01
3
my @array = undef;
while (@array = undef){

These two lines don't do what (I assume) you think they are doing.

my @array = undef;

This defines an array with a single element which is the special value undef. I suspect that what you actually wanted was:

my @array = ();

which creates an empty array. But Perl arrays are always empty when first created, so this can be simplified to:

my @array;

The second line repeats that error and adds a new one.

while (@array = undef) {

I suspect you want to check for an empty array here and you were reaching for something that meant something like "if @array is undefined). But you missed the fact that in Perl, assignment operators (like =) are different to comparison operators (like ==). So this line assigns undef to @array rather than comparing it. You really wanted @array == undef - but that's not right either.

You need to move away from this idea of checking that an array is "defined". What you're actually interested in is whether an array is empty. And Perl has a clever trick that helps you work that out.

If you use a Perl array in a place where Perl expects to see a single (scalar) value, it gives you the number of elements in the array. So you can write code like:

my $number_of_elements = @an_array;

The boolean logic check in an if or while condition is a single scalar value. So if you want to check if an array contains any elements, you can use code like this:

if (@array) {
  # @array contains data
} else {
  # @array is empty
}

And to loop while an array contains elements, you can simply write:

while (@array) {
  # do something
}

But here, you want to do something while your array is empty. To do that, you can either invert the while condition logic (using ! for "not"):

while (!@array) {
  # do something
}

Or you can switch to using an until test (which is the opposite of while):

until (@array) {
  # do something
}

I'm going to have to stop there. I hope this gives you some insight into what is wrong with your code. I'm afraid that this level of wrongness permeates the rest of your code too.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97