Что может быть не так с программой подсчета слов? - PullRequest
1 голос
/ 21 октября 2010

У меня есть вопрос в моем тесте:

Что не так с программой, которая считает количество строк и слов в файле?

open F, $ARGV[0] || die $!;
my @lines = <F>;
my @words = map {split /\s/} @lines;
printf "%8d %8d\n", scalar(@lines), scalar(@words);
close(F); 

Мои предположения:

  1. Если файл не существует, программа не сообщит нам об этом.
  2. Если в файле есть знаки препинания, программа их подсчитает, например, в

    abc cba
    , , ,dce
    

    будет пятью словами, но, с другой стороны, wc выдает тот же результат, поэтому его можно рассматривать как правильное поведение.

  3. Если F - большой файл, может быть лучше перебрать строки и не выгружать его в массив lines.

Есть ли у вас менее тривиальные идеи?

Ответы [ 3 ]

5 голосов
/ 21 октября 2010

В первой строке у вас есть проблема приоритета:

open F, $ARGV[0] || die $!;

совпадает с

open F, ($ARGV[0] || die $!);

, что означает, что die выполняется, если имя файла равно false, а не если open не удается. Вы хотели сказать

open(F, $ARGV[0]) || die $!;

или

open F, $ARGV[0] or die $!;

Кроме того, вы должны использовать форму с тремя аргументами open, если $ARGV[0] содержит символы, которые что-то значат для open.

open F, '<', $ARGV[0] or die $!;

На другой ноте разделение на /\s/ означает, что вы получаете «слово» между последовательными пробельными символами. Вы, вероятно, имели в виду /\s+/, или, как подсказал амфетахин, /\W+/, в зависимости от того, как вы хотите определить «слово».

Это все еще оставляет проблему пустого «слова», которое вы получаете, если строка начинается с пробела. Вы можете разделить на ' ', чтобы подавить это (это особый случай), или вы можете сначала обрезать начальные пробелы, или вставить grep { length $_ }, чтобы отсеять пустые "слова", или отказаться от split и использовать другой метод для считая слова.

Обработка строки за строкой вместо чтения всего файла также была бы хорошим улучшением, но это не так важно, как у первых двух элементов.

3 голосов
/ 21 октября 2010
  • Ваша гипотеза # 1 неверна: ваша программа умрет в случае сбоя open. (см. Порядок действий cjm для ответа.)
  • вы используете глобальный дескриптор файла, а не лексическую переменную.
  • вы не используете форму с тремя аргументами open.
  • вы можете просто читать из stdin, что дает больше гибкости в отношении ввода - пользователь может предоставить файл или направить ввод в stdin.
  • наконец, я бы не стал писать свой собственный код для разбора слов; Я бы потянулся к CPAN, сказал бы что-то вроде Lingua :: EN :: Splitter .
use strict; use warnings;
use Lingua::EN::Splitter qw(words);
my ($wordcount, $lines);
while (<>)
{
    my $line = $_;
    $lines++;
    $wordcount += scalar(words $line);
}

printf "%8d %8d\n", $lines, $wordcount;
1 голос
/ 21 октября 2010

Когда вы open F, $ARGV[0] || die $!, это эффективно завершится, если файл не существует.

Здесь необходимо внести некоторые улучшения:

{local $/; $lines = <F>;} # read all lines at once

my @words = split /\W+/, $lines;
...