2010-10-21 19 views
1

J'ai une question dans mon test:Qu'est-ce qui ne va pas avec le programme de compte de mots?

Quel est le problème avec le programme qui compte le nombre de lignes et de mots dans le fichier?

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

Mes sont conjectures:

  1. Si le fichier n'existe pas, le programme ne nous dira pas à ce sujet.
  2. S'il y a des signes de ponctuation dans le fichier, le programme va les compter, par exemple, dans

    abc cba 
    , , ,dce 
    

    sera de cinq mots, mais d'autre part wc émet le même résultat, il pourrait être considéré comme un comportement correct.

  3. Si F est un fichier volumineux, il peut être préférable d'itérer sur les lignes et de ne pas le vider dans le tableau lines.

Avez-vous des idées moins triviales?

Répondre

5

Sur la première ligne, vous avez un problème de priorité:

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

est le même que

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

qui signifie que le die est exécuté si le nom de fichier est false, pas si le open échoue.Vous vouliez dire

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

ou

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

En outre, vous devez utiliser le formulaire 3 argument ouvert, en cas $ARGV[0] contient des caractères qui signifient quelque chose à open.

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

Sur une note différente, le fractionnement sur /\s/ signifie que vous obtenez un « mot » entre un espace consécutifs. Vous avez probablement voulu dire /\s+/, ou comme suggéré amphétamachine, /\W+/, selon la façon dont vous voulez définir un "mot". Cela laisse toujours le problème du "mot" vide que vous obtenez si la ligne commence par un espace. Vous pouvez diviser ' ' pour supprimer cela (c'est un cas particulier), ou vous pouvez couper les espaces blancs en premier, ou insérer un grep { length $_ } pour éliminer les mots vides, ou abandonner split et utiliser une méthode différente pour compter les mots.

Le traitement ligne par ligne au lieu de lire tout le fichier à la fois serait également une bonne amélioration, mais ce n'est pas aussi important que ces deux premiers éléments.

+0

Merci, je pense, problème de priorité est la réponse à cette question. – alexanderkuk

+0

\ s est aussi une erreur en fait. – alexanderkuk

1

Lorsque vous open F, $ARGV[0] || die $! quittera effectivement si le fichier n'existe pas.

Il y a quelques améliorations à apporter ici:

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

my @words = split /\W+/, $lines; 
3
  • Votre conjecture # 1 est incorrect: votre programme va mourir si le open échoue. (voir la réponse de cjm concernant l'ordre des opérations.)
  • Vous utilisez un handle de fichier global plutôt qu'une variable lexicale.
  • vous n'utilisez pas la forme à trois arguments de open.
  • Vous pouvez simplement lire depuis stdin, ce qui donne plus de flexibilité quant à la saisie - l'utilisateur peut fournir un fichier, ou diriger l'entrée vers stdin. Enfin, je n'écrirais pas mon propre code pour analyser les mots; Je voudrais atteindre pour CPAN, dire quelque chose comme 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; 
+0

Non, il mourra si le nom de fichier est faux. Vois ma réponse. – cjm