55
\$\begingroup\$

In an earlier challenge I asked code golfers to produce strings which copy each character in a string. For example:

TThhiiss  iiss  ddoouubbllee  ssppeeaakk!!

This challenge is simply to detect if some text meets the definition of a double speak string.

  • There is an even number of characters.
  • When split into pairs, every pair consists of two of the same character.

The challenge

  • It's code golf, do it in few bytes.
  • Use any language you choose.
  • Please include a link to an online interpreter.
  • The code will accept some text.
  • For simplicity, the input will only consist of printable ASCII characters
  • It will return an indication of whether or not the input is double speak. It could be:
  • A boolean
  • Strings ('true', 'false', 'yes', 'no' etc)
  • Integers 0 or 1

Test Cases:

input -> output
aba -> false
aab -> false
abba -> false
aabb -> true
aaabb -> false
tthhiiss -> true
ttthhhiiisss -> false
\$\endgroup\$
12
  • 9
    \$\begingroup\$ May we error on inputs of length < 2? \$\endgroup\$
    – cole
    Aug 6, 2019 at 16:06
  • 4
    \$\begingroup\$ Suggested test case: abba which should be falsey \$\endgroup\$
    – Giuseppe
    Aug 6, 2019 at 16:29
  • 3
    \$\begingroup\$ Suggested test case: aabbbb which should be truthy \$\endgroup\$ Aug 6, 2019 at 17:30
  • 2
    \$\begingroup\$ @val Well, I'm not going to argue with standard I/O \$\endgroup\$
    – AJFaraday
    Aug 7, 2019 at 8:27
  • 4
    \$\begingroup\$ What about the empty string? \$\endgroup\$
    – PieCot
    Aug 7, 2019 at 21:04

102 Answers 102

2
\$\begingroup\$

C (gcc), 50 bytes

Returns 0 if double-speak (or empty string), truthy otherwise.

f(s,i)char*s;{for(i=*s;i&(i=*s++)&&i==*s++;);s=i;}

Try it online!

If the input string is assumed to always be at least 2 characters (gives incorrect result for single-character strings):

C (gcc), 38 bytes

f(char*s){while(*s&&*s++==*s++);s=*s;}

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ The second suggestion seems to treat single character strings the same as any other odd length strings? \$\endgroup\$
    – Taemyr
    Aug 8, 2019 at 7:30
  • \$\begingroup\$ Wouldn't putting s=i at the end of for braces (for(i=*s;i&(i=*s++)&&i==*s++;s=i) save you one ;? \$\endgroup\$ Aug 8, 2019 at 17:47
  • \$\begingroup\$ @PrzemysławCzechowski s=i is used to return the value: it's not part of the loop. I'm using i to capture the character value, so assigning it to s during the loop would mess up the pointer (and likely crash the program!) \$\endgroup\$
    – ErikF
    Aug 8, 2019 at 18:04
  • \$\begingroup\$ @ErikF my bad, I thought s=i is repeated in every iteration. \$\endgroup\$ Aug 8, 2019 at 18:23
2
\$\begingroup\$

Pip, 31 bytes

a:qFb,#a{b%2?x(ab+1)Qa@b?xi:1}i

Try it online!

slightly different approach with fold operator, 31 bytes

a:qFb,#a{I!b%2i:$Q[a@b(ab+1)]}i

outputs 0 if double speak, 1 otherwise

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

Perl 6, 14 bytes

