# Count of “a”s and “b”s must be equal. Did you get it computer?

In the popular (and essential) computer science book, An Introduction to Formal Languages and Automata by Peter Linz, the following formal language is frequently stated:

mainly because this language can not be processed with finite-state automata. This expression mean "Language L consists all strings of 'a's followed by 'b's, in which the number of 'a's and 'b's are equal and non-zero".

# Challenge

Write a working program/function which gets a string, containing "a"s and "b"s only, as input and returns/outputs a truth value, saying if this string is valid the formal language L.

• Your program cannot use any external computation tools, including network, external programs, etc. Shells are an exception to this rule; Bash, e.g., can use command line utilities.

• Your program must return/output the result in a "logical" way, for example: returning 10 instead of 0, "beep" sound, outputting to stdout etc. More info here.

• Standard code golf rules apply.

This is a . Shortest code in bytes wins. Good luck!

### Truthy test cases

"ab"
"aabb"
"aaabbb"
"aaaabbbb"
"aaaaabbbbb"
"aaaaaabbbbbb"


### Falsy test cases

""
"a"
"b"
"aa"
"ba"
"bb"
"aaa"
"aab"
"aba"
"abb"
"baa"
"bab"
"bba"
"bbb"
"aaaa"
"aaab"
"aaba"
"abaa"
"abab"
"abba"
"abbb"
"baaa"
"baab"
"baba"
"babb"
"bbaa"
"bbab"
"bbba"
"bbbb"


Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

/* Configuration */

var QUESTION_ID = 85994; // Obtain this from the url
// It will be like https://XYZ.stackexchange.com/questions/QUESTION_ID/... on any question page
var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk";
var OVERRIDE_USER = 48934; // This should be the user ID of the challenge author.

/* App */

return "https://api.stackexchange.com/2.2/questions/" +  QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER;
}

}

jQuery.ajax({
method: "get",
dataType: "jsonp",
crossDomain: true,
success: function (data) {
data.items.forEach(function(a) {
});
comment_page = 1;
}
});
}

jQuery.ajax({
method: "get",
dataType: "jsonp",
crossDomain: true,
success: function (data) {
data.items.forEach(function(c) {
if (c.owner.user_id === OVERRIDE_USER)
});
else process();
}
});
}

var SCORE_REG = /<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/;

function getAuthorName(a) {
return a.owner.display_name;
}

function process() {
var valid = [];

var body = a.body;
if(OVERRIDE_REG.test(c.body))
body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>';
});

var match = body.match(SCORE_REG);
if (match)
valid.push({
user: getAuthorName(a),
size: +match[2],
language: match[1],
});

});

valid.sort(function (a, b) {
var aB = a.size,
bB = b.size;
return aB - bB
});

var languages = {};
var place = 1;
var lastSize = null;
var lastPlace = 1;
valid.forEach(function (a) {
if (a.size != lastSize)
lastPlace = place;
lastSize = a.size;
++place;

.replace("{{NAME}}", a.user)
.replace("{{LANGUAGE}}", a.language)
.replace("{{SIZE}}", a.size)

var lang = a.language;
if (/<a/.test(lang)) lang = jQuery(lang).text();

languages[lang] = languages[lang] || {lang: a.language, user: a.user, size: a.size, link: a.link};
});

var langs = [];
for (var lang in languages)
if (languages.hasOwnProperty(lang))
langs.push(languages[lang]);

langs.sort(function (a, b) {
if (a.lang > b.lang) return 1;
if (a.lang < b.lang) return -1;
return 0;
});

for (var i = 0; i < langs.length; ++i)
{
var language = jQuery("#language-template").html();
var lang = langs[i];
language = language.replace("{{LANGUAGE}}", lang.lang)
.replace("{{NAME}}", lang.user)
.replace("{{SIZE}}", lang.size)
language = jQuery(language);
jQuery("#languages").append(language);
}

}
body { text-align: left !important}

width: 290px;
float: left;
}

#language-list {
width: 290px;
float: left;
}

font-weight: bold;
}

