Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Challenge - Creative Way To Detect Alpha Characters

by Limbic~Region (Chancellor)
on Sep 13, 2004 at 17:37 UTC ( [id://390612]=perlquestion: print w/replies, xml ) Need Help??

Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:

All,
Today I happened to be in a long boring meeting to discuss the process flow of a project. The presentation was accompanied by some really pitiful code. Pitiful in the sense that the language was not rich, not in the sense that it was bad code. Indeed, there were quite a few creative solutions given the limits of the language (the || operator is used to concatenate strings).

One such problem was to detect if there was any alpha [a-zA-Z] characters in a string where the language didn't support regexes. The equivalent solution using Perl syntax is:

if ( lc $foo eq uc $foo ) { }
I came up with a few alternatives (walking the string) but they all seemed "wrong" given the current solution.

Challenge:
What is the most creative solution for detecting the presence of alpha chars [a-zA-Z] in a string without the use of regexes that you can come up with?

Cheers - L~R

Replies are listed 'Best First'.
Re: Challenge - Creative Way To Detect Alpha Characters
by qumsieh (Scribe) on Sep 13, 2004 at 17:59 UTC
    This is definitely not creative, and I might be cheating:
    if ($foo =~ tr/a-zA-Z//) {
    Strictly speaking, tr/// does not use regexps.

    ;-)

Re: Challenge - Creative Way To Detect Alpha Characters
by CombatSquirrel (Hermit) on Sep 13, 2004 at 18:07 UTC
    The plain old C way:
    #!perl use strict; use warnings; sub contains_alpha($) { my ($ord_a, $ord_z, $ord_A, $ord_Z) = map ord, qw/a z A Z/; for (split //, $_[0]) { $_ = ord $_; return 1 if ($ord_a <= $_ and $_ <= $ord_z) || ($ord_A <= $_ and $_ <= + $ord_Z); } return 0; } for (<DATA>) { chomp; print "'$_' " . (contains_alpha $_ ? 'contains' : 'does not contain +') . " standard alphabet characters\n"; } __DATA__ Hi 123456789o 1234567890 xAFCE3 xAFCEA ... --- ... .s. -o- .s.
    Not really creative, though...
    CombatSquirrel.

    Entropy is the tendency of everything going to hell.
Re: Challenge - Creative Way To Detect Alpha Characters
by tye (Sage) on Sep 13, 2004 at 21:12 UTC
    length($foo)-grep(1+index(((1e9/.7)^7*13*89)."\_",$_),map chr($_),unpa +ck"C*",$foo) == length("\Q$foo")-length($foo)

    (since split and /./gs use regular expressions)

    Replace == with != depending on whether you want what was asked for or something that matches the example code given. (:

    - tye        

Re: Challenge - Creative Way To Detect Alpha Characters
by Pragma (Scribe) on Sep 13, 2004 at 20:10 UTC
    Admittedly not very efficient:

    grep { 0 <= index $str, $_ } a..z, A..Z

    I like your lc/uc solution the most. Rather creative.

Re: Challenge - Creative Way To Detect Alpha Characters
by Pragma (Scribe) on Sep 13, 2004 at 21:34 UTC
    A hash slice variation:

    $H{$_}++ for split '', $str; if (grep $_, @H{a..z,A..Z}) { ... }
    Or an array slice variation:

    $A[ord $_]++ for split '', $str; if (grep $_, @A[map ord, a..z,A..Z]) { ... }
    Update: or forget the slices (duh):

    $H{$_}++ for split '', $str; if (grep $H{$_}, a..z, A..Z) { ... }

    $A[ord $_]++ for split '', $str; if (grep $A[ord $_], a..z, A..Z) { ... }

    Update #2: using POSIX:

    use POSIX; if ( grep { isalpha $_ } split '', $str ) { ... }
    Add salt, quotes, and strict to taste.
Re: Challenge - Creative Way To Detect Alpha Characters
by amt (Monk) on Sep 13, 2004 at 17:46 UTC
    This link will describe to you how to switch between ASCII characters and their corresponding numerical value.

    Perl Cookbook Recipe 1.4. Converting Between ASCII Characters and Values

    You can then step through the unpack'd string with a foreach loop to compare that numerical value with the values for alphabetic characters, A-Z(65-90) and a-z(97-122).
    amt

    Steve_p - Removed link to copyrighted material

      amt,
      You can then step through the...

      That was one of the solutions I came up with (walking the string). I don't see it as being a very creative solution in the sense that uc eq lc is. I was thinking some bitwise operation or something else unique.

      Cheers - L~R

Re: Challenge - Creative Way To Detect Alpha Characters
by Fletch (Bishop) on Sep 13, 2004 at 18:07 UTC
      Great minds think alike :)
Re: Challenge - Creative Way To Detect Alpha Characters
by TheEnigma (Pilgrim) on Sep 13, 2004 at 18:12 UTC
    I'm not sure I understand. Do you want to do something only if there are no alpha characters? Because isn't that what

    if ( lc $foo eq uc $foo ) { }

    would do?

    I just want to make sure I understand the condition of the challenge, not that I'm up to it ;)

    TheEnigma

      TheEnigma,
      Do you want to do something only if there are no alpha characters

      Or the reciprocal - do something only if there are alpha chars. The trick is to figure it out creatively without using regular expressions. As you can see, assuming for a second plain jane ASCII, the only way lc $foo eq uc $foo can be true is if there are no alpha ([a-zA-Z]) characters in $foo.

      Cheers - L~R

Re: Challenge - Creative Way To Detect Alpha Characters
by UnderMine (Friar) on Sep 13, 2004 at 22:22 UTC
    Given that your language is limited and '||' concatanates stings I would guess at SQL.
    CASE WHEN LOWER(foo) = UPPER(foo) THEN 'No Alpha' ELSE 'Alpha' END
    However in some SQL varients you can use translate (has an effect like tr///)
    CASE WHEN INSTR(TRANSLATE(foo, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'), 'X')>0 THEN 'Alpha' ELSE 'No Alpha' END
    Or Even
    CASE WHEN LENGTH(TRANSLATE(foo, '#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', '#'))<>LENGTH(foo) THEN 'Alpha' ELSE 'No Alpha' END
    Hope it Helps UnderMine
      My guess would be REXX.

        I first thought it could be maple. Maple uses || for string catenation (in version 5 and 7, version 3 uses .). It does not have regexps.

        However, maple (version 7 at least) has string functions to do this:

        with(StringTools): str:="2-h/": if ""<>Select(IsAlpha, str) then `has alphas` else `no alphas` fi; str:="2-/": if ""<>Select(IsAlpha, str) then `has alphas` else `no alphas` fi;

        ... in that case ...

        /* rexx */ strings.1 = 85865487878 strings.2 = 'oewiopeoewirpo iep ' strings.3 = '4889jfkjdk' strings.4 = 'hfhjh 767484' strings.5 = '<&jZ>(){}' strings.6 = '<&>(){}' do i = 1 to 6 say has_alpha(strings.i) '-> alpha? ' "'"strings.i"'" end exit has_alpha: procedure parse upper arg string if (length(string) = 0 | datatype(string) = 'NUM') then return 0 start = c2d( 'A' ) stop = c2d( 'Z' ) do while start <= stop if ( pos(d2c(start) , string) \= 0 ) then return 1 start = start +1 end return 0

        ...which gives...

        0 -> alpha?  '85865487878'
        1 -> alpha?  'oewiopeoewirpo iep '
        1 -> alpha?  '4889jfkjdk'
        1 -> alpha?  'hfhjh 767484'
        1 -> alpha?  '<&jZ>(){}'
        0 -> alpha?  '<&>(){}'
        
Re: Challenge - Creative Way To Detect Alpha Characters
by parv (Parson) on Sep 13, 2004 at 19:44 UTC

    It would be hard to suggest a solution w/o knowing the capabilities of that language. Does it support index() like function? Can it convert between hex and decimal values? OTOH, would a solution in perl do?

    UPDATE: I decided to give a perl solution anyway. Use index() in a loop (similar to CombatSquirrel's reply and possibly slower) ...

    sub has_alpha { my $string = shift; return scalar map { index($string , $_) == -1 ? () : 1 } ('a' .. 'z' , 'A' .. 'Z') ; }
      parv,
      Heh - I don't know the language myself except from what code I saw as supplemental information in the very long and boring meeting. I was going for a Perl solution by handi-capping it by not allowing regexes.

      Cheers - L~R

Re: Challenge - Creative Way To Detect Alpha Characters
by Pragma (Scribe) on Sep 13, 2004 at 23:59 UTC
    Iterating is for wussies:
    my @ALPHA; $ALPHA[ord $_]++ for 'a'..'z', 'A'..'Z'; sub hasalpha { my $str = shift; return $ALPHA[ord $str] if length $str < 2; my $pos = int rand length $str; return hasalpha(substr $str, 0, $pos) || hasalpha(substr $str, $pos) +; } print hasalpha('1231a21221');
Re: Challenge - Creative Way To Detect Alpha Characters
by davido (Cardinal) on Sep 14, 2004 at 05:22 UTC

    I couldn't help but turn Quantum::Superpositions loose on this problem. The 'any()' function is the key to a creative solution. The idea is to compare the return value of 'any( $test_string )' with the return value of 'any( 'A'..'Z', 'a'..'z' )'. If the comparison results in equality, you've got alphas in your test string. Here's an example:

    use strict; use warnings; use Quantum::Superpositions; { my $superstring = any( 'A'..'Z', 'a'..'z' ); sub has_alpha { return $superstring eq any( map chr, unpack 'C*', shift ); } } while ( <DATA> ) { chomp; print "$_\t=>\thas alphas.\n" if has_alpha($_); } __DATA__ 12345 abcdef12345 $@!^%^&(*aB&#@ 1A2B3C4D !@#$*&()

    I love that module. theDamian++. Of course if you want to implement this solution in another language, you'll have to port his module too. ;)

    Updated: Streamlined the code a bit, and eliminated need for split.


    Dave

      Of course, before porting Quantum::Superpositions to another language, you might want to consider porting perl's other nondeterministic embedded domain specific language with the terse syntax that's specifically optimized for operating on strings ;-)


      -- All code is 100% tested and functional unless otherwise noted.
Re: Challenge - Creative Way To Detect Alpha Characters
by doowah2004 (Monk) on Sep 13, 2004 at 21:01 UTC
    I don't know if this will work for all cases, but I was trying to be creative.

    foreach (@alpha = split //,$foo) { $_++; if (($_**2 == 0) && ($_ != 1) ){ print "$foo Contains Alpha Characters\n"; last; } }

    Cameron
Re: Challenge - Creative Way To Detect Alpha Characters
by tachyon (Chancellor) on Sep 14, 2004 at 02:11 UTC
    use Inline "C"; print is_alpha("foo"); __END__ __C__ int is_alpha( char * str ) { while ( *str != '\0' ) { if ( ((*str >= 97) && (*str <=122)) || ((*str >= 65) && (*str +<=90)) ) return 1; str++; } return 0; }

    cheers

    tachyon

      Or:
      use Inline "C"; print is_alpha("foo"); __END__ __C__ int is_alpha( char * str ) { while ( *str ) if ( isalpha( *str++ ) ) return 1; return 0; }
Re: Challenge - Creative Way To Detect Alpha Characters
by ambrus (Abbot) on Sep 14, 2004 at 07:30 UTC

    This is both perl-specific and slow, but I'll just show it:

    sub isalpha { no warnings "numeric"; my($p) = @_; 0==++$p; } sub hasalpha { my($s) = @_; isalpha(substr($s, 0, 1, "")) and return 1 while length($s); return; };
      You could hit it from the back {g}:
      sub hasalpha { local($_) = @_; isalpha chop and return 1 while length; return; }
      Shurely shome mishtake. This returns true for anything other than 0-9, doesn't it? Is that what the OP wanted?

      My solution, slightly similar to one of the others:
      $range[ chop ] = 1 while $_; # $_ is copy :) grep $_, @range[65..90,97..122];
      Did someone mention efficiency (I hope not)?

        No. The isalpha function I gave returns true only for [a-zA-Z] characters, false on numbers, punctation, international letters, or any other character.

        It seems to give the same results for me than lc ne uc:

        sub isalpha { no warnings "numeric"; my($p) = @_; 0==++$p; } sub hasalpha { my($s) = @_; isalpha(substr($s, 0, 1, "")) and return 1 while length($s); return; }; @t = ("f/2", "-2/", "*+)", "165", "foop", " A="); $\=$/; $,=" "; print grep hasalpha($_), @t; print grep lc ne uc, @t;

        outputs

        f/2 foop A= f/2 foop A=

        This is because if $p is a non-alnum character in the isalpha function, $p++ converts it to a numeric 0 first, than it increases it to 1, so 0==$p++ is false.

        Your forgot to ord before you chop.
Re: Challenge - Creative Way To Detect Alpha Characters
by Anonymous Monk on Sep 13, 2004 at 22:32 UTC
    Okay, I think this should work: #!/usr/bin/perl $string = $ARGV[0]; for(split(//,$string)) { if(($_ * $_ == 0) && $_ ne '0') { print "alpha: $string\n"; exit(0); } } print "numeric: $string\n"; jkauffman

      Sorry, this is my first time posting here... let me try that again:

      #!/usr/bin/perl $string = $ARGV[0]; for(split(//,$string)) { if(($_ * $_ == 0) && $_ ne '0') { print "alpha: $string\n"; exit(0); } } print "numeric: $string\n";

      jkauffman

        jkauffman,

        Unfortunately this code fails for special chars (@,#,$,%...), if you $_++, the the special chars=>1, but alphas increment. If you look at my code above you will see that our attempts are similar, $_**2 is the same as $_ * $_, and does remove the the numbers (except 0) again this is why you would use $_++, adding that first takes care of the zero. I am just not sure if my code above filters all special chars.

        Good attempt, we were thinking similar ;)

        Cameron
Re: Challenge - Creative Way To Detect Alpha Characters
by Aristotle (Chancellor) on Sep 15, 2004 at 05:28 UTC

    I'm surprised noone posted this yet.

    sub has_alpha { local $_ = shift; my %c; @c{ unpack "(A1)*", $_ } = ( 1 ) x length; $_ && return 1 for @c{ 'A'..'Z', 'a'..'z' }; return; }

    Makeshifts last the longest.

Re: Challenge - Creative Way To Detect Alpha Characters
by doowah2004 (Monk) on Sep 14, 2004 at 14:58 UTC
    After reading posts, I realized my error in using split, so I rewrote and simplified to:

    while ($_ = substr($foo,$i++,1)){ if (++$_**2 == 0) { print "$foo Contains Alpha Characters\n"; last; } }

    This is better I think.

    Cameron

    UPDATE: I have updated the code with the suggestions offered by ambrus. Thanks for pointing that out...ambrus I reverted to the original code, and am posting the changes below.

    Updated with ambrus suggestions:
    my $i = 0; while (local $_ = substr($foo,$i++,1)){ if (++$_ == 0) { print "$foo Contains Alpha Characters\n"; last; } }


    I optimized further:

    while (++substr($foo,$i++,1) != 0){}
    This breaks at an alpha char, and significantly increases the speed, but it is still ~3 times slower that initial solution.

      I think this works without the **2 too.

      Also, you'll want to add my $i = 0; before and change while ($_ = to while (local $_ = (or reorganize it to a for loop that localizes automatically) if you want to use this more than once.

      Update: for the record, the original code had ++$_**2 instead of ++$_.

Re: Challenge - Creative Way To Detect Alpha Characters
by johndageek (Hermit) on Sep 14, 2004 at 14:51 UTC
    Just for fun.
    perl -le '@t=split(/[A-Z]|[a-z]/,"1gGiI234");print "$#t\n";'

    Silly question, doesn't uc/lc "walk the string" in the background?

    Enjoy!
    Dageek
      johndageek,
      Because it has just been for fun, I haven't been too strict on the loose set of rules, but split takes a regular expression as the first argument - which isn't allowed. To answer your question, the string has to be walked in some fashion to change case though special care has to be taken if we aren't talking about plain jane ASCII.

      Cheers - L~R

Re: Challenge - Creative Way To Detect Alpha Characters
by parv (Parson) on Sep 14, 2004 at 21:37 UTC

    To all those, including myself, who are using, or had used, two lists (a..z , A..Z): Why not just convert the string to either upper or lower case before working on it so that only 26-element list, instead of 52, would be needed?

    In my case ... i didn't even think of it until i was working on the Rexx solution. To repent, below is perl translation....

    sub has_alpha { my ($string) = @_; return unless length $string; $string = lc $string; my ($start , $stop) = qw/a z/; while ($start le $stop) { index($string , $start++) != -1 and return 1; } return; }

      You'd then have to scan the list twice. Once to convert, once to compare.

      The original (and best) solution did 3 passes by both upping and lowing the string and comparing, but all three passes where in C rather than perl, so fast.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

        (I removed my comment about the first statement of BrowserUK about which i am still thinking.)

        Yes, you are quite right, BrowserUK, on the point of original solution being the best; it is simple, short, and to the point.

Re: Challenge - Creative Way To Detect Alpha Characters
by premchai21 (Curate) on Sep 15, 2004 at 23:13 UTC

    Highly ASCII-specific, and inefficient, but perhaps mildly interesting. Appears to work, though no absolute guarantees are made of course. Using tr/// is kind of a wart, though, hmm.

    sub contains_alpha { use bytes; my ($string) = @_; my $len = length($string .= ""); my @b = map{$string & (chr(1<<$_) x $len)} (0..7); tr/\x01-\xff/\x01/ for @b; my $result = (~$b[7]) & ($b[6]) & (($b[4] & ~($b[3] & (($b[1] & $b[0]) | $b[2]))) | ((~$b[4]) & ($b[3] | $b[2] | $b[1] | $b[0]))) ; $result =~ tr/\x00//d; length($result); }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://390612]
Approved by kvale
Front-paged by gmax
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (2)
As of 2024-04-19 19:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found