{!S:g/(.)$0//}

Try it online!

Replaces all pairs of identical characters with nothing and then boolean NOTs the result to return true if is an empty string (i.e. all characters were next to an identical one), or false otherwise.

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

PHP, 51 50 38 bytes

echo preg_match("/^((.)\2)+$/",$argn);

Checks whether input matches pairs of chars where the chars in each pair are the same (using back ref).

Run like this:

echo 'dd00uubbllee' | php -nR 'echo preg_match("/^((.)\\2)+$/",$argn);';echo
  • -1 byte thanks to @manatwork
  • -12 bytes by using preg_match with back references.
\$\endgroup\$
2
  • \$\begingroup\$ Wouldn't /.(.?)/ be enough to handle odd length strings? \$\endgroup\$
    – manatwork
    Aug 8, 2019 at 9:29
  • \$\begingroup\$ @manatwork you're right, thanks \$\endgroup\$
    – aross
    Aug 8, 2019 at 9:32
2
\$\begingroup\$

PHP, 37 40 bytes

+3 bytes to fix '0' issue

<?=''==preg_replace('/(.)\1/','',$argn);

Try it online!

Similar to other RegEx answers.


PHP, 55 bytes

for(;($l=$argn[$i++])==$argn[$i++]&&$l.$l;);echo$l=='';

Try it online!

Loops to the end of the string as long as every even character (0th, 2nd, 4th, etc ...) is equal to the character after it, else stops the loop. Finally, checks if it has arrived at the end of the string, which means the string is a double speak.


Outputs 1 for double speak and nothing for not double speak.

\$\endgroup\$
2
  • \$\begingroup\$ Your first one is close, but it negates the resulting string. "0" is falsey in PHP, so an input of "000" will be deemed doublespeak \$\endgroup\$
    – aross
    Aug 8, 2019 at 9:59
  • \$\begingroup\$ @aross: Thanks for pointing it out, yeah that '0' == false is always an issue. Fixed! \$\endgroup\$
    – Night2
    Aug 8, 2019 at 11:38
2
\$\begingroup\$

Java (JDK), 64 bytes

s->{int i=s.length;for(;i>1;)i=s[--i]==s[--i]?i:i|1;return i<1;}

Try it online!

Explanations

s->{
 int i=s.length;
 for(;i>1;)                 // while the length is 2 or greater.
  i=s[--i]==s[--i]?i:i|1;   // check the 2 previous values. If they're identical, don't do anything. Else, make i odd so that it fails at the return.
 return i<1;                // i will be 0 only when the size is even and all characters were doubled.
}
\$\endgroup\$
2
\$\begingroup\$

Stax, 6 5 bytes

╩3╦x╨

Run and debug it at staxlang.xyz!

Outputs integers. Zero for double speak and nonzero (one now!) otherwise, as allowed here. Replace 1I below with |e if you want the opposite behavior, but I think this version looks nicer.

Unpacked (6 bytes) and explanation

:G|g1I
:G        Array of run lengths
  |g      GCD
    1I    Is odd? Could use 2% instead

The GCD of run lengths will be even exactly when all run lengths are.

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

Pushy, 13 bytes

LL2%}2/:=};P#

Try it online!

L              \ Push len(input)
 L2%           \ Take (len(input) + 1) % 2. This will be 1 only if the input had even length.
    }          \ Move this to the bottom of the stack for later.
     2/:  ;    \ Length of original input, divided by 2, times do:
        =      \    Pop top two characters, and check equality. (Pushes 0 or 1)
         }     \    Move this to the bottom of the stack for later.
           P   \ Push the stack's product. If the input had odd length, or any two characters
               \ did not match, then the stack will contain a 0, so the product will be 0.
               \ Else, it will be 1.
            #  \ Print the product.  
\$\endgroup\$
2
\$\begingroup\$

naz, 50 bytes

2a2x1v1x1f1r3x1v2e2x2v1r3x2v1e0m1o0x1x2f0m1a1o0x1f

Works for any input string, provided it's passed as a file terminated with the control character STX (U+0002).

Explanation (with 0x commands removed)

2a2x1v                       # Set variable 1 equal to 2
1x1f1r3x1v2e                 # Function 1
                             # Read a byte and jump to function 2 if it equals variable 1
            2x2v             # Otherwise, store it in variable 2
                1r3x2v1e     # Read another byte
                             # Jump back to the start of function 1 if it equals variable 2
                        0m1o # Otherwise, output 0
1x2f0m1a1o                   # Function 2
                             # Output 1
1f                           # Call function 1
\$\endgroup\$
2
\$\begingroup\$

Burlesque, 8 bytes

J2en)J==

Try it online!

                           [AABBCC]         [ABC]
J   #Duplicate             [AABBCC,AABBCC]  [ABC,ABC]
2en #Every other character [AABBCC,ABC]     [ABC,AC]
)J  #Duplicated            [AABBCC,AABBCC]  [ABC,AACC]
==  #Is the same           [1]              [0]

Burlesque, 11 bytes

