3

I'm practicing Perl with a challenge from codeeval.com, and I'm getting an unexpected error. The goal is to iterate through a file line-by-line, in which each line has a string and a character separated by a comma, and to find the right-most occurrence of that character in the string. I was getting wrong answers back, so I altered the code to print out just variable values, when I got the following error:

Can't use string ("Hello world") as an ARRAY ref while "strict refs" in use at char_pos.pl line 20, <FILE> line 1.

My code is below. You can see a sample from the file in the header. You can also see the original output code, which was incorrectly only displaying the right-most character in each string.

#CodeEval challenge: https://www.codeeval.com/open_challenges/31/
#Call with $> char_pos.pl numbers
##Hello world, d
##Hola mundo, H
##Keyboard, b
##Connecticut, n

#!/usr/bin/perl
use strict;
use warnings;

my $path = $ARGV[0];

open FILE, $path or die $!;

my $len;

while(<FILE>)
{
    my @args = split(/,/,$_);
    $len = length($args[0]) - 1;
    print "$len\n";
    for(;$len >= 0; $len--)
    {
        last if $args[0][$len] == $args[1];
    }


    #if($len > -1)
    #{
    # print $len, "\n";   
    #}else
    #{
    # print "not found\n";   
    #}

}

EDIT: Based on the answers below, here's the code that I got to work:

#!/usr/bin/perl
use strict;
use warnings;
use autodie;


open my $fh,"<",shift;

while(my $line = <$fh>)
{
    chomp $line;
    my @args = split(/,/,$line);
    my $index = rindex($args[0],$args[1]);

    print $index>-1 ? "$index\n" : "Not found\n";
}

close $fh;
5
  • 5
    Check perldoc substr instead of $args[0][$len] and use eq if you're comparing strings. Commented Sep 5, 2014 at 13:54
  • And use while (<>) { … } instead of explicitly opening files, etc; it is much more idiomatic Perl. And while you're checking the docs (noting that it needs to be perldoc -f substr to look up the function substr by name), look up perldoc -f rindex. You might want to split on /, */ to allow for the space before the trailing letter. Commented Sep 5, 2014 at 14:09
  • 1
    Tip: You might prefer my ($word, $letter) = split(/,/,$_);. @args is rather poor name. Commented Sep 5, 2014 at 15:17
  • I'd tend to avoid the $_ in the split as well. split ( /,/ ); will do the same thing. Commented Sep 5, 2014 at 15:25
  • @ikegami - I like that approach to split. I'll keep that in mind in the future. Thanks! Commented Sep 5, 2014 at 16:42

3 Answers 3

3

It looks like you need to know a bit about Perl functions. Perl has many functions for strings and scalars and it's not always possible to know them all right off the top of your head.

However, Perl has a great function called rindex that does exactly what you want. You give it a string, a substring (in this case, a single character), and it looks for the first position of that substring from the right side of the string (the index does the same thing from the left hand side.)

Since you're learning Perl, it may be a good idea to get a few books on Modern Perl and standard coding practices. This way, you know newer coding techniques and the standard coding practices.

Here's a sample program:

#!/usr/bin/perl

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

open my $fh, "<", shift;

while ( my $line = <$fh> ) {
    chomp $line;
    my ($string, $char) = split /,/, $line, 2;
    if ( length $char != 1 or not defined $string ) {
        say qq(Invalid line "$line".);
        next;
    }
    my $location = rindex $string, $char;
    if ( $location != -1 ) {
        say qq(The right most "$char" is at position $location in "$string".);
    }
    else {
        say qq(The character "$char" wasn't found in line "$line".)";
}
close $fh;

A few suggestions:

  • use autodie allows your program to automatically die on bad open. No need to check.
  • Three parameter open statement is now considered de rigueur.
  • Use scalar variables for file handles. They're easier to pass into subroutines.
  • Use lexically scoped variables for loops. Try to avoid using $_.
  • Always do a chomp after a read.

And most importantly, error check! I check the format of the line to make sure that's there is only a single comma, and that the character I'm searching for is a character. I also check the exit value of rindex to make sure it found the character. If rindex doesn't find the character, it returns a -1.

Also know that the first character in a line is 0 and not 1. You may need to adjust for this depending what output you're expecting.

Sign up to request clarification or add additional context in comments.

1 Comment

This is great. Thanks for the thorough answer! I'll make sure to check out those resources. Perl isn't my favorite language, but it helps to have clear, detailed explanations.
3

Strings in perl are a basic type, not subscriptable arrays. You would use the substr function to get individual characters (which are also just strings) or substrings from them.

Also note that string comparison is done with eq; == is numeric comparison.

Comments

0
while($i=<DATA>){
($string,$char)=split(",",$i);
push(@str,$string);}
@join=split("",$_), print "$join[-1]\n",foreach(@str);



__DATA__
Hello world, d
Hola mundo, H
Keyboard, b
Connecticut, n

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.