Significant Whitespace

We define whitespace to be any of the three characters, tab (0x09), newline (0x0A) or space (0x20).

For this challenge, you're to write two programs or functions in the same programming language, which perform the following tasks:

1. Count the whitespace characters in a given string. For instance, the input

  123 -_-   abc
def


would return 7 (provided there is no trailing newline).

2. Split a given string at consecutive runs of whitespace. If the string starts or ends with whitespace, no empty strings should be returned at the ends. For instance, the same input

  123 -_-   abc
def


would return ["123", "-_-", "abc", "def"].

In either case you may take input via STDIN, command-line argument or function argument a return the result or print it STDOUT. For the second program, if you choose to print to STDOUT, please print each string on its own line, without surrounding quotes.

For both programs you may assume that the input contains only printable ASCII (0x20 to 0x7E) and whitespace.

Now here is the catch:

• If all whitespace is removed from both programs/functions, the resulting strings have to be identical. That is, your two submissions may only differ in the number and placement of whitespace characters.
• Neither program/function may contain any string or regex literals (character literals are fine, provided your language has a designated character type).
• Neither program/function may contain any comments.
• You must not read the program's source code, directly or indirectly.

This is code golf. Your score is the sum of the sizes of both solutions (in bytes). Lowest score wins.

The following Stack Snippet generates both a regular leaderboard and an overview of winners by language. So even if your language of choice doesn't let you win the entire challenge, why not try to snatch a spot on the second list? I'd be very interested to see how people tackle this challenge in a variety of languages!

# Language Name, N bytes


where N is the total size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes


You may also include the individual counts before the total count, e.g.

# Python 2, 35 + 41 = 76 bytes


The last number that is not struck through will be used by the snippet.

<script src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'></script><script>site = 'meta.codegolf',postID = 5314,isAnswer = true,QUESTION_ID = 42253;jQuery(function(){var u='https://api.stackexchange.com/2.2/';if(isAnswer)u+='answers/'+postID+'?order=asc&sort=creation&site='+site+'&filter=!GeEyUcJFJeRCD';else u+='questions/'+postID+'?order=asc&sort=creation&site='+site+'&filter=!GeEyUcJFJO6t)';jQuery.get(u,function(b){function d(s){return jQuery('<textarea>').html(s).text()};function r(l){return new RegExp('<pre class="snippet-code-'+l+'\\b[^>]*><code>([\\s\\S]*?)<\\/code><\/pre>')};b=b.items[0].body;var j=r('js').exec(b),c=r('css').exec(b),h=r('html').exec(b);if(c!==null)jQuery('head').append(jQuery('<style>').text(d(c[1])));if (h!==null)jQuery('body').append(d(h[1]));if(j!==null)jQuery('body').append(jQuery('<script>').text(d(j[1])))})})</script>

• That leaderboard is cool! – Digital Trauma Dec 9 '14 at 19:56
• Sounds like any Whitespace answer would comply with rule 1. ;) – nyuszika7h Dec 10 '14 at 14:31
• @nyuszika7h Indeed it would, but it won't be particularly short anyway. – Martin Ender Dec 10 '14 at 14:32
• Significant whitespace, so not whitespace with benefits or anything... – corsiKa Dec 10 '14 at 19:23
• So what in Bash count as string literals? – jimmy23013 Dec 22 '14 at 11:08

Pyth, 16 + 15 = 31 bytes

Try it here.

Counter:

L@,cb)sm!cd)b1 0


Splitter:

L@,cb)sm!cd)b10


These each define a function, y, which takes a string input to solve the desired task.

Thanks to @FryAmTheEggman for the idea of using Pyth's feature of modular indexing into lists to shave a character.

Test cases:

L@,cb)sm!cd)b1 0y++"abc def"b"gh ij k"
L@,cb)sm!cd)b10y++"abc def"b"gh ij k"


Explanation:

