(First challenge, please let me know if there are any problems with it.)

A heterogram is a word where no letter of the alphabet occurs more than once, and a palindrome is a phrase that is the same backwards and forwards.

The challenge here is to write a piece of code that takes in a word (just letters) as input, and outputs whether or not it is a heterogram (truthy/falsy). The catch is that the program must be a palindrome -- reads the same backwards and forwards. Capitalization doesn't matter here, so for the heterogram to be valid it can't have both q and Q, for instance. No comments are allowed, and you cannot place strings that contain your code (or a significant part of your code) to try to make the palindrome part easy :P

This is code-golf, so shortest code wins. Good luck!

EDIT: Parens, brackets or other symbols that have left and right forms must be reversed approriately for the palindrome part. So (helloolleh) is a palindrome, but (helloolleh( is not. Apparently this is called a convenient palindrome.

EDIT 2: You won't get any empty input, input with multiple words or input with characters other than letters. So don't worry about that :)

  • \$\begingroup\$ Are brackets considered to be equivalent for palindromicity? That is, is (hellolleh) a valid palindrome? Similar for [], {}, and <> (where appropriate). \$\endgroup\$
    – Glen O
    Commented Sep 24, 2015 at 15:11
  • \$\begingroup\$ Yes. Sorry, I should have explained that. \$\endgroup\$ Commented Sep 24, 2015 at 15:21
  • \$\begingroup\$ What about new lines, will asdsa be considered equal to asd\nsa? \$\endgroup\$ Commented Sep 24, 2015 at 15:42
  • \$\begingroup\$ No, they would not be equivalent. \$\endgroup\$ Commented Sep 24, 2015 at 16:01
  • 5
    \$\begingroup\$ I believe it's called a convenient palindrome. \$\endgroup\$
    – lirtosiast
    Commented Sep 24, 2015 at 22:36

10 Answers 10


Pyth, 17 bytes


Try it online here.

The leading space is necessary. I have counted it and the trailing space in the byte count.

Here's the breakdown:

     z            z is initialized to the input
    r Z           Z is initialized to 0, and r(string)0 converts the string to lowercase
  .{              .{ is pyth's builtin uniqueness test
       .q         .q terminates the program
         .Zzr}    This is just the program mirrored
              .Z  . requires a number to immediately follow it
                  If left blank the parser would throw an error
 Z                This is just mirrored from the end
                  The leading space suppresses the automatic printing of this 0
                  The trailing space mirrors the leading space
  • 3
    \$\begingroup\$ Jeez, and I was worried that this would be too hard... Nice job! \$\endgroup\$ Commented Sep 24, 2015 at 16:15
  • 2
    \$\begingroup\$ You have a .q in your comments, but a .w in your program. \$\endgroup\$ Commented Sep 25, 2015 at 7:31
  • \$\begingroup\$ @JamesWebster Thanks for pointing that out. It should be .q \$\endgroup\$ Commented Sep 25, 2015 at 17:59

Python 3, 125

The main problem is to make the reverse of the code parsable. Then we can let it error out from undefined identifiers.

  • \$\begingroup\$ Very minor point, but from the rules you should change the second < into a >! \$\endgroup\$
    – Jarmex
    Commented Sep 24, 2015 at 22:25

Perl, 43 bytes


Usage example:

echo "abcde" | perl -n entry.pl
  • \$\begingroup\$ Wow, that's amazing. Nice job! \$\endgroup\$ Commented Sep 24, 2015 at 22:18

Pyth - 11 bytes

(Trailing and leading spaces necessary and counted).

 z.{ z }.z 

Test Suite.

<space>        Suppress print
 z             Input (for palindromness)
.{             Unique - actually does testing
  z            Input
<space>        Suppress print
  }            In operator
   .z          Cached input list
   <space>     At end of program makes empty tuple
  • \$\begingroup\$ Mike's Pyth answer converts to lowercase before doing .{, are you sure you don't need to do that too? \$\endgroup\$
    – Sparr
    Commented Dec 6, 2016 at 2:54

><>, 137 131 Bytes

When I saw this challenge, I thought ><> might finally be a good choice of language since using it you can mostly ignore palindromes; it is simple to make sure the pointer only stays where it should. While this is true, ><> unfortunately makes golfing conditionals excruciating (or just golfing in general). I hope to use some weird tricks I thought of to compensate for this, but here's a "quick" (not actually, both program-wise and creation-wise) answer. You can try it online here.

  ;n1<^  -*48<   .00~<  ;n-10<01-n;  >~00.   >84*-  ^>1n;

Returns 1 for true and -1 for false (I could change it to 0 but the length would stay the same, unfortunately)

As always, let me know if this doesn't work and if you have any ideas on how to golf it down. I tested it against a few test cases, but there always could be an exception.

Here's another version, one that I think is a bit more clever, but alas is ten bytes more. Truthy/falsey values this time are 1 and an error (something smells fishy...):

>i:0(?v>:"Z")?vl: 2(?v&{:@$:@=01-*2.
 < ;n1<^  -*48<f6+0.0<
>0.0+6f>84*-  ^>1n; > 
.2*-10=@:$@:}&v?)2 :lv?("Z":<v?)0:i<


Here's the code without the part added to make it a palindrome. This one doesn't use the "more clever" tricks I attempted to use for the alternate version, so it's a bit easier to explain (if anyone is interested in an explanation for the "tricks," I'd be happy to give one, though).

  ;n1<^  -*48<   .00~<  ;n-10<

Line 1:

i:0(?v                                 #Pushes input and directs down if negative
      >:"Z")?v                         #Directs down if input is greater than "Z"
                                       #(reduces lowercase input to uppercase)
              l                        #Pushes length

                                       #Main loop begins
               1-:1(?v                 #Decrements top, and then directs down if less than 1
                      &                #Pushes top of stack onto register (length minus 1)
                       :{              #Duplicates top, shifts stack to the left
                         :@            #Duplicates top, shifts top three values of the stack to the right

                           =?v         #If top two values are equal, directs down
                              $        #Swaps top two values of the stack
                               &       #Pushes register onto stack
                                e0.    #Jumps back to the "1" after "?vl"
                                       #Main loop ends

                                   >   #Makes sure when the pointer jumps back to i it goes the right way

Here's how the convoluted swapping (:{:@=?v$) works -- I'll use a test case of this stack: [5,1,8,1] where the last character is the top.

:{ The top of the stack is duplicated: [5,1,8,1,1], and the stack shifted to the left: [1,8,1,1,5]

:@ The top is duplicated: [1,8,1,1,5,5], then the top three values are shifted to the right: [1,8,1,5,1,5]

=?v Unnecessary for this part of explanation

$ The top value is swapped once more yielding [1,8,1,5], which, if you'll note, is the original stack shifted over once (as if { had been the only command).

So what this does in English ("Thank God, he's actually explaining things") is check the entire stack against the top value and move to a point in the second line if any value equals the top. This checking is done proportionate to how many values there are in the stack (l - 1, where l is the length of the stack) so that all values are checked against each other.

Line 2:

  ;n1<^  -*48<   .00~<  ;n-10<
   n1<                          #If input is less than 0 (i.e. there is none), print 1
  ;                             #and terminate

             <                  #If redirected because input is greater than "Z"
         -*48                   #Push 32, subtract (reducing lowercase to uppercase, numerically)
      ^                         #And move back to the portion that tests if input 
                                #is uppercase (which it will pass now)

                     <          #If counter is less than 1 (for main loop)
                 .00~           #Pop the counter and jump to the beginning (i)

                             <  #If any two values in the stack are equal
                          -10   #Push -1 (subtract 1 from 0)
                        ;n      #Print and terminate
  • \$\begingroup\$ Glad to see a ><> answer :) \$\endgroup\$ Commented Sep 25, 2015 at 13:42
  • 1
    \$\begingroup\$ Also, ><> is a palindrome itself (just not a convenient one) \$\endgroup\$
    – Jo King
    Commented Mar 2, 2019 at 11:31

PHP, 126 Bytes

You need to run this with the short_tags ini directive turned off in 5.4 or above.

First golf ever. Two copies, the first prints a whole bunch of garbage with the falsy/truthy result:


This version will not print any jargon (162 bytes):


Run from the command line with

php -f golf.php heterogram

Probably can be golfed a bit further

  • \$\begingroup\$ Clever, though pushing limits of validness. Maybe for the sport think of a solution with comments \$\endgroup\$
    – Martijn
    Commented Sep 25, 2015 at 11:24
  • \$\begingroup\$ Instead of ?><?, you can use //\\. That should remove that requirement. And instead of __halt_compiler() use return; \$\endgroup\$ Commented Sep 25, 2015 at 11:24
  • \$\begingroup\$ Disregard it. You can't use comments. But the return; is still valid. \$\endgroup\$ Commented Sep 25, 2015 at 11:47

05AB1E, 9 bytes


Try it online.

*insert something about going all the way back to my very first challenge*

Non-competing since 05AB1E was made after this challenge.


l           Take input and lowercase it.
 DÙ         Duplicate and uniquify.
   Q        Compare the two strings.
    q       Immediately exit.
     QÙDl   The rest of the program is ignored.
  • 1
    \$\begingroup\$ "Non-competing since 05AB1E was made before this challenge." Perhaps you meant after this challenge? ;) \$\endgroup\$ Commented Nov 3, 2016 at 15:04

Brachylog, 3 bytes, language postdates challenge


Try it online!

This is one of the very few programs that works in both Brachylog 1 and Brachylog 2. The TIO link is to Brachylog 1 for old times' sake. Also unsually for Brachylog, this is a full program, not a function. (Full programs in Brachylog implicitly output booleans, which is just what we want for this question.)

The general principle here is that placing a predicate between a pair of identical uppercase letters is an assertion that the current value is invariant under that predicate. So you often see things like AoA for "is sorted" ("invariant under sorting"); A↔A would (in Brachylog 2) mean "is a palindrome" ("invariant under reversal"), and so on. This program is "invariant under removing duplicates", i.e. "doesn't contain duplicates". It's really convenient that this method of specifying invariance happens to be a palindrome.


Brachylog, 3 bytes


Try it online!

The predicate succeeds if the input is a heterogram and fails if it is not.

≠      The characters of the input are all distinct,
 ụ     and when converted to upper case,
  ≠    are still all distinct.

MATL, 7 bytes


Try it online!

Returns list [1, 1] if the input is a heterogram and [0, 0] if not.


t       % duplicate the input
u       % remove duplicates from the original
X=      % check the two lists are equal
Xu      % unique rows (does nothing as we now have a boolean)
t       % duplicate the result
        % (implicit) convert to string and display

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.