Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Write a program which gets a string as its input, and counts the number of alphanumeric and non-alphanumeric characters in it. It has to display its result like this:

input: http://stackexchange.com
output: 20 + 4 = 24

The catch is, your source code has to have the same number of alphanumeric characters as non-alphanumeric ones. Comments are not allowed, whitespace is ignored. (The language called Whitespace might compete for votes, but will not be selected as winner, obviously)

Characters in the code must have at least some minor justification, they should not be completely superfluous. For example, longer variable names are allowed, i = (j*3)+4; instead of i = j*3+4; is also allowed. However, i = i + 1;;;;;; is not.

Besides this, standard code-golf rules apply.

share|improve this question
    
If I define a new, preprocessed variant of Ook where the keywords are O., O?, and O! and then any program I write meets the character class restriction... Of course it is likely to lose on the length business. –  dmckee Mar 14 at 17:58
1  
will it all be ascii? –  Jordon Biondo Mar 14 at 17:58
    
@JordonBiondo : I was thinking of anything you want from the full 8 bit ANSI to unicode, but if your code only supports 7 bit ASCII, I will accept it as well. –  vsz Mar 14 at 18:19
3  
Is the whitespace inside the output-string counted into the non-alphanumerics? Or ignored with all the other (non-string-literal) whitespace? –  Kninnug Mar 14 at 22:25
1  
@dmckee: If you're going to define your own language, just define a variant of the language of your choice where non-empty programs work just like in the base language, but the empty program is preprocessed into code that does exactly what the question asks for. –  user2357112 Mar 15 at 0:56
show 2 more comments

16 Answers 16

up vote 7 down vote accepted

Perl, 32 + 32 = 64

The string is expected in STDIN. The output is written to STDOUT. White space is ignored. My interpretation of the task is that the program should be able to run on itself to get the score.

$/ = $,;
$_ = <>;
s x\sxxg;
$\ = length;
print s x[0-9a-z]xxgi,
      ' + ',
      s x.xxg,
      ' = '

Ungolfed with comments

$/ = $,; # The input separator becomes undefined, because the default for $, is "undef"
$_ = <>; # now $_ takes the whole file (STDIN) instead of the first line
s x\sxxg; # $_ =~ s/\s//g;
          # white space is removed from $_
$\ = length; # The number of the other characters are put into $\,
             # which is automatically printed the end of "print".
print s x[0-9a-z]xxgi, # s/[0-9a-z]//gi
                       # Remove alphanumeric characters and return their count
      ' + ',
      s x.xxg, # s/.//g
               # Remove the remaining special characters and return their count.
               # "." does not catch new lines, but we have already
               # removed white spaces including new lines.
      ' = '

I found several variations with the same byte counts, e.g.:

$/ = $x;
$_ = <>, s x\sxxg;
$\ = split $x;
print s x[\da-z]xxgi,
      " + ",
      s x.xxg,
      ' = '

Examples

  • Example from the question:

    echo 'http://stackexchange.com' | perl a.pl
    20 + 4 = 24
    
  • Running on itself (a.pl):

    cat a.pl | perl a.pl
    32 + 32 = 64
    

    The file size is 104 bytes, thus 40 bytes are ignored as white space.

Perl, 29 + 29 = 58

$_=<>;s x\sxxg;$\=length;print s x[0-9a-z]xxgi,' + ',s/.//g,' = '

The string is expected at STDIN and it is limited to the first line. The result is printed to STDOUT. White space is ignored.

Ungolfed

$_ = <>;
s x\sxxg; # same as s/\s//gx; removes white space;
$\ = length($_); # sum is automatically appended at the end of print
print sx[0-9a-z]xxgi, # same as s/[0-9a-z]//gi;
                      # the number of alphanumeric characters
      ' + ',
      s/.//g, # the number of the remaining special characters
      ' = '

Examples

File a.pl contains the Perl script.

  • Example from the question:

    echo 'http://stackexchange.com' | perl a.pl
    20 + 4 = 24
    
  • Running on itself:

    cat a.pl | perl a.pl
    29 + 29 = 58
    

    The file size of a.pl is 65 bytes, thus 7 bytes are ignored as white space.