L                  define a function, y, which takes one input, b.
@                 Index into
,                2-tuple of
cb)             b.split()                          (solution to splitter)
s               sum over                           (solution to counter)
m              map, with input d, to
!cd)          logical negation of d.split()      (empty list is falsy)
b             over b.
Index is either:
10
1
Indexing is modulo the length of the list in Pyth.
0                 In one case, a 0 with a leading space is outside the function.
Leading space suppresses print, so the 0 is invisible.


Python, 54 + 56 = 110 bytes

Counter:

m=lambda x:sum(y.isspace()for y in x)
+1
0<9or x.split()


Splitter:

m=lambda x:sum(y.isspace()for y in x)+10<9or x.split()


For the counter, we use the fact that Python is okay with having just an expression on a line. It's necessary to split up +1 and 0<9or x.split() to stop a NameError from being thrown, as 0<9 being True prevents x.split() from being evaluated due to short circuiting.

For the splitter, since the number of whitespace is always nonnegative, sum(y.isspace()for y in x)+10<9 is always False and the splitting function comes into play.

Alternative 1, 59 + 60 = 119 bytes

Counter:

m=lambda x:[x.split(),sum(y.isspace()for y in x)][min([1])]


Splitter:

m=lambda x:[x.split(),sum(y.isspace()for y in x)][m in([1])]


The results of both counting and splitting are stored in a two-element list. The list is indexed by either min([1]), returning the minimum of the one-element list containing 1, or m in([1]), which returns False (equivalent to 0) as m is not contained in [1].

Alternative 2, 67 + 69 = 136 bytes

Counter:

ted=1;s=lambda x:[x.split(),sum(y.isspace()for y in x)][not s or ted]


Splitter:

ted=1;s=lambda x:[x.split(),sum(y.isspace()for y in x)][not sorted]


Like above, the results of both counting and splitting are stored in a two-element list. sorted is a built-in function which is a truthy value, so not sorted returns False (equivalent to 0). For not s or ted, since s is a function and also truthy, not s is False and ted = 1 is returned.

Alternative 3, 59 + 60 = 119 bytes

Counter:

def f(s):a=s.split();a1=len(s)-len((s*0).join(a));return a1


Splitter:

def f(s):a=s.split();a1=len(s)-len((s*0).join(a));return a
1


This is a function where splitter's result is stored in the variable a, and counter's result is stored in the variable a1. Like before, Python is fine with having just an expression on a line, in this case 1. Splitting up a1 determines what to return from the function.

• I'd +1 again for not sorted. – Martin Ender Dec 9 '14 at 17:02
• Is the newline between +1 and 0<9or x.split() needed? – isaacg Dec 9 '14 at 20:26
• @isaacg If you care about exceptions, then yes – Sp3000 Dec 9 '14 at 20:28
• You can shave off 3 bytes with m=lambda x:sum(y.isspace()for y in x)+00and x.split() and m=lambda x:sum(y.isspace()for y in x)+0;0and x.split() (making that semicolon a newline of course) – cjfaure Dec 11 '14 at 18:14
• @cjfaure I don't think the first one works as a splitter if there's no whitespace – Sp3000 Dec 11 '14 at 18:19

Java 8, 239 + 240 = 479

Count the whitespace (returns Integer)

Object f(String s){String r=new String(new char[]{92,'s'}),e=new String();int intx=0;intx=1;return intx>0?s.chars().filter(c->c==9|c==10|c==32).count():java.util.Arrays.stream(s.split(r)).map(t->t.replaceAll(r,e)).filter(t->t.length()>0);}


Split on the whitespace (returns Stream<String>)

Object f(String s){String r=new String(new char[]{92,'s'}),e=new String();int intx=0;int x=1;return intx>0?s.chars().filter(c->c==9|c==10|c==32).count():java.util.Arrays.stream(s.split(r)).map(t->t.replaceAll(r,e)).filter(t->t.length()>0);}


Explanation:

