Home > Software engineering >  How to lcfirst each group of words?
How to lcfirst each group of words?

Time:01-24

My mind is blocked now, why this is not working as I expected:

$ echo 'JOHN, BO PEEP, BoPeep' | perl -0777ne 'map { print ($_) if $_ ne uc($_) } split /,/'
 BoPeep

$ echo 'JOHN, BO PEEP, BoPeep' | perl -0777ne 'map { print lcfirst($_) if $_ ne uc($_) } split /,/'
 BoPeep

The only difference between the two commands is print ($_) vs print lcfirst($_), while I was expecting that print lcfirst($_) would output boPeep instead.

What I'm missing?

UPDATE:

Sample input:

JOHN, BO PEEP, BoPeep, AVG, Hex_String_Literal, Time_String, MODULE

CodePudding user response:

The problem is that splitting on a comma alone leaves the leading spaces in each term.

One way to take care of that is to split on all that can come between tokens of interest, here /\s*,\s*/ (recall that split takes a full legit regex there). With other simplifications

echo "JOHN, BO PEEP, BoPeep, AVG, Hex_String_Literal, Time_String, MODULE"
    | perl -nlE'$_ ne uc($_) and say lcfirst($_) for split /\s*,\s*/'

This prints words boPeep, hex_String_Literal, time_String each on a separate line. To print them as shown, on one line separated by spaces, we need a little more. For example

perl -nE'say join " ", map { $_ ne uc($_) ? lcfirst($_) : () } split /\s*,\s*/'

This uses a trick to filter by map, by passing an empty list () for all-caps words, which gets flattened into nothing in the overall output list. Or, filter then process further

perl -nE'say join " ", map { lcfirst($_) } grep { $_ ne uc($_) } split /\s*,\s*/'

This processes the list from split first to grep through it and then goes over its output list of remaining items with map, what is likely a bit less efficient than having a ternary in one pass over the whole list, but that's surely not going to show in any reasonable work loads.

Some comments

  • I've dropped -0777 as I am not sure of its utility -- if this runs on a file and a multiline chunks in text need be processed then we need some other provisions for that anyway. If it goes line-by-line then there's no need for it. Put it back if needed :)

  • That convenient -E switch, which provides say (and all other features!) isn't good in the eyes of future compatibility. If that is or can become an issue then use CORE::say, or of course print "...", "\n", and keep -e instead of -E

  •  Tags:  
  • Related