=[{L[2dv}al

Try it online!

=[   # Group consecutive like values
{
 L[  # Length of (group)
 2dv # Divisible by 2
}al  # All
\$\endgroup\$
2
\$\begingroup\$

Pip, 5 bytes

$QUWa

The existing solution used a longer iterative comparison. Thankfully, the unweave function exists.

Try it online!

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

Marbelous, 84 bytes

No output for false, output one \u0000 for true using this standard I/O rule

@0@1
0000
]]]]&101
eqal\\&1
@0@1..=0\/
:eqal
@0..@1
}0..}1
--..--
<1&0<1&0
{0@0{1@1

interpretor

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

JavaScript (Node.js), 23 bytes

z=>/^((.)\2)+$/.test(z)

Uses a regular expression that match two of the same characters one or more times

Try it online!

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

Risky, 5 bytes

0?/20_?:?

Try it online!

Doesn't work with the empty string; OP hasn't clarified if that's a problem.

Explanation

0   transpose
?       input
/     split into groups of
2       2
0 reduce
_
?       accumulator
:     =
?       item
\$\endgroup\$
2
\$\begingroup\$

PHP4 (45 chars)

Given $argv[1] as a command line argument :

echo preg_match('#^((.)\2)*$#',$argv[1])?1:0;

The script will output 0 or 1 to answer.

PHP7.4 (41 chars)

The $f arrow function will return the answer :

$f=fn($s)=>preg_match('#^((.)\2)*$#',$s);

C gcc (27 chars)

Function f today return an indication of whether or not the input is double speak :

f(char*s){1[s]-*s||f(s+2);}

Usage :

#include <stdio.h>

int main() {
    printf("EXPECT %5s ... %-5s .. aba\n","NO", f("aba")?"NO":"YES");
    printf("EXPECT %5s ... %-5s .. abba\n","NO", f("abba")?"NO":"YES");
    printf("EXPECT %5s ... %-5s .. aabb\n","YES", f("aabb")?"NO":"YES");
    printf("EXPECT %5s ... %-5s .. aaabb\n","NO", f("aaabb")?"NO":"YES");
    printf("EXPECT %5s ... %-5s .. tthhiiss\n","YES", f("tthhiiss")?"NO":"YES");
    printf("EXPECT %5s ... %-5s .. ttthhhiiisss\n","NO", f("ttthhhiiisss")?"NO":"YES");
}

Try it Online

C Variants :

g(char*s){s=*s&&g(s+2)+s[1]-*s;} // 32 chars
h(char*s){return*s&&h(s+2)+s[1]-*s;} // 36 chars
\$\endgroup\$
2
\$\begingroup\$

><> (Fish), 22 bytes

l2%?v>l?v1n;
 ;n0<^?=<

Try it

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

X86_64/Linux Machine Code, 19-Bytes

Try it online!

Usage:

$> gcc -Wl,-Tbss=0x80000000 -DIDEAL_BSS -s -static -nostartfiles -nodefaultlibs -nostdlib -Wl,--build-id=none double-speak.S -o double-speak
$> echo -n "<input> | ./double-speak; echo $?

Program reads input from stdin (it is sensitive to the exact byte count of input, i.e stray spaces/new lines will change result).

Returns 0 for double-speak, and non-zero otherwise.

Notes:

  • Size is measured in machine code (i.e 19 bytes of PROGBITS; .text, .data, etc...)
  • The 19-byte size assumes the above compile command which sets the BSS address at an ideal value. With generic compile commands (no linker inputs), its 21-bytes.

Program:

/* Uncomment below if BSS was setup at 2^20.  */
/* #define IDEAL_BSS */
    .global _start
    .text
_start:
    /* Incoming registers are zero.  */

    decl    %edx
#ifdef IDEAL_BSS
    btsl    %edx, %esi
#else
    movl    $G_mem, %esi
#endif
    /* eax == 0 == SYS_read.  */
    /* edi == 0 == STDIN.  */
    syscall
    xchgl   %eax, %ecx
loop:
    /* Load next pair.  */
    lodsw
    /* Need to decr by 2 each loop.  */
    decl    %ecx
    /* Check that bytes match.  */
    xorb    %ah, %al
    /* Break if ne or end of count.  */
    loope   loop

    /* edi is zero (clears hi bits of eax). eax is zero if double-
       speak. Otherwise non-zero.  */
    xchgl   %eax, %edi
    /* Exit.  */
    movb    $60, %al
    syscall
    .section .bss
G_mem:  .space(4096 * 8)

Example/Test:

$> for x in aba abba aabb aaabb tthhiiss ttthhhiiisss; do echo -n "${x}" | ./double-speak; echo "${x} -> $?"; done
aba -> 3
abba -> 3
aabb -> 0
aaabb -> 3
tthhiiss -> 0
ttthhhiiisss -> 28
\$\endgroup\$
2
\$\begingroup\$

TypeScript's Type System, 86 bytes

type F<S>=S extends`${infer C}${infer D}${infer R}`?C extends D?F<R>:0:S extends""?1:0

Try it at the TypeScript playground!

This is a generic type F taking a string-literal type parameter S, and outputting 0 for non-double-speak and 1 for double-speak.

None of the test cases offer a case where all of the pairs are equal but the whole string is of odd length, which tripped me up a bit. This is likely golfable, maybe that last extends clause can be dropped with something clever.

Explanation:

type F<S> = 
  // Match S to extract the first two chars C and D and the rest R
  S extends `${infer C}${infer D}${infer R}`
  // if it matched: is C the same type as D?
  ? C extends D
    ? F<R> // if so, recurse with R
    : 0 // otherwise, it's not double-speak; return 0
  // if it didn't match, we check if the string is empty or has one character.
  : S extends "" ? 1 : 0
\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node.js), 26 bytes

f=([a,b,...c])=>a==b&&f(c)

Attempt This Online!

Outputs via exit-code. I think this is really nice and clean but that's just my opinion. Unfortunately, this doesn't beat the tried-and-true regex.

Explanation:

  • f=([a,b,...c])=>...

    Makes a function f that destructures its string argument into the first character a, the second character b, and rest of the string c.

  • a==b&&f(c)

    Check if a is equal to b. The && (AND) operator only evaluates its right argument if its left is true, so if it is true we recurse with c.

    If a and b aren't equal, then it can't be double speak, so we stop recursion to output with a 0 exit code.

    If they're equal, we keep checking the next two until either

    • A) recursion stops because a isn't equal to b, or
    • B) a and b are both undefined because we've reached the end of the string and there are no more characters left, so we recurse until the recursion depth is hit, giving an exit code of 1.
