Disclaimer: This isn't my challenge but ThisGuy said I was OK to post.

Occasionally I want to make a word into it's opposite, like happiness goes to unhappiness. Unfortunately when this happens, my brain will sometimes go blank. Then one day, after yet another this happening, I thought to my self "This is what programs are for!"

As the English language has many exceptions, I have created a list which contains the prefix for the starting letter

q or h          -> dis- (honest -> dishonest)
l                -> il-  (legal -> illegal)
m or p           -> im-  (mature -> immature)
r                -> ir-  (responsible -> irresponsible)
everything else  -> un-  (worthy -> unworthy)


Given an input as a string, make the string into its negative and output the result. You can assume that all the inputs given will fit the above rules. Submissions may be programs or functions, not snippets.


A single string, taken either as a parameter or from STDIN


The negated form of that string, conforming to the above rules

How to Win

This is a so shortest code wins

  • 4
    \$\begingroup\$ Can we assume we'll never get a word that starts with a q without a u? \$\endgroup\$ – Business Cat Apr 5 '17 at 20:06
  • 3
    \$\begingroup\$ Off the top of my head, qadi, qat, the aforementioned qi, qirsh, and qwerty. (I play a lot of Scrabble) \$\endgroup\$ – AdmBorkBork Apr 5 '17 at 20:23
  • 4
    \$\begingroup\$ @wsbltc Well, there's a fair few, but they're pretty much all borrowed words from other languages so questionable whether they really count as English. So can we assume that a q is always followed by a u in the string or not? \$\endgroup\$ – Business Cat Apr 5 '17 at 20:24
  • 3
    \$\begingroup\$ Yes you can assume it always has a u \$\endgroup\$ – user67719 Apr 5 '17 at 20:48
  • 10
    \$\begingroup\$ This challenge could make a pedant quite dishappy... \$\endgroup\$ – Spratty Apr 6 '17 at 10:08

18 Answers 18


Python, 55 bytes

lambda n:'ddiiiiuiimmlrnss'[5-'rlmphq'.find(n[0])::7]+n

Try it online!

We need to handle 7 different starting letters:
g -> dis, h -> dis, p -> im, m -> im, l -> il, r -> ir and everything else -> un

We can store all these negations in a single string and extract the right one through slicing:

 d      i      s
  d      i      s
   i      m
    i      m
     i      l
      i      r
       u      n


Now we need to calculate the start index i. 'rlmphq'.find returns 0 for 'r', 5 for q and -1 for everything not contained in the string. To get the needed value from 0 to 6 we still need to subtract the return value from 5, resulting in this code:

  • \$\begingroup\$ That's really cute! \$\endgroup\$ – Steve Bennett Apr 6 '17 at 10:59
  • \$\begingroup\$ Can someone explain how the hell is this works? I understand what's going on with the slice notation, but what are the magic strings ddiiiiuiimmlrnss and rlmphq and the number 5 for, why is the slice skip 7? \$\endgroup\$ – Keatinge Apr 6 '17 at 19:51
  • \$\begingroup\$ @Keatinge added an explanation. I hope it helps you \$\endgroup\$ – ovs Apr 6 '17 at 20:52

GNU sed, 50 bytes

Includes +1 for -r


Nothing fancy. Uses & in the replacement to combine a few substitutions, and t to skip the last one if one of the first substitutions happens.

Try it online!


Jelly, 30 bytes


Try it online!


1ị“qmlrrhp”iị“3bµWI⁼ṡ÷ʠ$»œs5¤; - Main link: string
1ị                             - 1 index into the string (first character of the string)
           i                   - 1-based index of 1st occurrence of that in (else zero):
  “qmlrrhp”                    -     char list "qmlrrhp"
            ị                  - index into (1-based and modulo):
                            ¤  -     nilad followed by link(s) as a nilad:
             “3bµWI⁼ṡ÷ʠ$»      -         compressed string "dis"+"imili"+"run"="disimilirun"
                         œs5   -         split "equally" into 5: ["dis","im","il","ir","un"]
                             ; - concatenate with the string

Note that the repeated r in “qmlrrhp” is in the 5th index, which, if referenced, would result in prepending un, so it could equally well be anything other than h or p.


///, 59 56 bytes


Try it online!

Input goes after the very last #.