Object f(String s){
String r=new String(new char[]{92,'s'}),e=new String();  // init regex'es

int intx=0;     // critical variable

intx=1;         // change intx to 1
OR
int x=1;        // new, unused variable

return intx>0 ? // test variable to decide what to do
s.chars().filter(c->c==9|c==10|c==32).count() :
java.util.Arrays.stream(s.split(r)).map(t->t.replaceAll(r,e)).filter(t->t.length()>0);
}

• To me, this looks brilliant. +1 – Rodolfo Dias Dec 11 '14 at 11:20
• Pretty good, considering that Java has to be the worst language for code golfing. – tbodt Dec 13 '14 at 4:38

Whitespace, 75 + 153 = 228

Spaces, tabs and newlines substituted by STL, respectively, and folded for legibility. Convert to a proper Whitespace file with tr -d \\n | sed 'y/STL/ \t\n/'.

Counter

SSSLLSSLSSSTSSSSSLSLSSLSTLTSTTTSLSLTSSLTSSTLTTLSSSTLTSSSLSLLLSSSLS
LLSLLTLST


Splitter

SSSTLSSSLTTSLSSTLSSSLSLSTLTSTTTSLSSLSLTSLSSSTSSSSTLTSSTLTTTTLSSSTL
SLSTTSLSLSLLSSTTLSSSTSTSLSSSTLTTTLTSTSLTLSSSSSTLSSSLTTSLSSTSLLSLTL
LSSSLTLSSLSLTLLSSLLLL

• I was wondering how long it would take... ;) – Martin Ender Dec 12 '14 at 2:08
• With whitespace, this becomes two separate code golfing challenges. – tbodt Dec 13 '14 at 4:51

Marbelous, 103 + 92 = 195

Counter:

@0      @200@1@3
]]&1]]3W
\\!!?001
&0  >W@1
>W    @2/\  /\@3+A
00&0      &1
Dp
@0//


Splitter:

@0  @200@1    @3
]]&1]]  3W    \\
!!    ?001&0
>W@1>W@2
/\  /\@3+A00&0
&1Dp@0
//


Test these programs here. Cylindrical Boards, Include Libraries, and spaces for blank cells must all be checked.

Input and Output are through STDIN/STDOUT.

Explanation

Counter:

The blue path receives input. If the character is a whitespace character (ascii value less than 0x21), the black path is taken, which synchronizes with the purple path.

The purple path simply increments a marble stored in the &1 synchronizer each time the black path is taken.

Once there is no more input, the red path is taken, printing the number of whitespace characters and exiting.

Splitter:

The program begins with the blue path, which loops until a non-whitespace character is found.

Once a non-whitespace character is retrieved, the black path is taken, which prints out this character and moves execution to the green path, which loops and prints until a whitespace-character is received. Execution then continues to the purple path, which contains 3W, or a three-way splitter.

The left branch moves execution to the blue path (and whitespace is discarded until a non-whitespace character is found).

The middle branch sets the copy of the input to 0 with ?0 (generate a random number between 0 and 0), and adds 10 (0x0A = newline), which is then outputted.

• It seems like the splitter doesn't terminate if there's trailing whitespace. – Martin Ender Dec 13 '14 at 9:11

CJam, 26 + 27 = 53 59 61 73 77 bytes

Counter

'!q_,\SN9c++SerS%:Qs,-QN*?


Splitter

' !q_,\SN9c++SerS%:Qs,-QN*?


How it works

The idea is simple, calculate both number of whitespaces and split the string on consecutive runs of whitespaces. Then choose one of them based on the following fact that ' ! means not of space character which is falsy, while '! is the ! character, which is truthy.

Expanded code:

'! / ' !                              "Get truthy or falsy value";
q_                            "Read the input, make a copy";
,\                          "Take the length of the copy and swap";
SN9c++                    "Get a string comprised of all Whitespaces";
Ser                 "Replace any occurrence of any character of"
"the above string with space";
S%               "Split on one or more runs of space";
:Qs,           "Store this in Q, flatten to a string and take length";
-          "Subtract from total length to get count";
QN*       "Put the splitted array on stack and join newline";
?      "Base on the truthy or falsy value, pick one to print";


