100 Doors Problem in perl I'm solving this in perl:

https://github.com/kennyledet/Algorithm-Implementations/tree/master/100_Doors_Problem


  Puzzle:
  There are 100 doors in a long hallway. They are all closed. The first time you walk by each door, you open it. The second time around, you close every second door (since they are all opened). On the third pass you stop at every third door and open it if it’s closed, close it if it’s open. On the fourth pass, you take action on every fourth door. You repeat this pattern for 100 passes.
  At the end of 100 passes, what doors are opened and what doors are closed?


This is my solution:

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use feature qw(say);

my @doors = (0) x 100;

sub check_hallway {
    say "door ". ++$_ , $doors[$_] ? ' is open' : ' is closed' foreach 0 .. $#doors;
}

sub walk_hallway {
    for my $step ( 1 .. $#doors + 1 ) {
        for ( my $i = $step; $i &lt; $#doors + 1; $i += $step ) {
            $doors[ $i ] ^= 1;
        }
    }
}

walk_hallway();
check_hallway();


Is there any better way to write this or any advice to keep in mind?
You have a bug. Lists are 0-indexed in Perl.

Take a closer look at this:


my @doors = (0) x 100;

sub check_hallway {
    say "door ". ++$_ , $doors[$_] ? ' is open' : ' is closed' foreach 0 .. $#doors;
}



@doors has 100 elements, indexed from 0 to 99.
In check_hallway, you have a loop from 0 to 99, but due to the ++$_ earlier on the line,
$doors[$_] will be used with indexes 1..100.
$books[0] is not used, and $books[100] is used, out of bounds.

To make the problem more visible, simply add $books[$_] in the say statement:

say "door ". ++$_, $doors[$_], $doors[$_] ? ' is open' : ' is closed' foreach 0 .. $#doors;


When accessing $doors[0], the program will emit an error:


  Use of uninitialized value in say at prog.pl line 11.


So then why does it appear to be working?
Because in walk_hallway you are not using index 0..99,
but 1..99.

As it is, the last door is missed. Door 100 should be open, but the program shows it closed.

An easy fix is to use 101 doors and ignore index 0. Only the first part of the code needs to change to make that work:

my @doors = (0) x 101;

sub check_hallway {
    say "door ". $_, $doors[$_] ? ' is open' : ' is closed' foreach 1 .. $#doors;
}


But if take a look at the open doors in the output, a pattern becomes obvious:


door 1 is open
door 4 is open
door 9 is open
door 16 is open
door 25 is open
door 36 is open
door 49 is open
door 64 is open
door 81 is open
door 100 is open



The only doors open are the square numbers. This is because all other numbers have an even number of unique divisors. Only square numbers have an odd number of unique divisors.
So, to print the open and closed doors, we can use a much simpler and faster algorithm, for example:

my $doors = 100;

my %squares;
foreach (1..sqrt($doors)) {
    $squares{$_ * $_} = 1;
}

foreach my $door (1..$doors) {
    print 'door ', $door, ' is ', $squares{$door} ? 'open' : 'closed', "\n";
}