table td {
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr>

</tbody>
</table>
</div>
<div id="language-list">
<h2>Winners by Language</h2>
<table class="language-list">
<tr><td>Language</td><td>User</td><td>Score</td></tr>
<tbody id="languages">

</tbody>
</table>
</div>
<table style="display: none">
</tbody>
</table>
<table style="display: none">
<tbody id="language-template">
</tbody>
</table>

• Can the input be empty? (You're saying it's not part of the language, but not whether it's an input we need to consider.) – Martin Ender Jul 20 '16 at 20:04
• What if our language doesn't have truthy or falsy? Would empty string == truthy and non-empty string == falsy be acceptable? – James Jul 20 '16 at 20:20
• Nice challenge, but I think the title could be a little less ambiguous (i.e. a mention of a^n b^n or similar, rather than just the number of as equalling the number of bs) – Sp3000 Jul 21 '16 at 12:28
• @Sp3000 I choosed this title because it looked fun . I may change it later to sth else ... – user55673 Jul 21 '16 at 13:27
• I'm a little surprised that in 50+ answers I'm the only one to use a paser generator. To be sure it's not strictly competitive on length, but the problem posed is one of parsing a simple but non-trivial language. I'd very much like to see answers in other compiler-compiler syntaxes because I am not widely familiar with the choices. – dmckee --- ex-moderator kitten Jul 22 '16 at 22:47

# Octave, 28 bytes

@(m)diff(+m)>=0&~sum(m-97.5)


This defines an anonymous function. It works also for empty input. Falsy and truthy are as described in my MATL answer.

### Explanation

diff(+m)>0 checks if the input string (consisting of 'a' and 'b') is sorted, that is, all characters 'a' come before all 'b'.

The other condition that needs to be checked is whether the numbers of characters 'a' and 'b' are the same. Since their ASCII codes are 97 ansd 98, this is done subtracting 97.5 and chacking if the the sum is zero.

For empty input the result is empty, which is falsy.

## Mathematica, 45 bytes

#~StringMatchQ~RegularExpression@"(a(?1)?b)"&


Another recursive regex solution. This doesn't need anchors because StringMatchQ achors it implicitly, but unfortunately it just seems to do wrap the regex in ^(?:...)$ which means we can't use (?R) for the recursion, as that gets the anchors as well. Hence the seemingly useless group around the entire regex, so we can access only that part for the recursion. # Mathematica 838068 54 bytes #&@@@#<>""=="ab"&&Equal@@Length/@#&@*Split@*Characters  Thanks @MartinEnder for shortening it by 26 bytes :) If input can be a list of characters instead of a string, 39 bytes is possible: #&@@@#=={a,b}&&Equal@@Length/@#&@*Split  eg: #&@@@#=={a,b}&&Equal@@Length/@#&@*Split@{a,b,a,b,a,b} (*False*)  • It's probably shortest with a recursive regex: #~StringMatchQ~RegularExpression@"(a(?1)?b)"& – Martin Ender Jul 21 '16 at 9:41 • @MartinEnder Ah yes - much better - I'll delete & let you post that, since it doesn't resemble my awful attemp in the slightest! – martin Jul 21 '16 at 9:43 • No, don't delete yours. A regex-less solution is still interesting. :) – Martin Ender Jul 21 '16 at 9:43 # Racket, 91 bytes (λ(x)((λ(y)(equal?(append(make-list(- 1 y)#\a)(make-list y #\b))(cdr x)))(/(length x)2)))  Expects input in the form of a list of characters. If you really need to put it in as a raw string, that adds 21 extra characters (for 112 bytes): (λ(x)((λ(y)(equal?(append(make-list(- 1 y)#\a)(make-list y #\b))(cdr(string->list x))))(/(string-length x)2)))  An even longer (102 bytes with list input) way, but I think it's creative so I'm leaving it here: (λ(x)(and(eqv?(/(length x)2)(length(member #\b x)))(eqv?(length(remove-duplicates(member #\b x)))1)))  Explanation to follow. ## JavaScript, 34 bytes s=>(s=s.match^a(.*)b$[1])?f(s):1


In true automata fashion, this function returns 1 if it's true, and fails if it's not.

f=s=>(s=s.match^a(.*)b$[1])?f(s):1 let test_strings = ["ab", "aabb", "", "a", "abb", "abc", "abab", "abba"]; test_strings.map(s => { try {console.log("f(\"" + s + "\") returned " + f(s));} catch(e) {console.log("f(\"" + s + "\") threw " + e);} }); # Excel, 55 bytes =AND(A1<>"",A1=REPT("a",LEN(A1)/2)&REPT("b",LEN(A1)/2))  Test string in cell A1, formula above in any other cell. Generates a comparison string of the appropriate length and checks for a match. Shows TRUE or FALSE as appropriate. # PHP, 61 40 bytes new approach inspired by Didz´ answer: regexp with a recursive pattern <?=preg_match('#^(a(?1)?b)$#',$argv[1]);  P.S.: I see now that I am not the first one with this pattern. You never stop learning. Josh´s C solution translated to PHP comes at the same size (with one byte lost in translation, one byte golfed for PHP with bitwise and, one byte golfed for C and PHP): <?=strlen($s=$argv[1])==2*strspn($s,a)&$s[strrpos($s,a)+1]>a; (61 bytes)

My second own approach, a little longer: build a string with (input length / 2) of a, one of b and compare the concatenation to input:
<?=str_repeat(a,$n=strlen($s=$argv[1])/2).str_repeat(b,$n)==$s; (63 bytes) Could save 3 bytes on that if I could use ($r=str_repeat) for a function call directly ... if.

all versions:

• take the string as argument from cli
• print 1 for true, nothing for false

testing

• replace <?= with <?function f($s){return • remove =$argv[1] (or replace $argv[1] with $s)
• append } and my test suite (below)
• call in a web browser

function out($a){if(is_object($a)){foreach($a as$v)$r[]=$v;return'{'.implode(',',$r).'}';}if(!is_array($a))return$a;$r=[];foreach($a as$v)$r[]=out($v);return'['.join(',',$r).']';} function test($x,$e,$y){static $h='<table border=1><tr><th>input</th><th>output</th><th>expected</th><th>ok?</th></tr>';echo"$h<tr><td>",out($x),'</td><td>',out($y),'</td><td>',out($e),'</td><td>',$e==$y?'Y':'N',"</td></tr>";$h='';}
$cases=[ 1=>[ab,aabb,aaabbb,aaaabbbb,aaaaabbbbb,aaaaaabbbbbb], 0=>['',a,b,aa,ba,bb,aaa,aab,aba,abb,baa,bab,bba,bbb,aaaa,aaab,aaba, abaa,abab,abba,abbb,baaa,baab,baba,babb,bbaa,bbab,bbba,bbbb] ]; foreach($cases as$e=>$a)foreach($a as$x)test($x,$e,f($x)|0);  ## C, 65 bytes m,t;C(char*c){for(m=1,t=0;*c;)m>0&*c++-97&&(m-=2),t+=m;return!t;}  • m---- doesn't compile. – ugoren Jul 31 '16 at 14:33 • @ugoren what compiler are you using? – Stefano Sanfilippo Aug 8 '16 at 9:31 • gcc on Linux says error: invalid lvalue in decrement. Does any compiler compile this? – ugoren Aug 8 '16 at 14:56 • Whoopsie, correct, nice catch @ugoren, I must have missed the error among the warnings. Reverted to the 65 byte solution. – Stefano Sanfilippo Aug 10 '16 at 9:09 ## Brainfuck, 77 bytes , [ [ >+[>+<-]<< ,[>->+<<-] >[<<] > ] >+[<<] > [ >-[>+<-]<< , [ [>->+<<-] >[<<] < ] >> ] +>[<] ] <.  Expects input without a trailing newline. Outputs \x00 for false and \x01 for true. Try it online. The idea is to increment n for initial a characters and decrement n for subsequent b characters and then check whether n is zero at the end, short-circuiting to print false if the input does not match /^a+b+$/. Since the input is guaranteed to match /^[ab]*$/, we can ignore the fact that ord('a') = 97 and just use ord('b') = ord('a') + 1 to check for /^a+b/. # Cubically, 1099870 60 bytes +52/1+55~=7!6&(:7UR'UF'D2~=7)6>7?6&(:7D2FU'RU'~=7)6>7!6&!8%6  -11 bytes thanks to TehPers -10 bytes thanks to new language feature Try it online! Prints 1 if the string matches L, otherwise prints nothing. Explanation: +52/1+55 Sets the notepad to 97 ('a') ~ takes input =7!6& exits if input is not 'a' ( )6 While the notepad is truthy :7 Save the current character value UR'UF'D2 Perform a cube operation ~=7 Set notepad to true if next character is the same >7?6& Exit if next character is end of input (-1) ( )6 While the notepad is truthy :7 Save the current character D2FU'RU' Reverse one iteration of the previous operation ~=7 Set notepad to true if next character is the same >7!6& Exit if next character is NOT end of input !8%6 Print 1 if the cube is solved  The looping operation has been replaced with a new sequence with a period of 1260, which will still never give a false negative but now is guaranteed to work for inputs of less than 1260 characters. I've replaced the previous check for solved cube with !8%6. 8 is a recently added pseudo-face which is always equal to "Is the cube solved?" so I can just branch on that directly. • Why not do -6>7?6& instead of -6>7!6{...}? Also, couldn't you replace -6+111=3!6& with -6+21=3!6&, -6+1111=4!6& with -6+22=4!6&, and -6+11111=5!6& with -6+32=5!6&? You checked those faces earlier in the program. Also, you can replace -6=0!6& with -6>0?6& and remove the next -6 because 0 isn't greater than 0. Rather than =#!6&-6..., try =#0?6&.... I'm also pretty sure you can remove one or two of those checks, but I'm not a mathematician. I had a solution similar to this in mind, but I didn't think to compare the notepad to the previous value. Nice job! – TehPers Aug 7 '17 at 21:10 • Also, you can remove the -6 in -6=0!6& because to exit the loop before it, the notepad must be 0. – TehPers Aug 7 '17 at 21:15 # Brachylog (newer), 1110 6 bytes o?ḅĊlᵛ  Try it online! The predicate succeeds if the input is in L and fails otherwise.  The input o sorted ? is the input, ḅ and the runs in the input Ċ of which there are two lᵛ have the same length.  • At this rate you will catch up to my number of Brachylog answers in a few days! – Fatalize Mar 1 '19 at 12:17 Nearly 5 months later, decided to come back to my first answer here to see if I could golf it more. Here's the results! # Zsh, 28 bytes eval${${${1:?}//a/( }//b/)}


Just as I posted my 29 byte answer, I thought "what about if we handle the empty case specially?" Turns out, it's one byte shorter! Try it online!

Successful cases: ( ( ( ... ( ( ))...))).

Unsuccessful cases:

• Unmatched/uneven parens: obviously fails
• /ba/ case: ( )( ) is a "parse error" in the eval, but gives unknown file attribute: if run directly.
• Empty case: ${1:?} handles exactly this. # Zsh, 29 bytes eval $${{1//a/(+}//b/+i)}$$  Try it online! Same "eval matching delimiters" principle, but this time using math mode instead of parameter expansions: eval $${{1//a/(+}//b/+i)}$$  # literal ( )${1//a/(+}             # replace 'a' with '(+'
${ //b/+i)} # replace 'b' with '+i)'  A successful result looks like: ((+(+(...+(++i)+...i)+i)+i)), incrementing i to 1 and adding it repeatedly. This will result in a positive integer, which (( )) evaluates truthy. An unsuccessful result looks like one of the following: • Uneven/unmatched parens (obviously fails) • /^b(a{n}b{n})?a$/ case: (+i)(...)(+) fails with "missing identifier after +"
• Any other /ba/ case: (...)(...) errors in math mode
• Empty case: () is an anonymous function with no body, and errors

# Zsh, 35 bytes

eval '${'${${1//a/'${'}//b/'}'}'#}'


Inspired by xnor's post, this outputs via exit code. Zsh will happlily handle nested ${}s, but will err on ${${...}${...}} or unmatched braces. There are two caveats which makes this longer:

• We need the outer ${...}, since ${}${} is valid zsh. • We need a # at then end, which causes an error when the input is the empty string: • ${${}#} is prefix removal, which is fine. • ${#} evaluates to the number of parameters, which will be an integer and not a valid command.
• Welcome to PPCG! Nice first answer – Jo King Mar 21 '19 at 3:23

## Pyke, 10 bytes

le"ab"*Sq&


Try it here!

Or 9 bytes if null input isn't valid

le"ab"*Sq


# Java 7, 69 bytes

 boolean c(String i){return i.matches("(?x)(?:a(?=a*(\\1?+b)))+\\1");}


Regex shamelessly 'borrowed' from here.

Full code & test cases:

Try it here (with all test cases).

class Main{
static boolean c(String i){
return i.matches("(?x)(?:a(?=a*(\\1?+b)))+\\1");
}

public static void main(String[] a){
System.out.println(c("ab"));
System.out.println(c("aabb"));
System.out.println(c("aaabbb"));
System.out.println(c("aaaabbbb"));
System.out.println(c("aaaaabbbbb"));

System.out.println(c(""));
System.out.println(c("a"));
System.out.println(c("b"));
System.out.println(c("aa"));
System.out.println(c("ba"));
System.out.println(c("bb"));
System.out.println(c("aba"));
System.out.println(c("abab"));
System.out.println(c("abba"));
System.out.println(c("bbaa"));
System.out.println(c("bbbb"));
}
}


Output:

true
true
true
true
true
false
false
false
false
false
false
false
false
false
false
false


# Ruby, 33 bytes

Try it online

->s{s==?a*(l=s.size/2)+?b*l&&l>0}


## Actually, 14 bytes

"[]""ab"(t≡#ÆY


This uses the same strategy as xnor's solution for the first part: transform the input into a nested iterable.

Try it online!

Explanation:

"[]""ab"(t≡#ÆY
"[]""ab"(t      translate "a" -> "[", "b" -> "]"
≡     eval (since this is evaluating a literal, it still works in the online interpreter) - leaves a list if the string is valid, else a string
#    listify (does nothing to a list, makes a list of characters for a string)
Æ   filter strings (take all string elements in the list - so an empty list if there are none)
Y  boolean negate (an empty list is falsey and a non-empty list is truthy, so negation gets the correct value)


# Ruby, 31 bytes

Aw, that poor syntax highlighter :)

->s{s=~/^a+/&&$&.tr(?a,?b)==$'}


Does s begin with one or more a? Is also that bunch of as ($&) the same as the rest of the string ($') if we replace all those as with bs?

test here

bool f(string s)=>Regex.IsMatch(s,"^(?'o'a)+(?'-o'b)+(?(o)(?!))$");  This implementation uses .NET Regex's "Balancing Group Definitions" to match the same number of 'a' and 'b' characters while ensuring that the input isn't an empty string by using the + quantifier. # Python, 101 bytes def q(s): a=len(s)/2 for x in range(a): if s[x]!='a' or s[a+x]!='b' or a*2!=len(s):a=0 return a  Not the most efficient, had some trouble with 0 being even. Could probably get it lower with python tricks. Returns 0 if false, a positive integer if true. (which will be half len(s)) # k (21 bytes) Can probably be shorter {|/0,(=).(#:'=x)"ab"}  Example k){|/0,(=).(#:'=x)"ab"}"" 0b k){|/0,(=).(#:'=x)"ab"}"ab" 1b k){|/0,(=).(#:'=x)"ab"}"aab" 0b  • Gives the wrong answer for "abba" – geocar Jul 31 '16 at 13:02 # PHP bounty version, 31 bytes for PHP 4.1, call php-cgi -f <scriptname> s=<argument> (or in browser with ?s=<argument>) for current PHP, use $_GET[s] instead of $s 31 bytes <?eval(strtr("do$s;",ab,'{}'));


unexpected ';' for valid, unexpected end of file or unexpected '}' for invalid

<?eval(strtr("1|$s;",ab,'[]'));  ok for valid, unexpected ';' or unexpected ']' for invalid # 26 bytes if empty input was undefined or valid: <?eval(strtr($s,ab,'{}'));


29 bytes, if empty input was undefined or valid:

<?eval(strtr("$s;",ab,'[]'));  Abusing other control structures: 32 bytes <?eval(strtr("$c=$s;",ab,'[]'));  ok for valid, Parse error for invalid: unexpected ';', unexpected ']' or Cannot use [] for reading (for abab) 33 bytes <?eval(strtr("1 or$s;",ab,'[]'));


same as 1|

<?eval(strtr("if(0)$s",ab,'{}'));  ok for valid, unexpected end of file or unexpected '}' for invalid input 35 bytes: <?eval(strtr("for(;;)$s",ab,'{}'));


infinite loop for valid (use for(;0;) to make finite), same as if for invalid

36 bytes

<?eval(strtr("while(0)$s",ab,'{}'));  same as if 39 bytes <?eval(strtr("function()$s;",ab,'{}'));


unexpected ';' for empty, same as if for other input

s=input('');l=num2str(length(s)/2);regexp(s,['^a{',l,'}b{',l,'}$'])  The regular expression searches for exactly half the input's length in as consecutively at the beginning of the input string, followed by exactly half of the input's length in bs consecutively right up to the end of the input string. It returns [] on a non-even-length input, empty strings, and non-Language-L strings and only returns 1 on strings that are part of Language L. # TI-Basic, 35 bytes Zero is True; anything else is False. Input Str1 1+.5length(Str1 inString(Str1,"a",Ans) or Ans≠inString(Str1,"b  Explanation Input Str1 Get string into Str1 1+.5length(Str1 Get number that is one more than half. For example, 8 gives 5. inString(Str1,"a",Ans) Yields zero if there is no instances of a in the second half (using the number we just calculated as the start point for the search) or Both conditions need to be zero in order to output zero Ans≠inString(Str1,"b Yields zero if the first instance of b is the number we calculated earlier  • I think you might be confused... parentheses don't have to be closed in TI-Basic. Thanks for the concern, though. – Timtech Jul 28 '16 at 16:38 • You're not using TI-Basic's notion of truthy/falsy here. How is that allowed? – Jakob Aug 29 '17 at 19:24 • I answered this a while ago, and it seems that we could specify which outputs indicated true or false. But, in accordance with your suggestion you could add not( to the beginning of the last line. – Timtech Aug 29 '17 at 23:08 # Ruby, 42 bytes This is a simple solution, but unfortunately longer than many of the other Ruby solutions. ->s{s=~/^a+b+$/&&s.count(?a)==s.count(?b)}


Ungolfed:

def f(s)
if s =~ /^a+b+$/ && s.count(?a) == s.count(?b) return true else return nil end end  # ><>, 52 bytes Prints zero if input is false, n otherwise (the number of a's and b's). 0&i:0(?v10. &v?(2l~<0rv!?='a'r0!?='b'&+1 >l0=&*n;n<  Try it online! ## K, 21 Bytes 0b is false and 1b is true;  f:{(~).{+/x=y}[x]'"ab"} tests:("ab";"abbbb";"bbbbaa";"aabbababba";"bb";"aabaaabaaa";"aabbb";"aaabbaaabb";"ba";"bbbabbaabb") f'tests 1001000010b  • Edit - from seeing another answer I've shortened it down to 17 Bytes; {(~).(#:'=x)"ab"} – Chromozorz Jul 30 '16 at 22:44 # Pyth, 7 bytes .AanV_S  Try it online ### How it works  SQ sorted input _ reverse nV Q vectorized not-equal with input a Q append input .A test whether all elements are truthy  # K, 10 bytes ~/1_'-':'=  Note this is a function, so it needs to be called:  ~/1_'-':'="aaaaaabbbbbb" 1 ~/1_'-':'="aba" 0  = groups its arguments, so ="aaaaaabbbbbb" produces "ab"!(0 1 2 3 4 5;6 7 8 9 10 11) and ="aba" returns "ab"!(0 2;,1) -':' is minus eachprior each. -': is a good way to find out if a series is increasing (or decreasing). -':'="aaaaaabbbbbb" gives us "ab"!(0 1 1 1 1 1;6 1 1 1 1 1) and -':'="aba" gives us "ab"!(0 2;,1) 1_' is one drop each which pops the first element off each list. ~/ is match over. # Perl 6, 49 bytes grammar {token TOP{a<TOP>?b}}.parse(get).Bool.say  My entry for dmckee’s bounty. Checks the string using Perl 6’s parsing facilities. # Bash, 50 bytes l=$[${#1}/2] [[$1 =~ a{$l}b{$l}$]]&&echo$[$l>0]  It always returns 1 in case the statement is true, otherwise it doesn't return something except if the input is the empty string '', then it returns 0. Example: $ bash script 'aa'
$bash script 'ab' 1$ bash script 'aabb'
1
$bash script '' 0  It can be a bit shorter (46 bytes) if it returns$l in case of success.

l=$[${#1}/2]
[[ $1 =~ a{$l}b{$l}$ ]]&&echo $l  In case of success it always returns a value > 0, if the input is the empty string it returns 0 and otherwise it doesn't return anything. Examples: $ bash t.sh 'aa'
$bash t.sh 'ab' 1$ bash t.sh 'aabb'
2
\$ bash t.sh ''
0