Willman023 has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Monks,

I don't know why I'm still coding on Christmas Eve, but I'm having a problem using a function to reduce pasting code in a hundred places. I tryed calling the function two ways, one by reference one not:

cleaner(\$details); AND $details = cleaner($details); my $dirty; sub cleaner($dirty){ my $clean = ($dirty =~ s/\n/<br>/g;); $clean =~ s/\t/ /g; return $clean; }
If I just do $details =~ s/\n/<br>/g; it works, but I have about 100 other variables to put through this function. I either get no output, or it doesn't complete the regex. Thanks in advance to all that help me out.

bW

Perl - the breakfast of Champions!

Replies are listed 'Best First'.
Re: Problem creating a function
by jdporter (Paladin) on Dec 24, 2002 at 22:10 UTC
    It's the definition of your function that's not quite right -- in particular, how you're defining and accessing the parameters.

    If you don't mind changing the variable's contents (i.e. "destructive cleaning"), then I would write the function like this:
    sub cleaner { $_[0] =~ s/\n/<br>/g;; $_[0] =~ s/\t/ /g; }
    Then you can call it like this:     cleaner( $dirty ); and that will change the value of the variable in the way you want.

    However, I'd point out that that is a pretty uncommon way to use subroutines, althought there's nothing wrong with it. (Do it this way if it's appropriate.)

    If you want to get a cleaned version of a value, without changing the original, I'd write it like this:
    sub cleaner { local $_ = shift; s/\n/<br>/g;; s/\t/ /g; $_ }
    You can call this one like this:     $clean = cleaner( $dirty );

    jdporter
    ...porque es dificil estar guapo y blanco.

      Modifying $_[0] is bad practice. Also what happens if you cleaner("This will crash your code with a can't modify read only value error")

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

        Modifying $_[0] is bad practice.
        That's fine advice for newbies, but it's a white lie. Many languages support OUT parameters. Including Perl. At worst, it merits an encouragement to document the technique if used.
        what happens if you cleaner("This will crash your code with a can't modify read only value error")
        Perl helpfully tells you that you used the function incorrectly. So you got and fix your code. What's the big deal?

        jdporter
        ...porque es dificil estar guapo y blanco.

Re: Problem creating a function
by FamousLongAgo (Friar) on Dec 24, 2002 at 22:07 UTC
    This is the proper syntax:
    sub cleaner { my ( $text ) = @_; $text =~ s/\n/<br>/gs; $text =~ s/\t/ /g; return $text; } my $details = cleaner( $clean_me );
    Note that function prototypes don't work in perl like in C or PHP code. You can't say sub cleaner( $dirty ) and expect the function to contain a variable called $dirty with your input parameter - you have to pull it off the magic @_ parameter array using shift or the syntax I use here.

    Perl does have function prototypes of a sort, but they serve a different purpose than in other languages

    Also, if you are matching newlines in your regular expression, you will need the s flag - otherwise the match will stop at the first newline. The s flag tells Perl to treat newlines like any other character, and match to the end of the string.

    Update: See below for why that last piece of advice is irrelevant - thanks, jdporter!.

      if you are matching newlines in your regular expression, you will need the s flag - otherwise the match will stop at the first newline.
      No, that's not quite right. /s is only necessary if you want /./ (dot) to match newlines, which it does not normally do. Since the given regex contains no dots, /s has no effect.

      jdporter
      ...porque es dificil estar guapo y blanco.

Re: Problem creating a function
by pg (Canon) on Dec 25, 2002 at 00:28 UTC
    1. I doute that your program can even 100% pass compiler. An old advice many good people have been giving from time to time: PLEASE USE perl -w.

      In your case, if you use perl -w, you will see that, Perl actually takes your
      sub cleaner($dirty)
      as sub prototype, and warns you that there is illegal char in the prototype.

      As I mentioned prototype, let's just give a simple example of how to use prototype. In the following example, if you uncomment the third call to greeting, you will get an error. Yes, the Perl prototype check is working, and it gives you an error "too many arguments".
      use strict; sub greeting($); greeting("Merry Christmas"); greeting("Happy New Year"); #greeting("Merry Christtmas", "and Happy new Year"); sub greeting($) { print "I say, \"", shift, "!\"\n"; }
    2. Also there is really no point, for you to make it looks like that you are passing a parameter to that function, when you are actually using it as a global variable. To learn more about Perl scope, could help you make the concept straight.
Re: Problem creating a function
by bart (Canon) on Dec 25, 2002 at 10:56 UTC
    cleaner(\$details); AND $details = cleaner($details); my $dirty; sub cleaner($dirty){ my $clean = ($dirty =~ s/\n/<br>/g;); $clean =~ s/\t/ /g; return $clean; }
    Perl doesn't work that way. Passing data through to a sub goes via @_, not via named arguments.
    sub cleaner { my($dirty) = @_; ... }
    And your other huge mistake is in this line:
    my $clean = ($dirty =~ s/\n/<br>/g;);
    Let's start by dropping the inner semicolon...

    You assign the result of the s/// to $clean, which is the number of substitutions. Remember: s/// does an in-place substitution. The correct syntax for what you want is:

    (my $clean = $dirty) =~ s/\n/<br>/g;
    which first assigns the value of $dirty to $clean, and then the substitution takes place on the Lvalue, $clean. Cut into smaller peices, this does the same thing:
    my $clean = $dirty; $clean =~ s/\n/<br>/g;

    All put together, you no longer need $dirty:

    sub cleaner { (my $clean = $_[0]) =~ s/\n/<br>/g; $clean =~ s/\t/ /g; return $clean; } $details = cleaner($details);
    The code can still be further reduced to:
    sub cleaner { local $_ = shift; s/\n/<br>/g; s/\t/ /g; return $_; } $details = cleaner($details);

      I realise that your just correcting the OP's code to make it work, but doesn't converting \n's to <BR>'s and then tabs to spaces strike you as a strange thing to do?

      In as much as, one assumes that the results are destined for display in a browser, but the browser will discard both spaces and tabs, so the second step seems pointess.


      Examine what is said, not who speaks.

        Well BrowserUk, you would be correct if I was not working with a program that reads from a text file that's formated such that the paramaters are seperated by tabs, and the records by endlines. This program then takes the input from text file and displays to a browser, but I don't want to give you the impression that I'm heading down the wrong path to begin with.

        bW

      Thanks alot to jdporter,pg,bart and everyone else that helped with this node. I did put an extra semicolon in there, as did jdporter in his solution, sorry about that. I haven't started using -w yet, as I'm just starting to use strict and realize why I should have used it in the past...gimme a little time to wean myself onto that one.

      I think the reason my prototype and parameter passing was so off was my brain thinking in the wrong language(How dare I). But using $_ as many of you pointed out did the trick. I like to use functions in this manner because it is flexible, but not by way of "destructive cleaning".

      bW