How it works:

I made an optimization that reduced the size to 56 bytes, but since that complicates things I will explain the original version, then explain the golf.

/#qu/disqu//#h/dish//#l/ill//#m/imm//#p/ipp//#r/irr//#/un/# |everything after the ` |` is not code.
/#qu/disqu/                                                 |replace `qu` with `disqu`
           /#h/dish/                                        |replace `h` with `dish`.
                    /#l/ill/                                |replace `l` with `ill`.
                            /#m/imm/                        |replace `m` with `imm`.
                                    /#p/ipp/                |replace `p` with `ipp`.
                                            /#r/irr/        |replace `r` with `irr`.
                                                    /#/un/  |replace everything else with `un`.
                                                          # |safety control

Intuition: The challenge is simple enough, just add the negative depending on the beginning of the word. However, in ///, you cannot just concatenate if [...], you can only replace something following a specific pattern. So in this program, the positive word beginnings are replaced with the negative word beginnings. The # was added to make sure that once a new beginning was added, no more new beginnings would be added. The # also made it possible to do 'everything else: un'.

The golf incorporates a new substitution at he beginning: /^/\/\/#/. This replaces all ^ with //#, which was a common pattern in the original version.


TI-Basic, 104 bytes

Prompt Str0
If Str2="Q" or Str2="H
If Str2="L
If Str2="M" or Str2="P
If Str2="R
If Ans=Str0

Requires all capital letters.


