21
\$\begingroup\$

Semordnilaps (also known as heteropalindromes, semi-palindromes, half-palindromes, reversgrams, mynoretehs, reversible anagrams, word reversals, or anadromes) are words which are also words when spelled backwards. A few examples are:

  • Ward <=> Draw
  • Mined <=> Denim
  • Parts <=> Strap

Given a positive integer N (via function argument or STDIN), return / output a list of semordnilaps from this list of English words, that have exactly N letters. The list of words can be saved locally on your computer as a text file called: w.txt. You can also get the list from the url, but it will be included in the byte count.

Rules:

  1. Palindromes are not semordnilaps! Therefore, the words "noon", "rotor" and "radar" should not be included in the list.
  2. Only one of the words (in a semordnilap pair) should be included in the list. Therefore, if "dog" is in the list, "god" should not be (it doesn't matter which one is included.)
  3. If there are no semordnilaps, the output should be an empty string, 0, FALSE or something else indicating that there were no results. The function must work even if there are no results.

This is code golf so the shortest code in bytes wins!


Leaderboard

The Stack Snippet at the bottom of this post generates the catalog from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the 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

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=42090,OVERRIDE_USER=44713;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

\$\endgroup\$
  • 10
    \$\begingroup\$ Very important N = 9 example. \$\endgroup\$ – Martin Ender Dec 1 '14 at 18:54
  • 5
    \$\begingroup\$ Shouldn't the singular be Emordnilap and the plural be Semordnilap? ;) \$\endgroup\$ – FryAmTheEggman Dec 1 '14 at 19:17
  • 1
    \$\begingroup\$ "Palindromes are not semordnilaps!" Wow, that sentence / bullet couldn't be more unclear, but now I see what you mean. Words which are themselves when reversed are not semordnilaps. \$\endgroup\$ – cat Mar 27 '16 at 23:27

14 Answers 14

10
\$\begingroup\$

Pyth, 23 (18 code, 5 necessary STDIN)

J'f&qlTQ&}_TJ>_TTJ

This is a fairly straightforward solution.

J stores the list of words. Then we filter over the list of words (f J) on the length of the word being the input (qlTQ), the reversed word being in the list (}_TJ), and the reversal of the word being greater than the word (>_TT). The last condition ensures T is not palindromic, and that only one of the pair is printed. The resultant list is printed.

The way Pyth works, the only way to open a file is to receive its name on STDIN. This is why I have counted 5 of the STDIN bytes, w.txt, in my score.

Example run:

$ pyth -c "J'f&qlTQ&}_TJ>_TTJ" <<< '6
w.txt'
['animal', 'denier', 'diaper', 'drawer', 'pupils', 'recaps', 'redraw', 'sleets', 'snoops', 'sports']
\$\endgroup\$
10
\$\begingroup\$

Ruby, 74 bytes

f=->n{i=IO.read('w.txt').split
p *i&[f.reverse]if f.size==n while f=i.pop}

Iterates over the list by removing elements, which avoids both palindromes and outputting both "stressed" and "desserts". Using the same variable name for the function and the iterator gets around a Ruby syntax quirk: even though f=i.pop is evaluated before f.reverse, the line won't parse unless f already means something. I could also use p.

\$\endgroup\$
4
\$\begingroup\$

bash 134 157 118 bytes