\$\endgroup\$
1
\$\begingroup\$

Gema, 17 characters

?$1=
?=0@end
\Z=1

Outputs 1 on double speak and 0 on not.

Sample run:

bash-5.0$ echo -n 'TThhiiss  iiss  ddoouubbllee  ssppeeaakk!!' | gema '?$1=;?=0@end;\Z=1'
1

Try it online!

Gema, 12 characters

?$1=
?=@fail

Terminates with exit code 0 on double speak and 2 on not.

Sample run:

bash-5.0$ echo -n 'TThhiiss  iiss  ddoouubbllee  ssppeeaakk!!' | gema '?$1=;?=@fail'
bash-5.0$ echo $?
0

Try it online!

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

Kotlin, 59 57 bytes

{s->(0..s.length-1 step 2).fold(""){t,c->t+s[c]+s[c]}==s}

Try it online!

-1 byte thanks to Khuldraeseth na'Barya

-1 byte changing it -> s


Slightly lazy attempt - basically just took my answer from the doublespeak question, applied it to every second char of the input and see if the result is the input.

\$\endgroup\$
2
  • \$\begingroup\$ Save a byte \$\endgroup\$ Aug 6, 2019 at 19:25
  • \$\begingroup\$ @Khuldraesethna'Barya updated, thanks! did not know that was a thing \$\endgroup\$
    – Quinn
    Aug 6, 2019 at 19:32
1
\$\begingroup\$

Kotlin, 4649 bytes

-3 bytes thanks to @cubic lettuce

{it.chunked(2).all{it.length>1&&it[0]==it[1]}}

Try it online!

{it.length%2==0&&it.chunked(2).all{it[0]==it[1]}}

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Save 3 bytes by {it.chunked(2).all{it.length>1&&it[0]==it[1]}} ;) \$\endgroup\$ Aug 7, 2019 at 20:41
1
\$\begingroup\$

C (tcc), 59 55 50 bytes

f(char *s){while(*s==*++s)s++;return!*--s&&s!="";}

Try it online!

Thanks to Jo King for the 4 bytes and Unrelated String for saving 5 bytes

Can it be even shorter?

\$\endgroup\$
8
  • \$\begingroup\$ *s==*++s, how is this not UB when *s points to the end of the string? \$\endgroup\$
    – Taemyr
    Aug 7, 2019 at 11:26
  • \$\begingroup\$ the *s will always point to the last character of the string given in argument which is not last character because string here ends with '\0' so the *++s gets the \0 \$\endgroup\$
    – Loki
    Aug 7, 2019 at 11:36
  • 1
    \$\begingroup\$ You increment s by two each time through the loop, so if there is an even number of actuall characters in the string I do not see how you can guarantee that *s never points to the '\0' \$\endgroup\$
    – Taemyr
    Aug 7, 2019 at 11:58
  • \$\begingroup\$ You could golf it considerably by replacing *--s=='\0' with the simple !*--s, but also note that most challenges require submissions to be either functions or full programs, and this is neither. \$\endgroup\$ Aug 7, 2019 at 19:37
  • \$\begingroup\$ Thanks guys for helping shortening it and @Taemyr play with this code TIO I don't need to care if *s gets the \0 character cause then the *++s will get the first character from the string used in printf. \$\endgroup\$
    – Loki
    Aug 8, 2019 at 5:07