Prompt Str0             # 4 bytes, input user string to Str0
sub(Str0,1,1→Str2       # 12 bytes, store first character in Str2
Str0                    # 3 bytes, store Str0 in Ans
If Str2="Q" or Str2="H  # 14 bytes, if the first letter was Q or H
"DIS"+Ans               # 8 bytes, store DIS+Ans in Ans
If Str2="L              # 7 bytes, If the first letter was L
"IL"+Ans                # 7 bytes, store IL+Ans in Ans
If Str2="Q" or Str2="H  # 14 bytes, if the first letter was Q or H
"IM"+Ans                # 7 bytes, store DIS+Ans in Ans
If Str2="R              # 7 bytes, if the first letter was R
"IR"+Ans                # 7 bytes, store IR+Ans in Ans
If Ans=Str0             # 6 bytes, if Ans has not been changed (first letter was none of the above)
"UN"+Ans                # 7 bytes, store UN+Ans in Ans
Ans                     # 1 byte, implicitly return Ans

JavaScript (71 64 61 bytes)



  • Saved 7 bytes thanks to @ErtySeidohl (charAt(0) -> [0])
  • Saved 3 bytes thanks to @edc65 (assigning shared prefixes to variables)

var f = w=>({q:a='dis',h:a,l:'il',m:b='im',p:b,r:'ir'}[w[0]]||'un')+w;

function onChange() {
   var word = event.target.value;
   var output = f(word);
   document.getElementById('output').innerHTML = output;
Input Word: <input type='text' oninput='onChange()'/><br/>
Output Word: <span id="output">

  • 1
    \$\begingroup\$ If you don't care about backward compatibility with IE7, couldn't you use w[0] instead of w.charAt(0)? \$\endgroup\$ – Erty Seidohl Apr 6 '17 at 2:21
  • \$\begingroup\$ @ErtySeidohl Thanks! Just learned something new ;-) \$\endgroup\$ – forrert Apr 6 '17 at 4:41
  • \$\begingroup\$ I'm new here, but is it legitimate to provide an answer starting with just w=>...? The actual function definition would include let f=w=>... ? (Probably covered in an FAQ somewhere...) \$\endgroup\$ – Steve Bennett Apr 6 '17 at 7:10
  • \$\begingroup\$ @SteveBennett yes it's legitimate. Naming the function is not relevant \$\endgroup\$ – edc65 Apr 6 '17 at 10:32
  • 1
    \$\begingroup\$ w=>({q:a='dis',h:a,l:'il',m:b='im',p:b,r:'ir'}[w[0]]||'un')+w 3 bytes less \$\endgroup\$ – edc65 Apr 6 '17 at 10:52

Batch, 114 bytes

@set p=un
@for %%p in (dis.q dis.h il.l im.m im.p ir.r)do @if .%w:~,1%==%%~xp set p=%%~np
@echo %p%%w%

Checks the first character of the word against the list of custom prefixes and if so changes the prefix from the default of un. Special-casing qu is possible at a cost of 21 bytes.


Haskell, 71 bytes

f l=maybe"un"id(lookup(l!!0)$zip"qhlmpr"$words"dis dis il im im ir")++l

Usage example: f "legal"-> "illegal". Try it online!

Build a lookup table of prefix/replacement pairs for looking up the first char of the input string with a default value of "un" if not found.


Retina, 54 bytes



             {implicit replace stage}
^[^hqlmpr]   Append un to words starting with none of: hqlmpr
^[hq]        Append dis to words starting with h or q
 ^l          Append il to words starting with l
^[mp]        Append il to words starting with m or p
^r           Append ir to words starting with r

First time I've used Retina. It's a pretty neat language.

Try it online!

  • \$\begingroup\$ Very nice first try at a language! +1 \$\endgroup\$ – Arjun Apr 6 '17 at 8:22
  • \$\begingroup\$ And with that you have 2500 rep! Congratulations! \$\endgroup\$ – Arjun Apr 6 '17 at 8:23
  • \$\begingroup\$ And your user index is 26600! \$\endgroup\$ – Arjun Apr 6 '17 at 8:23
  • \$\begingroup\$ So much perfect base-10 ! \$\endgroup\$ – Arjun Apr 6 '17 at 8:24

Javascript, 72 71 66 61 60 59 bytes



Yeah, it's still longer than an existing solution. :)


In case this needs any explanation, I'm taking advantage of the q/h and m/p pairs by combining their index in the search string with a mod 4, then using that as the lookup into the prefix array.

  • \$\begingroup\$ Great answer. Save 1 byte using search instead of indexOf. And some more, I think, using & instead of % \$\endgroup\$ – edc65 Apr 6 '17 at 10:53
  • \$\begingroup\$ Thank you! I didn't know about search. Can't see how to make the & trick work - would be perfect if my array was only 4 elements. \$\endgroup\$ – Steve Bennett Apr 6 '17 at 11:07

C, 109 107 bytes


Try it online!


Mathematica, 107 bytes



StartOfString~~x:#:>#2<>x& is a pure function where the first argument is a string pattern to match at the beginning of the string and the second argument is a string to prepend to the match. It returns a delayed rule suitable for use within StringReplace. This is then applied to each of the pairs {{"q"|"h","dis"},{"l","il"},{"m"|"p","im"},{"r","ir"},{_,"un"}} resulting in the list of rules


Finally this list is passed into StringReplace which gives an operator on strings.

  • 2
    \$\begingroup\$ Does Mathmatica have a builtin for everything? \$\endgroup\$ – user67719 Apr 5 '17 at 21:14

PHP, 101 Bytes

echo preg_match("#^(qu|[hlmpr])#",$argn,$t)?[qu=>dis,h=>dis,l=>il,m=>im,p=>im,r=>ir][$t[1]]:un,$argn;

Online Version


Excel 78 bytes

=TRIM(MID("  disdisil im im ir un",IFERROR(FIND(LEFT(A1),"qhlmpr"),7)*3,3))&A1

I found some close contenders using different methods that scored 81 bytes:


And 84 bytes:

=IFERROR(TRIM(MID("im disi"&LEFT(A1),MOD(FIND(LEFT(F1),"qlmhrp"),3)*3+1,3)),"un")&A1

REXX, 78 bytes

arg a
say s.p||a

Saves a few bytes by replying in UPPERCASE, e.g. potent -> IMPOTENT.


Perl, 49 + 1 (-p flag) = 50 bytes



perl -pe 's|^[hq]|dis$&|||s|^[lmr]|i$&$&|||s|p|imp|||s||un|' <<< responsible

Try it online.


Clojure, 65 bytes

#(str(get{\q"dis"\h"dis"\l"il"\m"im"\p"im"\r"ir"}(first %)"un")%)

Well this is boring... but I couldn't make it any shorter. At least there is very little whitespace.


OCaml, 85

(fun s->(match s.[0]with|'q'|'h'->"dis"|'l'->"il"|'m'|'p'->"im"|'r'->"ir"|_->"un")^s)

Anonymous function, uses pattern matching on its first char.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy