27
\$\begingroup\$

Write a program or function which takes a string of text as input and outputs the number of non-alphabetical characters in it (standard I/O rules apply). A non-alphabetical character is any character not appearing in this string:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

In terms of ASCII character codes, that's anything not in ranges 65-90 inclusive or 97-122 inclusive.

Rules

Your program must be able to handle all of printable ASCII, as well as any characters that appear in your code. In other words, you may assume input will not contain any characters iff they are in neither printable ASCII nor your solution. Printable ASCII here means ASCII's character codes 32-126 inclusive as well as character code 10 (Meaning you will have to count spaces and newlines).

You may assume that input will not be empty.

Scoring

Your score is the number of non-alphabetical characters (not bytes!) in your code. Lowest score wins, with as a tie-breaker.

Examples

"input" => output
"example" => 0
"3xampl3" => 2
"Hello, World!" => 3
"abc.def-" => 2
"     " => 5
"IT'S NORMAL" => 2
"•¥¥€???!!!" => 10
"abc
def" => 1
\$\endgroup\$
2
  • 4
    \$\begingroup\$ I suggest renaming this to what the challenge actually is. At first I though this was some kind of complex number challenge. \$\endgroup\$
    – Seggan
    Commented Jun 8, 2022 at 18:48
  • 1
    \$\begingroup\$ @Seggan I could switch the order of the regex flags :P but yea it's pretty vanilla regex, I dunno. I'll think about verbosifying it. \$\endgroup\$ Commented Jun 8, 2022 at 19:23

46 Answers 46

1
2
3
\$\begingroup\$

PowerShell Core, 59 bytes, score: 8 10

$input-replace"[abcdefghijklmnopqrstuvwxyz]"|foreach Length

Try it online!

-2 bytes thanks to user314159

\$\endgroup\$
1
  • 3
    \$\begingroup\$ You can drop the round brackets, the -join, the i from -ireplace, and the .Length property, and instead pipe the output to "ForEach-Object -MemberName Length" for a score of 8 and still 59 bytes: $input-replace"[abcdefghijklmnopqrstuvwxyz]"|foreach Length \$\endgroup\$
    – user314159
    Commented Jun 13, 2022 at 16:59
3
\$\begingroup\$

Uiua, score 0, 15 bytes

redaddnotabssig

Try it: Uiua pad. Upon running, the code will be formatted to the following:

/+¬⌵±

This is because Uiua lets you type all of its glyphs using just alphabetical characters by using any prefix of that glyph's name 3 characters or longer. Some glyphs let you use 2 character prefixes, or even just the first letter in certain scenarios, but none of the ones used here do.

Explanation: the sign of a character gives -1 for lowercase letters, 1 for uppercase, and 0 for non-alphabet. Taking the absolute value of this gives 1 for alphabet and 0 for non-alphabet, and taking not of this gives the opposite. Then we reduce this list by addition, giving the total number of ones.

\$\endgroup\$
3
\$\begingroup\$

Uiua, 0 score, 14 bytes

firunwheabssig

Formats to ⊢°⊚⌵±.

Try it!

⊢°⊚⌵±
     ± # case of character (-1/1 for lower/uppercase, 0 for non a-z)
    ⌵  # absolute value (1 for a-z, 0 for non a-z)
 °⊚   # counts of 0s and 1s
⊢      # get first (count of 0s)
\$\endgroup\$
1
  • \$\begingroup\$ Smart to use firunwhe, didn't think of that. I just did redaddnot. \$\endgroup\$ Commented Oct 1 at 22:00
3
\$\begingroup\$

Jelly, score 0, 366 68 bytes

LdLYYTYYJSpppppJLdLdLdLdLdLNCNCNCNCSSSSRUFNMHpOIFACHHNCHNCNCRTLrLLCN

Try it online!

Uses a New and Exciting™️ construction for obtaining the pivotal [155, 64] constant from 91, which just so happens to require \$O(n^5)\$ time and memory:

pppppJLdLdLdLdLdLNCNCNCNCSSSS    Turn 91 into [155, 64]:

p                                Take the Cartesian product of [1 .. 91] with
                                 the input (whose length I'll call L).
p                                Length: 91 * L.
 p                               Do it again.
pp                               Length: 91 * L ** 2.
ppppp                            Do it a total of five times,
     J                           using [1 .. L] once for chaining purposes.
pppppJL                          Take the length: 91 * L ** 5.
       dL                        Divmod by L: [91 * L ** 4, 0].
         dL                      Do it again: [[91 * L ** 3, 0], [0, 0]].
           dL                    Do it a total of five times, vectorizing deeper
             dL                  each time and creating a big old nested mess
               dL                of pairs totaling 32 "leaf" scalar elements
    pJLdL                        all which are 0 except for one leftmost 91.
                 N               Negate each leaf
                  C              and subtract it from 1,
                 NC              which is to say add 1 *to each leaf*,
                 NCNCNCNC        four times.
                         S       Sum the outermost pair
                         SSSS    four times, leaving one remaining pair:
    pJLdL                        [91, 0]
                 NCNCNCNC        + (4
pppp     dLdLdLdL        SSSS    * 2 ** 4)
pppppJLdLdLdLdLdLNCNCNCNCSSSS    = [155, 64].

The Jelly interpreter as hosted on CPython absolutely is theoretically capable of running this program on itself--not the case with some of my earliest B-based attempts! MemoryErrors and OverflowErrors don't really keep you waiting--but you really, really don't want to. Fortunately, it is trivial to reduce the degree at the cost of length, removing one p, one dL, and one S to add four NCs, then eight, and so on, and doing so a maximal four times just brings you back to the vastly more performant previous revision:

Jelly, score 0, 366 365 200 194 172 bytes

LdLYYTYYJSpJLdLNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCRUFNMHpOIFACHHNCHNCNCRTLrLLCN

Try it online!

-1 remembering two golfs I'd thought of then abandoned two hours ago that cancel each other's problems/incompatibilities out

-165 by remembering I can divide by 2 before adding...

-6 with a little more goofier arithmetic

-22 realizing I can take distinct cumulative sums of integers with RUFNM, and that that's so powerful that it's worth having to go back to dividing by 2 only afterwards! (on account of the restriction to integers)

Relies extensively on the guarantee of non-empty input, allowing the length L to serve as a consistent positive integer that can math its way into making a handful of dyads not completely useless--aside from the underwhelming builtin set overall, the biggest weakness of ASCII-letter-only Jelly is the utter lack of any data-flow control whatsoever, rendering it completely impossible to combine two intermediate values if the right-hand one would be constructed by more than one builtin. However, most of the program is just spamming NC (negate then subtract from 1) as a vectorizing increment or CN as a vectorizing decrement--replacing those with normal additions or subtractions, we have:

LdLYYTYYJSpLLdL+64RUFNMH    Construct the range midpoints:
L                           The length of the input (let's call it L)
 dL                         divmod itself: [1, 0].
   Y                        Join on newlines: [1, '\n', 0].
    Y                       Join on newlines again: [1, '\n', '\n', '\n', 0].
     T                      Truthy indices: [1, 2, 3, 4].
      YY                    Join that on newlines, for a length of 13,
        JS                  then sum [1 .. 13]: 91.
          p                 Take the Cartesian product of that
         Sp                 (implicitly converted to a 1-range) with
           J                the range from 1 to L inclusive,
            L               and take the length of that: 91 * L.
             dL             Divmod by L: [91, 0].
               +64          Add 64: [155, 64].
                  R         Convert both to 1-ranges
                   U        descending,
                    F       concatenate them,
                     N      negate every element,
                      M     and take maximal (i.e. -1) indices: [155, 219].
                       H    Halve: [77.5, 109.5].
                    

pOIFACHH‘H+2RTLrLL’    Count the characters outside the ranges:
p                      Take the Cartesian product of the midpoints with
 O                     the codepoints of the input,
    A                  and take the absolute value of
  IF                   the difference of each pair.
     C                 Subtract the differences from 1,
      HH               divide by 4,
        ‘              increment,
         H             halve,
          +2           add 2,
            R          then finally convert to 1-ranges
            RT         which are empty if they're less than 1.
     CHH‘H+2           (21-d)/8 < 1 iff d > 13.
             TL        Count the nonempty ranges:
pOIFACHH‘H+2RTL        the number of characters *in* [A-Za-z].
               rL      Take the inclusive range from that to L,
                 L’    and subtract 1 from the length of that.

Yes, the 13th triangular number just so happens to also be 64 less than the sum of the codepoints of A and Z, i.e. twice their average which is 12.5 away from both of them, and that distance rounds up to 13. Wacky.

It feels like there might still be a cleverer way to construct the range midpoints using T or M, but the best I could think of so far was just Y spam that gets binary digits from the length of a list larger than the Jelly interpreter is capable of constructing, and at this point it might not beat 68.

If all Unicode characters with the Letter property counted as alphabetic, nŒsCS would be score 0 and 5 bytes, but rely heavily on the guarantee that the only non-ASCII characters in the input are the ones in the program--it counts the characters that don't change when case-swapped, but not all letters have case variants. Constructing a Python expression to access the unicodedata module with only letters could also be pretty entertaining itself...

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

Japt, score: 2, 5 bytes

;eF l
;     // Alternative initial variables, where F = "[A-Za-z]".
 eF   // Replace all occurrences of F in input with default value of empty string.
    l // Return the result's length.

Try it here.

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

APL (NARS2000) Score 19, 31 bytes

{≢⍵~⎕av[C,32+C←66..91]}

Examples

{≢⍵~⎕av[C,32+C←66..91]} 'Hello, World!' evaluates to 3

{≢⍵~⎕av[C,32+C←66..91]} '•¥¥€???!!!' evaluates to 10

How it works

⎕av[C,32+C←66..91] generates the alphabetic string from built-in ⎕av array. ~ performs set difference with right argument . counts number of element in the set difference.

Score of this expression could be determined by applying it to itself:

⎕fmt {≢⍵~⎕av[C,32+C←66..91]} '{≢⍵~⎕av[C,32+C←66..91]}' evaluates to 19.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Score 6, 8 bytes: ≢⍞∼⎕A,⎕a \$\endgroup\$
    – Adám
    Commented Jun 11, 2022 at 22:01
  • \$\begingroup\$ I didn't know existence of ⎕A and ⎕a and my ⎕av[C,32+C←66..91] is so inelegant. Will definitely apply them in future solutions. \$\endgroup\$
    – jimfan
    Commented Jun 11, 2022 at 23:29
  • \$\begingroup\$ @Adám I encourage you to put up a solution for Dyalog APL using ≢⍞∼⎕A,⎕a \$\endgroup\$
    – jimfan
    Commented Jun 11, 2022 at 23:31
  • \$\begingroup\$ It doesn't work in Dyalog APL, but does in APLX. I can post that. \$\endgroup\$
    – Adám
    Commented Jun 11, 2022 at 23:48
2
\$\begingroup\$

BQN, Score: 8, 60 bytes

+´¬∘∊⟜"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

Try it here!

Thanks to @thejonymyster for helping halve the score!... Of course at the cost of tripling the byte count ;D

Explanation

  • ¬∘∊⟜"..." replace each char in input with 0 if alphabetical, 1 otherwise
  • sum occurrences of 1
\$\endgroup\$
2
  • 2
    \$\begingroup\$ generating the alphabet seems to be giving you a lot of score that could be avoided :P \$\endgroup\$ Commented Jun 17, 2022 at 11:42
  • 2
    \$\begingroup\$ @jonymyster good point, I was too stuck in the code-golf mindset haha \$\endgroup\$ Commented Jun 17, 2022 at 12:33
2
\$\begingroup\$

Ruby, score 5, 67 bytes

p gets.count"^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

Attempt This Online!

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

Haskell + hgl, score 2, 14 bytes

cn$n<isAlpha

Attempt This Online!

Checks the number of characters not satisfying isAlpha.

isAlpha has a short version α, but that's not an alphabetic character according to the definition in the question, so while it would shorten things it would increase score. n < isAlpha is implemented as a function which would shorten things without increasing score, however the issue is that according to unicode α is an alphabetic character, and follows unicode's definition, not the challenge's. This is fine until we actually include α in the answer at which point it becomes wrong.

Alternate, score 2, 59 bytes

cna"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

Attempt This Online!

Reflection

Since this isn't a challenge it's not the most useful to reflect on this. I'm not going to say for example that α should be renamed for this challenge. In light of that however, I still have some thoughts.

The optimal (reasonable) score here is probably 1. There are a couple of reasonable ways this could happen:

  • There should probably be a builtin for the entire uppercase and lowercase alphabet. If it didn't contain special unicode characters, then we'd have a score one answer with cna alf.
  • There should be a builtin for cn<(n<), every other variant of cn has this, it's just frustratingly not present here. If it existed then cnN isAlpha would score 1.
  • There could be deprecated names for the negative character functions like isNotAlpha. This would improve usability a little bit in general and save us 1 score on this challenge.
\$\endgroup\$
1
\$\begingroup\$

Ruby, Score: 24, 42 bytes

->s{s.chars.map{_1=~/[A-Za-z]/?0:1}.sum-1}

Attempt This Online!

There is a -1 the end due to the \n. If you don't coun't it, it's 40 bytes and Score: 22

\$\endgroup\$
1
  • 1
    \$\begingroup\$ you can -2 your score if you verbosify your regex slightly :P \$\endgroup\$ Commented Jun 17, 2022 at 12:59
1
\$\begingroup\$

Brev, score 13

((fn (string-length (strse x 'alpha ""))) "(fn(string-length(strse x 'alpha \"\")))")
\$\endgroup\$
1
\$\begingroup\$

Japt v2.0a0, Score 2 (3 bytes)

è\L

Try it

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

Fig, score 0, \$4\log_{256}(96)\approx\$ 3.292 bytes

LFcB

Try it online!

Exact port of Vyxal. Beats it too :P

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

Go, score 30, 85 bytes

import."unicode"
func f(s[]rune)(c int){for _,e:=range s{if!IsLetter(e){c++}}
return}

Attempt This Online!

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

Uiua # Experimental!, score 0, 85 73 56 51 bytes

firunwhememdipabsaddranceipowetaaddtauetastringifyA

Try it here!

Formats to ⊢°⊚∈⊙⌵+⇡⌈ⁿη+τηstringifyA.

\$\endgroup\$
2
  • \$\begingroup\$ you can use ceipowetaaddtaueta to get 26 and move stringifyA to the end for 0 score (but +2 bytes)(pad) \$\endgroup\$
    – nyxbird
    Commented Sep 27 at 21:32
  • \$\begingroup\$ 0 score, 51 bytes \$\endgroup\$
    – nyxbird
    Commented Sep 27 at 21:52
1
\$\begingroup\$

AWK -F "", 13 score, 24 bytes

$0=NF-gsub(/[A-Za-z]/,x)
awk -F "" '$0=NF-gsub(/[A-Za-z]/,x)' <<< 890eee

$0=          # set output
NF           # number of chars
-gsub(       # returns number of matches
/[A-Za-z]/,  # regex
x)           # placeholder
\$\endgroup\$
1
2

Your Answer

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

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