share|improve this answer
    
It seems you are assuming the input is on only one line ... I didn't see anything about that in the spec? Also, what is the justification for the /x flag in the first replace? –  skibrianski Mar 15 at 0:51
    
@skibrianski: (a) The question is not too clear about the specification of "string". Now I have added a variant that can read whole files. (b) Also it is not clear to me, how white space should be treated by the script. My interpretation is that white space is ignored in both the task and in the score. (c) The /x flag allows white space in the pattern to increase the readability. The updated answer makes use of it. –  Heiko Oberdiek Mar 15 at 1:35
    
Re a), the author doesn't say anything about what will be in the string, so I'd guess it's unwise to make assumptions, which to me means newlines must be allowed. Re b) agreed, it's not clear. Re c) Right, but in your answer whitespace doesn't add readability to my eye, it just adds a alphanumeric character... Maybe I'm being too hard on this point, but it's revealing to me that you only use /x in one of your regexes, presumably to add that one last extra alphanumeric to get the counts to align =) Still I like your answer. I cooked up something pretty similar. –  skibrianski Mar 15 at 2:07
    
haha now we have essentially identical code =) good show =) –  skibrianski Mar 15 at 2:49
    
@skibrianski: :-) Thanks, you give me a reason to post one of the other variants with a little more differences. However the byte count remains. –  Heiko Oberdiek Mar 15 at 3:09
add comment

C - 96 (48+48) characters

It's somewhat readable. There's room for improvement, though.

i,j;main(_){while((_=getchar())>=0)isspace(_)||(isalnum(_)?i++:j++);printf("%i + %i = %i",i,j
,i+j);}
share|improve this answer
add comment

JavaScript (55 + 55 = 110) (52 + 52 = 104)

ip=prompt();
$=ip.match(/[a-z0-9]/gi).length;
_=ip.match(/[^a-z0-9\s]/gi).length;
alert($+' + '+_+' = '+($+_));

For the sake of testing-convenience I also made the program itself ignore whitespace. If this is undesired the \s can be removed from the second regexp, retaining the alphanumeric balance.

Alternatively: (40 + 40 = 80)

_=prompt();
$=_.match(/[a-z0-9]/gi).length;
alert($+' + '+(_.length-$)+' = '+_.length)

This will always include whitespace in the non-alphanumeric count. Hence it can't be used to check the validity of itself: it returns 40 + 44 = 84, counting the whitespace in the output-string.

I should probably stop testing this in a console on this page: overriding $ breaks jQuery...

share|improve this answer
add comment

Bash + coreutils, 72 (36 + 36) non-whitespace chars

a=`tr -dc [:alnum:]<<<$1|wc -c`
n=`tr -dt [:space:]<<<$1|wc -c`
echo $a + $[n-a] = $n

Output:

$ ./alnumbalance.sh http://stackexchange.com 
20 + 4 = 24
$ ./alnumbalance.sh "$(cat alnumbalance.sh)"
36 + 36 = 72
$ 

Previous answer:

Pure Bash, 92 (46 + 46) non-whitespace chars