f () { comm -12 <(sort w.txt) <(rev w.txt|sort)|while read w; do ((${#w}==$1))&&[[ $w<$(rev<<<$w) ]]&&echo $w; done; }

This is not a serious entry, but rather in response to Brian's Bash answer. This is how I tend to think about programming this sort of thing in Bash--by using Bash itself for as little as possible, and letting the built-in tools do all the work.

\$\endgroup\$
  • \$\begingroup\$ You will get both the word and its reflection from comm anyway, you could just echo "$w" and don't bother with extra sort and tail. If you do that, the result should already be sorted, so the final sort can also be removed. \$\endgroup\$ – orion Apr 16 '15 at 11:49
  • \$\begingroup\$ I don’t follow. How do I do the dedupe in your approach? \$\endgroup\$ – Aaron Davies Aug 17 '15 at 21:06
  • \$\begingroup\$ Also, I completely forgot to remove the palindromes. Fixed. \$\endgroup\$ – Aaron Davies Aug 17 '15 at 21:24
  • \$\begingroup\$ Sorry, sleep deprivation... you do indeed need to compare with the reverse to keep only one of them, but you don't have to care about palindromes and the rest of my comments are still valid. And now that you mention it, bash has a string comparison operator, so &&[[ $w > $(rev<<<$w) ]]&& echo $w still avoids sort|tail -1. With that, I think you can even drop the final sort and unique, because it removes palindromes and only outputs one of the words. \$\endgroup\$ – orion Aug 25 '15 at 13:05
  • \$\begingroup\$ Also, a useless cat at the beginning: <(sort w.txt) is ok. \$\endgroup\$ – orion Aug 25 '15 at 13:15
4
\$\begingroup\$

Python, 126 125 120 bytes

N=input()
J=open("w.txt").read().split()
for c in set(J):
 if N==len(c)and c!=c[::-1]and c[::-1]in J:print c;J.remove(c)

Pretty straightforward solution.

\$\endgroup\$
  • \$\begingroup\$ It appears to be shorter to define k=c[::-1]. Also, couldn't you call set() only the second time? I don't think there are repeats in the file? \$\endgroup\$ – FryAmTheEggman Dec 1 '14 at 19:57
  • \$\begingroup\$ @FryAmTheEggman: Actually it would take 11 characters to assign k (9 for definition plus 1 for newline plus 1 for space), but I would only save 10 characters (take out [::-1] for 6 but add 1 cause I then need a space). About the set, I need J to be a set because I remove the used words from it to satisfy the no-dupe rule \$\endgroup\$ – Claudiu Dec 1 '14 at 20:16
  • \$\begingroup\$ switching to N==len(c)and saves a space. \$\endgroup\$ – isaacg Dec 1 '14 at 20:18
  • \$\begingroup\$ @isaacg: Ah yes, ty \$\endgroup\$ – Claudiu Dec 1 '14 at 20:18
  • \$\begingroup\$ You're short an = - should be ==. \$\endgroup\$ – isaacg Dec 1 '14 at 20:25
3
\$\begingroup\$

CJam, 48 47 45 42 38 bytes

Since the URL has to be counted, I'm using the same URL shortener as Optimizer.

"ri.ms/§"gDcN+/{,ea~i=},_Wf%&{_W%>},N*

The § is part of extended ASCII, so each character in the code can be encoded in a single byte.

As in Optimizer's case, you'll have to use the Java interpreter and run this locally from a file, say semordnilap.cjam, and then

java -jar cjam-0.6.2.jar semordnilap.cjam <N>

so the input is given as a command-line argument.

How it works (slightly outdated):

"Prepare word list:";
"ri.ms/§"gDcN+/ 
"ri.ms/§"g      "Fetch list from shortened URL.";
          DcN+  "Create \r\n string.";
              / "Split input into lines.";

"Process input:";
{,ea~i=},
{      }, "Filter the list.";
 ,        "Get word length.";
  ea      "Push command-line arguments.";
    ~     "Unwrap array.";
     i    "Convert to integer.";
      =   "Check equality.";

"Find all palindromes and semordnilaps:";
_Wf%&
_     "Duplicate list.";
 Wf%  "Reverse each line.";
    & "Set intersection.";

"Remove duplicates and palindromes:";
{_W%>},N*
{    },   "Filter list.";
 _W%      "Duplicate word, reverse.";
    >     "Check lexicographic order.";
       N* "Join with newlines.";

The resulting string is automatically printed at the end of the program.

\$\endgroup\$
3
\$\begingroup\$

Java, 280 218 bytes

Compared to the rest of the competition, I have absolutely no idea if this is a good score.

void a(int n)throws Exception{List<String>l=Files.readAllLines(Paths.get("w.txt"));for(String s:l){String c=new StringBuilder(s).reverse()+"";if(s.length()==n&&c.compareTo(s)>0&&l.contains(c)){System.out.println(s);}}}

Expanded:

void a(int n)throws Exception{
    List<String>l=Files.readAllLines(Paths.get("w.txt"));
    for(String s:l){
        String c=new StringBuilder(s).reverse()+"";
        if(s.length()==n&&c.compareTo(s)>0&&l.contains(c)){
            System.out.println(s);
        }
    }
}

Uses compareTo() to simultaneously ignore palindromes and duplicates.

\$\endgroup\$
  • 2
    \$\begingroup\$ Java will always blow in a golf contest. \$\endgroup\$ – Rodolfo Dias Dec 2 '14 at 9:03
  • \$\begingroup\$ I've never seen Files.readAllLines(Paths.get("w.txt")) before. That's a useful trick. \$\endgroup\$ – Ypnypn Dec 2 '14 at 18:51
2
\$\begingroup\$

CJam, 68 bytes

"www.ri.ms/§"gDc-N/{_,ea~~=\_W%=!*},:Q{_W%aQ\/,({Q\a-:Q;1}{;0}?},N*

You will have to download the Java version of the compiler from here and save the above code in a file called words.cjam (can be any name). Then run the code like

java -jar cjam-0.6.2.jar <file_name> <input_number> [> <output_file_name>]

For example, for N = 8,

java -jar cjam-0.6.2.jar words.cjam 8 > out.txt  
\$\endgroup\$
  • \$\begingroup\$ Java–that's not an acronym. Please don't spell it JAVA. \$\endgroup\$ – FUZxxl Apr 16 '15 at 12:39
  • \$\begingroup\$ @FUZxxl this much is self-editable .. \$\endgroup\$ – Optimizer Apr 16 '15 at 13:06
  • \$\begingroup\$ I'm not a police officer. People have different opinions about spelling and I respect your opinion. I merely suggest you to not spell Java as if it was an acronym, I do not want to force a different spellin on you. \$\endgroup\$ – FUZxxl Apr 16 '15 at 17:07
  • \$\begingroup\$ @FUZxxl I really don't mid :D \$\endgroup\$ – Optimizer Apr 16 '15 at 17:15
2
\$\begingroup\$

Node.js, 172 bytes

Function:

function d(n){return(require('fs').readFileSync("w.txt")+"").match(RegExp(("b"+Array(n+1).join("(.)")+"b(?=(.|s)*b"+"87654321".substr(-n)+"b)").replace(/\w/g,"\\$&"),"g"))}

Testing:

console.log(d(+process.argv[2]));
// run from command line like this:
// node main 4
// where
//   main is js file name
//   4    is length
\$\endgroup\$
2
\$\begingroup\$

K, 59 bytes

{*+?{x@<x}'+(w;r)@\:&(x=#:'w)&(~w~'r)&w in r:|:'w:0:`w.txt}

Pretty straightforward. Read the list, construct the reverse list, take their intersection, filter out the palindromes, filter in the required count, sort and dedupe the pairs.

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

Ruby, 95 bytes

f=->n{l=[]
(a=IO.read"w.txt").split.map{|w|a[w]=?.
w.size==n&&a[/^#{r=w.reverse}\s$/]&&l<<w}
l}

Explanation

  • Input is taken as the argument to a lambda. It expects an Integer.
  • Read the file into memory as a String (a).
  • Loop trough an Array of all words (without newlines).
    • Remove the word from a.
    • Add qualifying words to the Array l.
  • Return l.

An empty Array is returned when no qualifying words were found.

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

Node.js, CoffeeScript, 132 Bytes

f=(n)->(require('fs').readFileSync('w.txt')+'').split('\n').filter((s,i,f)->s.length==n&&f.indexOf(s.split('').reverse().join(''))>i)

Node.js, 162 Bytes

function a(n){return(require('fs').readFileSync('w.txt')+'').split('\n').filter(function(s,i,f){return s.length==n&&f.indexOf(s.split('').reverse().join(''))>i})}

Chrome Dev Tools Console, 111 Bytes (On the download page)

f=(n=>$('pre').innerText.split('\n').filter((x,i,a)=>x.length==n&&a.indexOf(x.split('').reverse().join(''))>i))

All versions return an array of all Semordnilaps of length n.

Node.js, 162 Bytes

Prints all semordnilaps:

function a(n){(require('fs').readFileSync('w.txt')+'').split('\n').map(function(s,i,f){s.length==n&&f.indexOf(s.split('').reverse().join(''))>i&&console.log(s)})}
\$\endgroup\$
1
\$\begingroup\$

Julia, 101 bytes

k=split(readall("w.txt"))
for(i=k)
([]!=(l=find(x->x==reverse(i)&&x!=i,k)))&&(println(i);k[l]="")
end

This should actually work...

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

Mathematica, 105 bytes

F[f_,n_]:=(W=StringSplit@Import@f;Select[W,StringLength@#==n&&MemberQ[W,r@#]&&Order[r@#,#]==1&&r@#!=# &])

Sometimes Import will automatically split the text into list of list of lines, or treat it as CSV or TSV. At other times, Import will read the contents of the file into a string. Import did the latter for the test data.

tests

\$\endgroup\$
0
\$\begingroup\$

BASH

f() {
  w=($(cat "${1}"))
  for ((i=0;i<=${#w[@]};i++)); do
    if ((${#w[$i]} == $2)); then
      r=
      for((x=${#w[$i]}-1;x>=0;x--)); do
        r="$r${w[$i]:$x:1}";
      done
      if [[ ${w[$i]} != ${r} ]] && grep -qw $r "${1}"; then
        echo "${w[$i]}, ${r}"
      fi
      unset w[$i]
    fi
  done
}

tests...

f /usr/share/dict/words 5

debut, tubed
decaf, faced
decal, laced
deeps, speed
denim, mined
devil, lived
draws, sward
faced, decaf
keels, sleek
knits, stink
laced, decal
lager, regal
leper, repel
lever, revel
lived, devil
loops, spool
loots, stool
mined, denim
parts, strap
peels, sleep
pools, sloop
ports, strop
rebut, tuber
regal, lager
remit, timer
repel, leper
revel, lever
sleek, keels
sleep, peels
sloop, pools
smart, trams
snaps, spans
snips, spins
spans, snaps
speed, deeps
spins, snips
spool, loops
spots, stops
sprat, tarps
stink, knits
stool, loots
stops, spots
strap, parts
straw, warts
strop, ports
sward, draws
tarps, sprat
timer, remit
trams, smart
\$\endgroup\$
  • 2
    \$\begingroup\$ I guess this isn't very golfed..? This would be a nice answer on Stack Overflow (if it had some comments and explanations). \$\endgroup\$ – Stewie Griffin Dec 2 '14 at 7:40
  • 1
    \$\begingroup\$ Agreed, this was my first iteration at solving this in bash. I tired to make it as readable as possible. but as far as golf goes. it's more like I shot a 9 on a par 3 : - ) \$\endgroup\$ – Brian Dec 2 '14 at 15:52

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.