Input is from STDIN and output is to STDOUT

Try it online here

Mathematica, 44 + 43 = 87 97 bytes

I thought I'd add another language to the mix.

Counter:

StringCount[#,Whitespace]+0 1StringSpli t@#&


Splitter:

String Count[#,Whitespace]+01StringSplit@#&


This makes use of Mathematica's feature that space separation is the same as multiplication. And that multiplying something with 0 is always 0, and adding 0 to something is always idempotent.

For the counter, we first count the whitespace, and the we add 0*1*StringSpli*t@#. StringSpli and t aren't defined, but Mathematica uses symbolic computation, so it just treats those as an unknown variable and function. The 1* is idempotent (just as 0+), the 0* turns it into a zero. It is necessary to separate the StringSplit into two variables, because 0 times a list is treated as a scalar-vector multiplication which results in a vector (list) of zeroes.

For the splitter, I'm using the fact that Count also exists but doesn't look into strings. It tries to count all subexpressions matching the pattern, but Whitespace is a pattern that only applies to string contents. So Count will always return 0, which makes the String vanish. The multiplication of the split array by 01 = 1 is again idempotent.

Ruby, 107 91 bytes

Splitter (46 bytes)

p
p=gets(p).split
puts p||$_.size-pp.join.size  Counter (45 bytes) pp=gets(p).split puts p||$_.size-pp.join.size


p is a predefined method that, with no arguments, just returns nil. We use this in several ways. In the splitter, the initial p does nothing. gets(p) reads in everything from standard input, since the delimiter is null. We call the built-in split method on that, and assign the result to p, so now when it's not given arguments it'll be parsed as a variable. puts p||... short-circuits and prints each element of the p variable to its own line.

In the counter, we delete the first newline so that the split array is assigned to pp instead. Since we haven't assigned to p, it's still the nil-returning method so the second part of the || gets evaluated and passed to puts. $_ is a magic variable containing the result of gets, so the total amount of whitespace is the size of that minus the non-whitespace characters, which are what pp contains. I feel like there should be a shorter way of doing the counting, but I can't find one, and at any rate using the split array in the counter is fun. Python, 169 It's almost too easy to do this in Python! Counter: def x(s): def y(s): return sum(map(str.isspace,s)) return s.split() return y(s)  Splitter: def x(s): def y(s): return sum(map(str.isspace,s)) return s.split() return y(s)  They differ in only one space, and I'm not doing any trickery like splitting a number or a variable name in half :) C, 138+136 = 274 In each case, the code is a program that accepts exactly one command-line argument and prints the result to stdout. \t should be replaced with a tab character. If you want to pass an argument containing tabs and newlines, it's your job to figure out how ;). Counting #define strtok strpbrk h=' \t\n';main(c,v,n)char**v;{for(n=- -c-1;v[n]=strtok(1[v],&h);n[v]++)v[!n]=0,puts(*v),c++;n*='d%';printf(&n,c-2);}  Splitting #define strtokstrpbrk h=' \t\n';main(c,v,n)char**v;{for(n=--c-1;v[n]=strtok(1[v],&h);n[v]++)v[!n]=0,puts(*v),c++;n*='d%';printf(&n,c-2);}  JavaScript, 95 + 96 = 191 bytes Counter: c=(a,v)=>{v v=a=a.split(RegExp(String.fromCharCode(92,115)));return v?a.length-1:a.filter(j=>j)}  Splitter: s=(a,v)=>{vv=a=a.split(RegExp(String.fromCharCode(92,115)));return v?a.length-1:a.filter(j=>!!j)}  Ungolfed: s=(a,v)=>{ v // this v is ignored, or combined with the next line to make vv // split array and store it in a and v (or alternatively vv) v = a = a.split(RegExp(String.fromCharCode(92,115))); return v? a.length-1 // return number of whitespace chars : a.filter(j=>j) // return array without empty strings }  The RegExp(String.fromCharCode(92,115) line creates the whitespace-matching regex /\s/ without regex or string literals. In each program, we use the variable v or vv. We store the split array into that variable (v or vv), and then branch our behavior on the value of v (meanwhile, vv is ignored). In the counter, v has a truthy value; in the splitter it has a falsy value (because vv got the value instead). Alternate: JavaScript, 250 bytes I have another solution that isn't winning any prizes for brevity, but I thought it was an interesting challenge to abuse JavaScript's automatic semicolon insertion behavior. Counter: c=a=>{a=a.split(RegExp(String.fromCharCode(92,115)));x:for(i=2;i--;)for(o=i?a.length-1:a.filter(j=>j);1;){break x}return o}  Splitter: s=a=>{a=a.split(RegExp(String.fromCharCode(92,115)));x:for(i=2;i--;)for(o=i?a.length-1:a.filter(j=>j);1;){break x}return o}  Ungolfed counter: s=a=>{ a = a.split( RegExp(String.fromCharCode(92,115)) // split input on whitespace regex /\s/ ); x: // x is a label for the outer loop for(i=2;i--;) // run this outer loop twice for(o=i? // if this is the first outer loop pass, a.length-1 // set o to number of whitespaces : // else, on second outer loop pass, a.filter(j=>j); // set o to split input (w/o empty strings) 1; // 1 is truthy; run inner loop forever ) { break x; // break out of outer loop } return o; // return o }  The splitter is exactly the same, except for the line: break x;  is now break x;  JavaScript's automatic semicolon insertion normally does not terminate multi-line statements early if they can be understood without a line break, but it does not tolerate line breaks after return, continue or break. Therefore, the line is read simply as break, which breaks out of the inner loop only, instead of breaking out of the outer loop. The "second-pass" behavior o = a.filter(j=>j) is then executed (versus being skipped in the counter), because the outer loop is given a second pass. • Is !!x different to the auto Bool convert? – l4m2 Mar 31 '18 at 13:24 • @l4m2 It is not! I have removed it, since filter auto-bools its callback return by the same rules as !!. Thanks! – apsillers Mar 31 '18 at 17:26 Python, 228198182166146 145 bytes Counter (72 bytes): ior1=0 w=lambda i:i.split()if ior1 else sum(ord(c)in(9,10,32)for c in i)  Splitter (73 bytes): ior1=0 w=lambda i:i.split()if i or 1else sum(ord(c)in(9,10,32)for c in i)  ior1 is a falsey variable, but i or 1 is truthy. That's the main trick. • Doesn't this break if i is the empty string for the splitter? Could be fixed by changing iorb to ior1, which also lets you save the character between 1 and else. – isaacg Dec 9 '14 at 23:56 • @isaacg I totally forgot that you could have numbers in variable names! Thank you <3 – undergroundmonorail Dec 10 '14 at 13:17 Befunge 98, 61 + 59 = 120 Counter: ~:'!-0#v_ >$1+#@ #. #
@#,#$#<_v#0-!':~ # .# ,#$ #+55<v


Splitter:

~:'!-0#v_ >$1+#@ #.# @#, #$#<_v#0-!':~ #.#
, #$#+55<v  Bash, 75 + 79 = 154 bytes This relies on bash being able to continue execution even if some lines or parts of a line of the script are malformed (in some circumstances). Whitespace is used to deactivate escapes for some close brackets, and to break a pipe by putting it on a new line. Splitter: echo$((echo $1|wc -w+${#1}-$(\)\)\) for a in$1;do echo $a;done|wc -c)))  Counter: echo$((echo $1|wc -w+${#1}-$(\ )\ )\ ) for a in$1;do echo $a;done |wc -c)))  Input is via commandline argument, output is via stdout. Because this relies on bash error behaviour, the user is expected to ignore stderr. Example run (showing input with a newline and multiple contiguous spaces): # bash counter.sh "abc def gh ij k" 2>/dev/null 6 # bash splitter.sh "abc def gh ij k" 2>/dev/null abc def gh ij k  Ruby, 114 + 116 107 + 109 = 216 bytes This can't compete with the ruby solution by histocrat, but I thought it would be worth putting up anyway. I used $z for nil and nil.to_s for String.new

The extra whitespace character that I add to the end of the input is to force the last word to be added to the array (r) - a word is only added to the end of the array when a whitespace character follows a non-whitespace character. The alternative was adding another r<<w if w after the each_byte block.

Counting

->s{r=[]
n=-1
w=$z (s+9.chr).each_byte{|b|if b<33 r<<w if w w=$z
n+=1
else
w=w.to_s+b.chr
end}
$zorr ?r:n}  Splitting ->s{r=[] n=-1 w=$z
(s+9.chr).each_byte{|b|if b<33
r<<w if w
w=$z n+=1 else w=w.to_s+b.chr end}$z or r ?r:n}

• Ah, I wasn't sure about the ruling on that one. I've changed it to use if-else instead of the ternary operator - no more string literals. – alexanderbird Dec 13 '14 at 9:47

Java 8, 187 + 188 = 375

First of all, I'd like to say that this answer is heavily based on @Ypnypn's. I basically replaced some parts with shorter ones (including the whitespace-dependent part, which IMO is the most important in this challenge), but the functional code is mostly the same.

Count the whitespace, 187 (returns int):

Object f(String s){String r=new String(new char[]{92,115});int a=0;return a--+a>0?s.chars().filter(c->c>8&c<11|c==32).count():java.util.Arrays.stream(s.split(r)).filter(t->t.length()>0);}


Split on the whitespace, 188 (returns Stream<String>):

Object f(String s){String r=new String(new char[]{92,115});int a=0;return a- -+a>0?s.chars().filter(c->c>8&c<11|c==32).count():java.util.Arrays.stream(s.split(r)).filter(t->t.length()>0);}


J, 48 + 49 = 97 char

Two functions taking and returning a single argument. Used the crappiest way that I could think of to beat the same-but-whitespace rule, so there are probably characters to be saved by finding a more clever route around that.

(aa[aa=.+/@a=.a:-.~(e.u:)&9 10 32 :(<;._1~1,}.))      NB. count
(a a[aa=.+/@a=.a:-.~(e.u:)&9 10 32 :(<;._1~1,}.))     NB. split


We define the verb a to have two different actions, depending on whether it's used with one argument or with two. With one argument, it is (e.u:)&9 10 32, which checks if each character is whitespace or not. With two arguments, it is a:-.~(<;._1~1,}.), which takes a boolean vector on the right and cuts the left argument at those positions, throwing out any empty cuts with a:-.~.

Then, we define aa to be number of True values in the result of a, which only makes sense with one argument. Finally, we use aa or a a depending on whether we want to count or split the whitespace of the string. aa works as expected.

The reason a a works is because when J sees (f g)y, it considers (f g) a hook and evaluates it like y f (g y). In this case, f is the dyadic a above which does the cutting, and g is a[aa, which calculates the sum from aa, throws it out, and calculates (monadic) a again.

At the REPL:

   (aa[aa=.+/@a=.a:-.~e.&(u:9 10 32) :(<;._1~1,}.))   '  123',TAB,'-_-   abc',LF,'def'
7
(a a[aa=.+/@a=.a:-.~e.&(u:9 10 32) :(<;._1~1,}.))  '  123',TAB,'-_-   abc',LF,'def'
+---+---+---+---+
|123|-_-|abc|def|
+---+---+---+---+


Bash, 54 + 50 = 104 bytes

Counter

a=$IFS IFS= cat()(tr -cd$a|wc -c)
printf %s \\n$1|cat  Splitter a=$IFSIFS=ca
t()(tr-cd$a|wc-c) printf %s\\n$1|cat


Haskell, 53 + 55 = 108 bytes

Counter

f=do
pure(length.filter(elem[' ','\t','\n']))words


Splitter

f=do
pure(length.filter(elem[' ','\t','\n']))
words


This solution makes use of the fact that in Haskell functions are an instance of the Monad type class and thus can be used as monadic actions in do-notation.

In the first case the resulting function of the do-block is the first argument of pure (which is essentially const for the function type), making the counter the final result and the splitter being discarded.

In the second case pure is only applied to a single argument, making it a function that returns another function (the counter). However, the result is never used and thus discarded. The final result is the second line of the do-block, the splitter.

• Nice approach! [' ','\t','\n'] can be shortened to " \t\n". – Laikoni Mar 31 '18 at 9:11
• @Laikoni The description only allows character literals, no string or regex literals – siracusa Mar 31 '18 at 14:09

Python 2, 98

Splitting (49)

Returns the tokens in a list.

f=lambda s:[sum(map(str.isspace,s))]and s.split()


Counting (49)

Returns a list of length one containing the number of space characters. It will most likely cause a runtime error, but the function f can be used after executing the code.

f=lambda s:[sum(map(str.isspace,s))]
ands.split()


Perl, 37 + 38 = 75

Counter:

sub f{($_)=@_;(y/ - //,[split])[0.1]}  Splitter: sub f{($_)=@_;(y/   - //,[split])[0 .1]}


Perl 6, 31 + 32 = 63 bytes

Counter

{?^1??.words!!+grep 33>*,.ords}


Try it online!

?^1 is parsed like ?^ 1 which applies the Boolean negation operator to 1, resulting in False.

Splitter

{? ^1??.words!!+grep 33>*,.ords}


Try it online!

? ^1 converts the range 0..0 to Bool, resulting in True.

C (gcc), 88 + 89 = 177 bytes

Splitter

i,n,x,c;f(char*s){x=n=i=0;for(x+++n;c=*s++;c*x&&putchar(c))i=c<33?n++,c=i*10,0:1;x=n-1;}


Splitter

Counter

i,n,x,c;f(char*s){x=n=i=0;for(x+ ++n;c=*s++;c*x&&putchar(c))i=c<33?n++,c=i*10,0:1;x=n-1;}


Counter

Rundown

Takes input as a function argument. The counting function returns the number of whitespace. The splitting function uses STDOUT for its output (but incidentally returns number of whitespace minus one as well).

i,                      Flag signalling whether we are inside a word.
n,                      Number of whitespace encountered.
x,                      Flag signalling whether or not we should output the words.
c;                      Current character.
f(char*s){              Take input string as an argument.
x=n=i=0;for(            Initialise everything and start loop.
x+++n;                  SPLITTER ONLY: Interpreted as x++ +n, meaning x is set to 1 and n stays 0.
x+ ++n;                 COUNTER ONLY: Inverse of the above. Sets n to 1, and x stays 0.
c=*s++;                 Sets c to current char and increment string pointer, end loop if end of string.
c*x&&putchar(c))        Only output c if x is 1 and c is non-zero, which only happens if we left a word.
i=c<33?                 Since input contains only printable ASCII or whitespace, anything below 33 is whitespace.
n++,             ...if that is the case, increment the whitespace counter (n)
c=i*10,      ...and set c to newline (10), but only if we just left a word (if i is 1)
0:    ...and set i to 0.
1;  If not a whitespace, set i to 1, signalling we are inside a word.
x=n-1;}                 Implicitly returns n-1, which is the number of whitespaces if we are in the counter function.


Zsh, 36+36=72 bytes

Split:

: <<<${#1//[^$IFS]}
:
<<<${(F)${=1}}


Count:

:
<<<${#1//[^$IFS]}
: <<<${(F)${=1}}


The : builtin is equivilent to true, we use it as something between a comment and /dev/null (since comments are disallowed) by piping the unwanted expansion to it.

Zsh has a builtin for splitting on whiltespace, that being \${=var}. This makes it hard to do any kind of logical combination other than just executing both and discarding the one we don't want.

Try it online!