nosp=${1//[[:space:]]}
noaln=${nosp//[[:alnum:]]}
echo $[${#nosp}-${#noaln}] + ${#noaln} = ${#nosp}

Output:

$ ./alnumbalance.sh http://stackexchange.com 
20 + 4 = 24
$ ./alnumbalance.sh "$(cat alnumbalance.sh)"
46 + 46 = 92
$ 
share|improve this answer
    
Woohoo - it even beats golfscript! ;-) –  DigitalTrauma Mar 14 at 20:49
    
What about control characters? [:alnum:] is not the inverse of [:punct:]. Try eg head -c256 /dev/urandom | tr -d [:alnum:][:punct:] –  skibrianski Mar 15 at 1:55
    
@skibrianski good point. I've edited the answer to take this into account. –  DigitalTrauma Mar 15 at 2:07
add comment

GolfScript, 74 characters (=37+37)

{+}:PLUS;.,.@10,''*26,{65PLUS.32|}%PLUS$-,\1$-' + 'PLUS\PLUS' = 'PLUS\PLUS

Online test for the code with the code as input.

share|improve this answer
add comment

Ruby 38+38=76

This program counts trailing newline in the input.

puts"#{a=gets.scan(/[a-z0-9]/i).length}+#{b=$_.scan(/\W|_/).length}=#{a+b}"

The character count is done by the program itself: $ ruby alphabalance.rb alphabalance.rb :)

share|improve this answer
add comment

PowerShell (43+43=86)

Golfed

function alf($i){$a=0;$n=0;[char[]]$i|%{if($_-match"[a-zA-Z0-9]"){$a++}else{$n++}}; write-host "$a+$n=$($a+$n)"}

Un-golfed

function alf($i){
    $a=0;$n=0;  
    [char[]] $i | %{ if ($_ -match "[a-zA-Z0-9]") { $a++ } else { $n++ } };
    write-host "$a+$n=$($a + $n)"
}

Test

PS > alf "http://stackexchange.com"
20+4=24

Testing with the code itself to pass the criteria

PS > alf "function alf($i){$a=0;$n=0;[char[]]$i|%{if($_-match`"[a-zA-Z0-9]`"){$a++}else{$n++}}; write-host `"$a+$n=$($a+$n)`"}"
43+43=86

" has been escaped with ` which is not part of string.

share|improve this answer
add comment

Python (60+60 = 120)

Tough one, there is probably room for improvement. Like the fact the function itself can be used to evalulate its own alphnumeric balance.

def f(s):
 i=j=0
 for c in s:
  t=ord(c)
  if (t!=2**5): 
   i+=1  
  if (48<=t<=57 or 65<=t<=90 or 97<=t<=122):
   j+=1 
 print `j`,'+',`i-j`,'=',i      

Test:

>>> f("http://stackexchange.com")
20 + 4 = 24
share|improve this answer
add comment

Julia, 64

f(s)=(b=endof(s);a=sum([isalnum(c) for c in s]);"$(a) + $(b-a) = $(b)";)

All the only unnecessary non alphanumeric characters are the last ; and some of the () in the string formatting., it came out almost perfectly balanced, and as a power of 2 without much fiddling.

julia> f("http://stackexchange.com")
"20 + 4 = 24"
julia> nowhite(s)=join(split("s"," "))
julia> f(nowhite("f(s)=(b=endof(s);a=sum([isalnum(c) for c in s]);\"\$(a)+\$(b-a)=\$(b)\";)"))
"32 + 32 = 64"
share|improve this answer
add comment

perl, 64 non-whitespace chars:

$/=$,;
$_=<>;
s 0\s00g;
$\=length;
print s 1[a-z0-9]11ig .
      " + " .
      s 2.22g .
      " = "

Clarified slightly via perl -MO=Deparse and some comments:

$/ = $,;               # input record separator = a variable defaulting to undef
$_ = <ARGV>;           # slurp stdin
s/\s//g;               # strip whitespace
$\ = length $_;        # output record separator = total length of string sans whitespace
print s/[a-z0-9]//gi . ' + ' . s/.//g . ' = '; # count alphanumerics, then everything else

The ORS, $\ is appended automatically in every call to print, putting the total count at the end.

share|improve this answer
    
Had 66 characters on my first pass. Thanks to Heiko Oberdiek for showing that you can unset $/ with fewer chars by setting it to $, =) –  skibrianski Mar 15 at 2:46
add comment

Rebol (64 + 64 = 128)

