16
\$\begingroup\$

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.

\$\endgroup\$
7
  • \$\begingroup\$ 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. \$\endgroup\$ Mar 14, 2014 at 17:58
  • 2
    \$\begingroup\$ will it all be ascii? \$\endgroup\$ Mar 14, 2014 at 17:58
  • \$\begingroup\$ @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. \$\endgroup\$
    – vsz
    Mar 14, 2014 at 18:19
  • 3
    \$\begingroup\$ Is the whitespace inside the output-string counted into the non-alphanumerics? Or ignored with all the other (non-string-literal) whitespace? \$\endgroup\$
    – Kninnug
    Mar 14, 2014 at 22:25
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ Mar 15, 2014 at 0:56

18 Answers 18

8
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ 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? \$\endgroup\$ Mar 15, 2014 at 0:51
  • \$\begingroup\$ @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. \$\endgroup\$ Mar 15, 2014 at 1:35
  • \$\begingroup\$ 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. \$\endgroup\$ Mar 15, 2014 at 2:07
  • \$\begingroup\$ haha now we have essentially identical code =) good show =) \$\endgroup\$ Mar 15, 2014 at 2:49
  • \$\begingroup\$ @skibrianski: :-) Thanks, you give me a reason to post one of the other variants with a little more differences. However the byte count remains. \$\endgroup\$ Mar 15, 2014 at 3:09
7
\$\begingroup\$

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);}
\$\endgroup\$
5
\$\begingroup\$

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
$ 
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Woohoo - it even beats golfscript! ;-) \$\endgroup\$ Mar 14, 2014 at 20:49
  • \$\begingroup\$ What about control characters? [:alnum:] is not the inverse of [:punct:]. Try eg head -c256 /dev/urandom | tr -d [:alnum:][:punct:] \$\endgroup\$ Mar 15, 2014 at 1:55
  • \$\begingroup\$ @skibrianski good point. I've edited the answer to take this into account. \$\endgroup\$ Mar 15, 2014 at 2:07
3
\$\begingroup\$

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.

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
2
\$\begingroup\$

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 :)

\$\endgroup\$
2
\$\begingroup\$

Powershell, 70 bytes (=35+35)

param($s)"$(($l=$s.Length)-($n=($s|sls '\W' -a).Matches.Count))+$n=$l"

Test script:

$f = {
param($s)"$(($l=$s.Length)-($n=($s|sls '\W' -a).Matches.Count))+$n=$l"
}

&$f "http://stackexchange.com"
&$f $f.toString().Trim()

Output:

20+4=24
35+35=70

Powershell, 70 bytes (=35+35), alternative

"$(($l="$args"|% Length)-($n=($args|sls '\W'-a).Matches.Count))+$n=$l"
\$\endgroup\$
2
\$\begingroup\$

Python 2 (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
\$\endgroup\$
2
  • \$\begingroup\$ What version of Python is this? \$\endgroup\$
    – Gigaflop
    Dec 11, 2018 at 14:55
  • \$\begingroup\$ @Gigaflop I edited it. The print statement is Python 2 only, as is the backtick syntax for repr. \$\endgroup\$
    – mbomb007
    Dec 11, 2018 at 20:07
1
\$\begingroup\$

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'.

\$\endgroup\$
1
\$\begingroup\$

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?)

\$\endgroup\$
1
  • \$\begingroup\$ Try using Python 2, since print doesn't require parenthesis, and using the '%d + %d = %d' % (a,l-a,l) method. That should save some characters. \$\endgroup\$
    – mbomb007
    Jan 14, 2015 at 16:52
1
\$\begingroup\$

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"
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ Had 66 characters on my first pass. Thanks to Heiko Oberdiek for showing that you can unset $/ with fewer chars by setting it to $, =) \$\endgroup\$ Mar 15, 2014 at 2:46
1
\$\begingroup\$

Python 2, 50 + 50 = 100

import re
def f(i):
    w = re.sub('\s', '', i)
    s = re.subn('[\W_]', '', w)
    a = len(s[0])
    print '%d + %d = %d' % (a, s[1], a+s[1])

Try it Online!

\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

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
\$\endgroup\$
0
\$\begingroup\$

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!

\$\endgroup\$
0
\$\begingroup\$

Clojure: (31 + 31 = 62) non-whitespace chars

(def ff #(let [c count y (c %) x (c (re-seq #"\w" %))] (str x " + " (- y x) " = " y)))

Output:

alphabalance.core=> (ff "http://stackexchange.com")
"20 + 4 = 24"
\$\endgroup\$
0
\$\begingroup\$

CJam, 27 + 27 = 54

CJam is a couple of months newer than this challenge, so this answer is not eligible for the green checkmark. It was a fun exercise anyway, though!

ea0=eu{A,s'[,65>+#)g}%_:+1@f-:+ea0=,]"DODDaD"36f-3/]zo

It takes the input string as a command-line argument, so it won't work in the online interpreter, but you can test it with the Java interpreter.

Explanation

"Distinguish alphanumeric characters:";
ea0=eu{A,s'[,65>+#)g}%
ea0=                   "Get the first command-line argument.";
    eu                 "Convert it to upper case.";
      {             }% "Map this block onto each character.";
       A,s             "Get the string '0123456789'.";
          '[,          "Get a string with all characters from the null byte to Z.";
             65>       "Remove the first 65 characters, to leave A to Z.";
                +      "Add to digit.";
                 #     "Find character in that string. Returns -1 if not alphanumeric.":
                  )g   "Increment and get signum. Yields 1 for alphanumeric characters,
                        0 otherwise.";

"Now we've got an array of 0s and 1s. Let's do the counting:";
_:+1@f-:+ea0=,]
_               "Duplicate array.";
 :+             "Get the sum. This is the number of alphanumeric characters.";
   1@           "Push a 1 and pull up the other copy of the array.";
     f-         "Subtract each element from 1, this swaps 0s and 1s.";
       :+       "Get the sum. This is the number of symbol characters.";
         ea0=   "Get the first command-line argument again.";
             ,  "Get its length. This is the total number of characters.";
              ] "Collect everything in an array.";

"And now the formatting:";
"DODDaD"36f-3/]zo
"DODDaD"          "Push this string.";
        36f-      "Subtract 36 from each character. This yields ' +  = '.";
            3/    "Split into two halves of 3 characters each.";
              ]   "Wrap this and the previous array in another array.";
               z  "Zip. Transposes the array to interleave strings with numbers.";
                o "Output the resulting array without delimiters.";
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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