1
\$\begingroup\$

Rust, 73 bytes

|s:&str|!s.chars().enumerate().any(|(i,c)|s.chars().nth(i|1).unwrap()!=c)

Try it online!

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

T-SQL 2012, 76 bytes

Looping using the following logic.

Finding the first match on the first character in the rest of the string.

If the match is on the second position, the first 2 characters are removed.

if the match is on a later position, character 0 to 1 is removed, resulting in null string.

If there are no matches, characters 3-4 are removed. Repeating this will eventually result in a null string.

The final string will be an empty string or a null string.

Empty string is "double speak"(1). Null string is not "double speak"(0).

DECLARE @ varchar(max)='ccbbddeeff'

WHILE @>''SET
@=stuff(@,-3/~charindex(left(@,1),@,2),2,'')PRINT
iif(@=0,1,0)

Try it online

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

JavaScript (Node.js), 26 bytes

s=>!s.replace(/(.)\1/g,'')

Try it online!

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

C++ (gcc), 48 47 45 43 bytes

-2 bytes thanks to @ceilingcat
-2 bytes thanks to @JoKing

recursive function assuming zero terminated char table

int f(char*s){return!*s||*s==s[1]&&f(s+2);}

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ @ceilingcat cool, I didn't now you can be that concise \$\endgroup\$ Aug 9, 2019 at 8:02
  • \$\begingroup\$ @JoKing it works, there's just a warning because I'm using string literals to generate right kind of input - zero-terminated char* - which is not exactly legal in c++ but gcc allows it \$\endgroup\$ Aug 9, 2019 at 8:26
  • \$\begingroup\$ Ah sorry, I thought the lack of output meant it wasn't working. 43 bytes \$\endgroup\$
    – Jo King
    Aug 9, 2019 at 8:36
1
\$\begingroup\$

EDIT:

This solution is totally broken as demonstrated by the aabbbb test case. Thanks to @manatwork for pointing this out. Do not pass Go! Do not collect 200 dollars!

Bash and Gnu utils, 46 45 bytes

fold -1|uniq -c|grep -qv "2 "&&echo 0||echo 1

Try it online!

-1 byte by skipping the first space in grep expression

\$\endgroup\$
3
  • \$\begingroup\$ Not sure if I can use grep exit codes and return output there? Instead of this &&echo 0||echo1 \$\endgroup\$ Aug 9, 2019 at 9:32
  • 1
    \$\begingroup\$ This works fine on the question's test cases, but as I understand the challenge, it should output 1 on Khuldraeseth na'Barya's suggested test case, “aabbbb”. \$\endgroup\$
    – manatwork
    Aug 9, 2019 at 9:34
  • \$\begingroup\$ @manatwork You're right. This solution is buggy in that sense. \$\endgroup\$ Aug 9, 2019 at 11:23
1
\$\begingroup\$

Lua, 90 87 86 82 bytes

a,e=1,os.exit;(...):gsub('.',load's=...a=a~=1 and(a==s and 1 or e(1))or s')e(a==1)

Try it online!

Take input as argument, use exit code as output.

Explanation

This is based on abusing few Lua features:

  • os.exit could accept either number, allowing short code for exit or boolean, making last check possible (true meaning success, false meaning failure).
  • gsub can be also used to iterate over string in callback-style, not only for doing replacement.
  • load is shorter than function(...) end.
  • ... is often shorter than arg[1].

Whole idea hidden behind those tricks:

  1. For each character: either remember if nothing is already it or perform check: if current is different from remembered, fail. Otherwise, reset remembered value and continue.
  2. When done, make sure that there's nothing pending in the buffer (e(a==1)). This is required to fail on strings with odd length.
\$\endgroup\$
1
\$\begingroup\$

C++ (gcc), 156 145 bytes

#include <iostream>
int f(){
    char a[2]; while(std::cin.get(a, 3)) if(a[0] != a[1]) return 0; return 1;
}
int main()
{
    std::cout << f();
}

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to the site! C++ is pretty lenient about whitespace so you can save bytes by removing a bunch of it. \$\endgroup\$
    – Wheat Wizard
    Aug 11, 2019 at 11:58
  • \$\begingroup\$ @SriotchilismO'Zaic Thank you! \$\endgroup\$
    – Ver Nick
    Aug 11, 2019 at 17:12

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.