f: func [x] [
    c: :charset
    a: c [#"a" - #"z"]
    s: c [#" " #"^/" #"^-"]
    n: complement union a s
    a+: n+: 0
    parse x [
        some [
            a (++ a+) |
            n (++ n+) |
            s
        ]
    ]
    print [a+ "+" n+ "=" a+ + n+]
]

Usage example (in Rebol console):

>> f "http://stackexchange.com"
20 + 4 = 24

NB. Program ignores spaces, tabs & newlines from counts.

share|improve this answer
add comment

C++, 146 (73+73) 178 (89+89) non-whitespace characters#

Original included <algorithm> for no good reason. Oops.

//create a test string
#include<string>
std::string a = "\?\?=include <cstdio>\
int x,y;\
int main()\?\?<\
    for(char c : a)\
            !isspace(c) ? (isalnum(c) ? y++ : x++) : 0;\
    printf(\"%d\?\?/t%c\?\?/t%d\?\?/t%c\?\?/t%d\?\?/n\",y,'+',x,'=',(x+y));\
\?\?>";

//Code itself starts here
??=include <cstdio>
int x,y;
int main()??<
    for(char c : a)
        !isspace(c) ? (isalnum(c) ? y++ : x++) : 0;
    printf("%d??/t%c??/t%d??/t%c??/t%d??/n",y,'+',x,'=',(x+y));
??>

I am only counting characters in the lines after //Code itself starts here. In particular, this means not counting #include <string>. I am also counting trigraphs as three characters each, which is perhaps debatable. Note that in testing the program on its own source code some care is required to prevent trigraph replacement within the string literal.

There are some peculiar design decisions here -- in most production code you will not encounter trigraphs and range-based-for loops in the same function -- but I think all within the bounds of 'justifiable'.

share|improve this answer
add comment

python 52 +52 = 104

Interesting challnge because python avoids non-alphanumeric characters.

def f(_):
    _=_.replace(" ","");l=len(_);a=sum([c.isalnum() for c in _][:l]);print("{0} + {1} = {2}".format(a,l-a,l))

Minor justification for using slice: it speeds it up (maybe?)

share|improve this answer
add comment

J - 46 + 46 = 92

Counts whitespace, so you can't self-test without a modification. Takes input on stdin. Has a bad mouth, should go wash it out with soap.

;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10

Usage:

   ;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
http://stackexchange.com
20 + 4 = 24

   NB. modification for self-test:    vvvvvv - remove spaces, the only whitespace
   ;":&.>(+/;' + ';(#-+/);' = ';#)(e.~' '-.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
;":&.>(+/;' + ';(#-+/);' = ';#)(e.~1!:1@1:)(,toupper)'golfscriptSUCKSabdehjkmnquvwxyz',,":"0 i.10
46 + 46 = 92
share|improve this answer
add comment

Python 2.7 - 43 + 43 = 86

As whitespace doesn't count here's a Python answer that might be considered formatted!

import re
__ = input()
b = re.subn('[^a-zA-Z0-9]', '', __)[1]
c = len(__)
print '%d + %d = %d' % (c - b, b, c)

.subn() is a nifty function that returns a tuple; the characters after the regular expression substitution has been made and the number of characters replaced.

If you're a serious golfer and can't deal with the whitespace :-) it can also be written as:

import re
__=input()
b=re.subn('[^a-zA-Z0-9]','',__)[1]
c=len(__)
print'%d+%d=%d'%(c-b,b,c)
share|improve this answer
add comment

Javascript - 76 (38 + 38)

_ = prompt()
o = _.match(/[a-z0-9]/gi).length
$ = _.length - o
alert(o + " + " + $ + " = " + (o + $))

Sample Input: http://stackexchange.com
Output: 20 + 4 = 24

Running on self:

var a  = '_ = prompt()o = _.match(/[a-z0-9]/gi).length$ = _.length - oalert(o + " + " + $ + " = " + (o + $))'

var letters = a.match(/[a-z0-9]/g).length; 
var nons = a.match(/[^a-z0-9 ]/g).length; // excludes whitespace from count

console.log(nons + " = " + letters); // 38 = 38 :)

P.S. For those concerned with (o + $) being done to maintain alphanumeric balance, it is not so. Because after seeing o + " + " JS would decide all + to be string concatenaters rather than number adders. Thus the parentheses are necessary, or 20 + 4 would become 204 rather than 24 :D

Happy Coding!

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.