Changeable words

From Rosetta Code
Revision as of 14:39, 15 December 2020 by Simonjsaunders (talk | contribs) (Minor edit to C++ code)
Changeable words is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Using the dictionary   unixdict.txt,   change one letter in a word,   and if the changed word occurs in the dictionary,
then display the original word   and   the changed word here (on this page).

The length of any word shown should have a length   >  11.


Other tasks related to string operations:
Metrics
Counting
Remove/replace
Anagrams/Derangements/shuffling
Find/Search/Determine
Formatting
Song lyrics/poems/Mad Libs/phrases
Tokenize
Sequences



C++

Translation of: Go

<lang cpp>#include <cstdlib>

  1. include <fstream>
  2. include <iomanip>
  3. include <iostream>
  4. include <string>
  5. include <vector>

int hamming_distance(const std::string& str1, const std::string& str2) {

   size_t len1 = str1.size();
   size_t len2 = str2.size();
   if (len1 != len2)
       return 0;
   int count = 0;
   for (size_t i = 0; i < len1; ++i) {
       if (str1[i] != str2[i])
           ++count;
       // don't care about counts > 2 in this case
       if (count == 2)
           break;
   }
   return count;

}

int main(int argc, char** argv) {

   const char* filename(argc < 2 ? "unixdict.txt" : argv[1]);
   std::ifstream in(filename);
   if (!in) {
       std::cerr << "Cannot open file '" << filename << "'.\n";
       return EXIT_FAILURE;
   }
   std::string line;
   std::vector<std::string> dictionary;
   while (getline(in, line)) {
       if (line.size() > 11)
           dictionary.push_back(line);
   }
   std::cout << "Changeable words in " << filename << ":\n";
   int n = 1;
   for (const std::string& word1 : dictionary) {
       for (const std::string& word2 : dictionary) {
           if (hamming_distance(word1, word2) == 1)
               std::cout << std::setw(2) << std::right << n++
                   << ": " << std::setw(14) << std::left << word1
                   << " -> " << word2 << '\n';
       }
   }
   return EXIT_SUCCESS;

}</lang>

Output:
Changeable words in unixdict.txt:
 1: aristotelean   -> aristotelian
 2: aristotelian   -> aristotelean
 3: claustrophobia -> claustrophobic
 4: claustrophobic -> claustrophobia
 5: committeeman   -> committeemen
 6: committeemen   -> committeeman
 7: committeewoman -> committeewomen
 8: committeewomen -> committeewoman
 9: complementary  -> complimentary
10: complimentary  -> complementary
11: confirmation   -> conformation
12: conformation   -> confirmation
13: congresswoman  -> congresswomen
14: congresswomen  -> congresswoman
15: councilwoman   -> councilwomen
16: councilwomen   -> councilwoman
17: craftsperson   -> draftsperson
18: draftsperson   -> craftsperson
19: eavesdropped   -> eavesdropper
20: eavesdropper   -> eavesdropped
21: frontiersman   -> frontiersmen
22: frontiersmen   -> frontiersman
23: handicraftsman -> handicraftsmen
24: handicraftsmen -> handicraftsman
25: incommutable   -> incomputable
26: incomputable   -> incommutable
27: installation   -> instillation
28: instillation   -> installation
29: kaleidescope   -> kaleidoscope
30: kaleidoscope   -> kaleidescope
31: neuroanatomy   -> neuroanotomy
32: neuroanotomy   -> neuroanatomy
33: newspaperman   -> newspapermen
34: newspapermen   -> newspaperman
35: nonagenarian   -> nonogenarian
36: nonogenarian   -> nonagenarian
37: onomatopoeia   -> onomatopoeic
38: onomatopoeic   -> onomatopoeia
39: philanthrope   -> philanthropy
40: philanthropy   -> philanthrope
41: prescription   -> proscription
42: proscription   -> prescription
43: schizophrenia  -> schizophrenic
44: schizophrenic  -> schizophrenia
45: shakespearean  -> shakespearian
46: shakespearian  -> shakespearean
47: spectroscope   -> spectroscopy
48: spectroscopy   -> spectroscope
49: underclassman  -> underclassmen
50: underclassmen  -> underclassman
51: upperclassman  -> upperclassmen
52: upperclassmen  -> upperclassman

FreeBASIC

Brute force method, reuses some code from Odd_words#FreeBASIC. <lang freebasic>

  1. define NULL 0

type node

   word as string*32   'enough space to store any word in the dictionary
   nxt as node ptr

end type

function addword( tail as node ptr, word as string ) as node ptr

   'allocates memory for a new node, links the previous tail to it,
   'and returns the address of the new node
   dim as node ptr newnode = allocate(sizeof(node))
   tail->nxt = newnode
   newnode->nxt = NULL
   newnode->word = word
   return newnode

end function

function length( word as string ) as uinteger

   'necessary replacement for the built-in len function, which in this
   'case would always return 32
   for i as uinteger = 1 to 32
       if asc(mid(word,i,1)) = 0 then return i-1
   next i
   return 999

end function

dim as string word dim as node ptr tail = allocate( sizeof(node) ) dim as node ptr head = tail, curr = head, currj tail->nxt = NULL tail->word = "XXXXHEADER"

open "unixdict.txt" for input as #1 while true

   line input #1, word
   if word = "" then exit while
   if length(word)>11 then tail = addword( tail, word )

wend close #1

dim as string tempword

while curr->nxt <> NULL

   for i as uinteger = 1 to length(curr->word)
       for j as uinteger = 97 to 122
           if j = asc(mid(curr->word,i,1)) then continue for
           tempword = left(curr->word,i-1)+ chr(j) + mid(curr->word, i+1, length(curr->word)-i)
           currj = head
           while currj->nxt <> NULL
               if tempword = currj->word then print left(curr->word,length(curr->word));"   --->   ";tempword
               currj = currj->nxt
           wend
       next j
   next i
   curr = curr->nxt

wend</lang>

Output:
aristotelean   --->   aristotelian
aristotelian   --->   aristotelean
claustrophobia   --->   claustrophobic
claustrophobic   --->   claustrophobia
committeeman   --->   committeemen
committeemen   --->   committeeman
committeewoman   --->   committeewomen
committeewomen   --->   committeewoman
complementary   --->   complimentary
complimentary   --->   complementary
confirmation   --->   conformation
conformation   --->   confirmation
congresswoman   --->   congresswomen
congresswomen   --->   congresswoman
councilwoman   --->   councilwomen
councilwomen   --->   councilwoman
craftsperson   --->   draftsperson
draftsperson   --->   craftsperson
eavesdropped   --->   eavesdropper
eavesdropper   --->   eavesdropped
frontiersman   --->   frontiersmen
frontiersmen   --->   frontiersman
handicraftsman   --->   handicraftsmen
handicraftsmen   --->   handicraftsman
incommutable   --->   incomputable
incomputable   --->   incommutable
installation   --->   instillation
instillation   --->   installation
kaleidescope   --->   kaleidoscope
kaleidoscope   --->   kaleidescope
neuroanatomy   --->   neuroanotomy
neuroanotomy   --->   neuroanatomy
newspaperman   --->   newspapermen
newspapermen   --->   newspaperman
nonagenarian   --->   nonogenarian
nonogenarian   --->   nonagenarian
onomatopoeia   --->   onomatopoeic
onomatopoeic   --->   onomatopoeia
philanthrope   --->   philanthropy
philanthropy   --->   philanthrope
prescription   --->   proscription
proscription   --->   prescription
schizophrenia   --->   schizophrenic
schizophrenic   --->   schizophrenia
shakespearean   --->   shakespearian
shakespearian   --->   shakespearean
spectroscope   --->   spectroscopy
spectroscopy   --->   spectroscope
underclassman   --->   underclassmen
underclassmen   --->   underclassman
upperclassman   --->   upperclassmen
upperclassmen   --->   upperclassman

Go

Translation of: Wren

<lang go>package main

import (

   "bytes"
   "fmt"
   "io/ioutil"
   "log"
   "unicode/utf8"

)

func hammingDist(s1, s2 string) int {

   r1 := []rune(s1) // in case there are non-ASCII characters
   r2 := []rune(s2) // ditto
   if len(r1) != len(r2) {
       return 0
   }
   count := 0
   for i := 0; i < len(r1); i++ {
       if r1[i] != r2[i] {
           count++
           if count == 2 {
               break // don't care about counts > 2
           }
       }
   }
   return count

}

func main() {

   wordList := "unixdict.txt"
   b, err := ioutil.ReadFile(wordList)
   if err != nil {
       log.Fatal("Error reading file")
   }
   bwords := bytes.Fields(b)
   var words []string
   for _, bword := range bwords {
       s := string(bword)
       if utf8.RuneCountInString(s) > 11 {
           words = append(words, s)
       }
   }
   count := 0
   fmt.Println("Changeable words in", wordList, "\b:")
   for _, word1 := range words {
       for _, word2 := range words {
           if word1 != word2 && hammingDist(word1, word2) == 1 {
               count++
               fmt.Printf("%2d: %-14s -> %s\n", count, word1, word2)
           }
       }
   }

}</lang>

Output:
Changeable words in unixdict.txt:
 1: aristotelean   -> aristotelian
 2: aristotelian   -> aristotelean
 3: claustrophobia -> claustrophobic
 4: claustrophobic -> claustrophobia
 5: committeeman   -> committeemen
 6: committeemen   -> committeeman
 7: committeewoman -> committeewomen
 8: committeewomen -> committeewoman
 9: complementary  -> complimentary
10: complimentary  -> complementary
11: confirmation   -> conformation
12: conformation   -> confirmation
13: congresswoman  -> congresswomen
14: congresswomen  -> congresswoman
15: councilwoman   -> councilwomen
16: councilwomen   -> councilwoman
17: craftsperson   -> draftsperson
18: draftsperson   -> craftsperson
19: eavesdropped   -> eavesdropper
20: eavesdropper   -> eavesdropped
21: frontiersman   -> frontiersmen
22: frontiersmen   -> frontiersman
23: handicraftsman -> handicraftsmen
24: handicraftsmen -> handicraftsman
25: incommutable   -> incomputable
26: incomputable   -> incommutable
27: installation   -> instillation
28: instillation   -> installation
29: kaleidescope   -> kaleidoscope
30: kaleidoscope   -> kaleidescope
31: neuroanatomy   -> neuroanotomy
32: neuroanotomy   -> neuroanatomy
33: newspaperman   -> newspapermen
34: newspapermen   -> newspaperman
35: nonagenarian   -> nonogenarian
36: nonogenarian   -> nonagenarian
37: onomatopoeia   -> onomatopoeic
38: onomatopoeic   -> onomatopoeia
39: philanthrope   -> philanthropy
40: philanthropy   -> philanthrope
41: prescription   -> proscription
42: proscription   -> prescription
43: schizophrenia  -> schizophrenic
44: schizophrenic  -> schizophrenia
45: shakespearean  -> shakespearian
46: shakespearian  -> shakespearean
47: spectroscope   -> spectroscopy
48: spectroscopy   -> spectroscope
49: underclassman  -> underclassmen
50: underclassmen  -> underclassman
51: upperclassman  -> upperclassmen
52: upperclassmen  -> upperclassman

Julia

After finding for example "are <=> art", we do not also list the redundant "art <=> are" below. <lang julia>function changeable(wordfile, lengthover)

   words, results = split(read(wordfile, String)), []
   worddict = Dict(w => length(w) for w in words)
   for w in words
       len = get(worddict, w, 0)
       if len <= lengthover
           continue
       end
       alternatives = [w[1:p[1]-1] * p[2] * w[p[1]+1:end] for p in Iterators.product(1:len, 'a':'z')]
       for a in alternatives
           if a != w && haskey(worddict, a)
               push!(results, join(sort([w, a]), " <=> "))
               break
           end
       end
   end
   println(join(sort(unique(results)), "\n"))

end

changeable("unixdict.txt", 11)

</lang>

Output:
aristotelean <=> aristotelian    
claustrophobia <=> claustrophobic
committeeman <=> committeemen    
committeewoman <=> committeewomen
complementary <=> complimentary  
confirmation <=> conformation    
congresswoman <=> congresswomen  
councilwoman <=> councilwomen    
craftsperson <=> draftsperson    
eavesdropped <=> eavesdropper    
frontiersman <=> frontiersmen    
handicraftsman <=> handicraftsmen
incommutable <=> incomputable
installation <=> instillation
kaleidescope <=> kaleidoscope
neuroanatomy <=> neuroanotomy
newspaperman <=> newspapermen
nonagenarian <=> nonogenarian
onomatopoeia <=> onomatopoeic
philanthrope <=> philanthropy
prescription <=> proscription
schizophrenia <=> schizophrenic
shakespearean <=> shakespearian
spectroscope <=> spectroscopy
underclassman <=> underclassmen
upperclassman <=> upperclassmen

Perl

<lang perl>#!/usr/bin/perl

use strict; use warnings;

my @words; @ARGV = 'unixdict.txt'; while( <> )

 {
 chomp;
 length > 11 or next;
 for my $prev ( @{ $words[length] } )
   {
   ($prev ^ $_) =~ tr/\0//c == 1 and printf "%30s <-> %s\n", $prev, $_;
   }
 push @{ $words[length] }, $_;
 }</lang>
Output:
                  aristotelean <-> aristotelian
                claustrophobia <-> claustrophobic
                  committeeman <-> committeemen
                committeewoman <-> committeewomen
                 complementary <-> complimentary
                  confirmation <-> conformation
                 congresswoman <-> congresswomen
                  councilwoman <-> councilwomen
                  craftsperson <-> draftsperson
                  eavesdropped <-> eavesdropper
                  frontiersman <-> frontiersmen
                handicraftsman <-> handicraftsmen
                  incommutable <-> incomputable
                  installation <-> instillation
                  kaleidescope <-> kaleidoscope
                  neuroanatomy <-> neuroanotomy
                  newspaperman <-> newspapermen
                  nonagenarian <-> nonogenarian
                  onomatopoeia <-> onomatopoeic
                  philanthrope <-> philanthropy
                  prescription <-> proscription
                 schizophrenia <-> schizophrenic
                 shakespearean <-> shakespearian
                  spectroscope <-> spectroscopy
                 underclassman <-> underclassmen
                 upperclassman <-> upperclassmen

Phix

<lang Phix>function changeable(string a, b)

   return length(a)=length(b) and sum(sq_ne(a,b))=1

end function

function over11(string word) return length(word)>11 end function sequence words = filter(get_text("demo/unixdict.txt",GT_LF_STRIPPED),over11),

        res = {}

for i=1 to length(words) do

   for j=i+1 to length(words) do
       if changeable(words[i],words[j]) then
           res = append(res,words[i]&" <=> "&words[j])
       end if
   end for

end for printf(1,"%d changeable words found:\n%s\n",{length(res),join(shorten(res,"",3),"\n")})</lang>

Output:
26 changeable words found:
aristotelean <=> aristotelian
claustrophobia <=> claustrophobic
committeeman <=> committeemen
...
spectroscope <=> spectroscopy
underclassman <=> underclassmen
upperclassman <=> upperclassmen

Python

<lang python>from collections import defaultdict, Counter


def getwords(minlength=11, fname='unixdict.txt'):

   "Return set of lowercased words of > given number of characters"
   with open(fname) as f:
       words = f.read().strip().lower().split()
   return {w for w in words if len(w) > minlength}

words11 = getwords() word_minus_1 = defaultdict(list) # map word minus char to (word, index) pairs minus_1_to_word = defaultdict(list) # map word minus char to word

for w in words11:

   for i in range(len(w)):
       minus_1 = w[:i] + w[i+1:]
       word_minus_1[minus_1].append((w, i))   # minus one char
       if minus_1 in words11:
           minus_1_to_word[minus_1].append(w)
   

cwords = set() # Changed char words for _, v in word_minus_1.items():

   if len(v) >1:
       change_indices = Counter(i for wrd, i in v)
       change_words = set(wrd for wrd, i in v)
       words_changed = None
       if len(change_words) > 1 and change_indices.most_common(1)[0][1] > 1:
           words_changed = [wrd for wrd, i in v
                            if change_indices[i] > 1]
       if words_changed:
           cwords.add(tuple(sorted(words_changed)))

print(f"{len(minus_1_to_word)} words that are from deleting a char from other words:") for k, v in sorted(minus_1_to_word.items()):

   print(f"  {k:12} From {', '.join(v)}")

print(f"\n{len(cwords)} words that are from changing a char from other words:") for v in sorted(cwords):

   print(f"  {v[0]:12} From {', '.join(v[1:])}")</lang>
Output:
6 words that are from deleting a char from other words:
  chromatograph From chromatography
  electroencephalograph From electroencephalography
  evolutionary From revolutionary
  michelangelo From michaelangelo
  spectrograph From spectrography
  tetrafluoride From tetrafluouride

26 words that are from changing a char from other words:
  aristotelean From aristotelian
  claustrophobia From claustrophobic
  committeeman From committeemen
  committeewoman From committeewomen
  complementary From complimentary
  confirmation From conformation
  congresswoman From congresswomen
  councilwoman From councilwomen
  craftsperson From draftsperson
  eavesdropped From eavesdropper
  frontiersman From frontiersmen
  handicraftsman From handicraftsmen
  incommutable From incomputable
  installation From instillation
  kaleidescope From kaleidoscope
  neuroanatomy From neuroanotomy
  newspaperman From newspapermen
  nonagenarian From nonogenarian
  onomatopoeia From onomatopoeic
  philanthrope From philanthropy
  prescription From proscription
  schizophrenia From schizophrenic
  shakespearean From shakespearian
  spectroscope From spectroscopy
  underclassman From underclassmen
  upperclassman From upperclassmen

Raku

Sorensen-Dice is very fast to calculate similarities but isn't great for detecting small changes. Levenshtein is great for detecting small changes but isn't very fast.

Get the best of both worlds by doing an initial filter with Sorensen, then get exact results with Levenshtein. <lang perl6>use Text::Levenshtein; use Text::Sorensen :sorensen;

my @words = grep {.chars > 11}, 'unixdict.txt'.IO.words;

my %bi-grams = @words.map: { $_ => .&bi-gram };

my %skip = @words.map: { $_ => 0 };

say (++$).fmt('%2d'), |$_ for @words.hyper.map: -> $this {

   next if %skip{$this};
   my ($word, @sorensens) = sorensen($this, %bi-grams);
   next unless @sorensens.=grep: { 1 > .[0] > .8 };
   @sorensens = @sorensens»[1].grep: {$this.chars == .chars};
   my @levenshtein = distance($this, @sorensens).grep: * == 1, :k;
   next unless +@levenshtein;
   %skip{$_}++ for @sorensens[@levenshtein];
   ": {$this.fmt('%14s')}  <->  ", @sorensens[@levenshtein].join: ', ';

}</lang>

Output:
 1:   aristotelean  <->  aristotelian
 2: claustrophobia  <->  claustrophobic
 3:   committeeman  <->  committeemen
 4: committeewoman  <->  committeewomen
 5:  complimentary  <->  complementary
 6:   confirmation  <->  conformation
 7:  congresswoman  <->  congresswomen
 8:   councilwoman  <->  councilwomen
 9:   draftsperson  <->  craftsperson
10:   eavesdropped  <->  eavesdropper
11:   frontiersman  <->  frontiersmen
12: handicraftsman  <->  handicraftsmen
13:   incommutable  <->  incomputable
14:   installation  <->  instillation
15:   kaleidescope  <->  kaleidoscope
16:   neuroanatomy  <->  neuroanotomy
17:   newspaperman  <->  newspapermen
18:   nonagenarian  <->  nonogenarian
19:   onomatopoeia  <->  onomatopoeic
20:   philanthrope  <->  philanthropy
21:   proscription  <->  prescription
22:  schizophrenia  <->  schizophrenic
23:  shakespearean  <->  shakespearian
24:   spectroscope  <->  spectroscopy
25:  underclassman  <->  underclassmen
26:  upperclassman  <->  upperclassmen

REXX

This REXX version doesn't care what order the words in the dictionary are in,   nor does it care what
case  (lower/upper/mixed)  the words are in,   the search for alternades is   caseless.

It also allows the minimum length to be specified on the command line (CL) as well as the dictionary file identifier. <lang rexx>/*REXX program finds changeable words (within an identified dict.), changing any letter.*/ parse arg minL iFID . /*obtain optional arguments from the CL*/ if minL== | minL=="," then minL= 12 /*Not specified? Then use the default.*/ if iFID== | iFID=="," then iFID='unixdict.txt' /* " " " " " " */ @.= /*default value of any dictionary word.*/

       do #=1  while lines(iFID)\==0            /*read each word in the file  (word=X).*/
       x= strip( linein( iFID) )                /*pick off a word from the input line. */
       $.#= x;         upper x;     @.x= $.#    /*save: original case.                 */
       end   /*#*/                              /* [↑]   semaphore name is uppercased. */

say copies('─', 30) # "words in the dictionary file: " iFID finds= 0 /*count of the changeable words found.*/ abc= 'abcdefghijklmnopqrstuvwxyz'; upper abc /*alphabet ordered by frequency of use.*/ Labc= length(abc) /*the length of the alphabet to be used*/

       do j=1  for #-1;        L= length($.j)   /*process all the words that were found*/
       if L<minL               then iterate     /*Is the word long enough?  No, skip it*/
       if \datatype($.j, 'M')  then iterate     /*verify that the word is all letters. */
       x= $.j;                 upper x          /*get an uppercased version of the word*/
                               pad= left(, 9) /*PAD:   used for indenting the output.*/
           do k=1  for L;    y= substr(x, k, 1) /*Y:  the current letter being changed.*/
              do c=1  for Labc                  /* [↓]  change the Y letter to another.*/
              ?= substr(abc, c, 1)              /*get a new char to replace one in word*/
              if ?==y  then iterate             /*Is this the same char?  Then use next*/
              new= overlay(?, x, k);  upper new /*create a spanking new (changed) word.*/
              if @.new==  then iterate        /*if the new word isn't a word, skip it*/
              finds= finds + 1                  /*bump count of changeable words found.*/
              say pad  left($.j, 30)  @.new     /*indent original word for readability.*/
              end   /*c*/
           end      /*k*/
       end          /*j*/

say copies('─', 30) finds ' changeable words found with a minimum length of ' minL</lang>

output   when using the default inputs:
────────────────────────────── 25105 words in the dictionary file:  unixdict.txt
          aristotelean                   aristotelian
          aristotelian                   aristotelean
          claustrophobia                 claustrophobic
          claustrophobic                 claustrophobia
          committeeman                   committeemen
          committeemen                   committeeman
          committeewoman                 committeewomen
          committeewomen                 committeewoman
          complementary                  complimentary
          complimentary                  complementary
          confirmation                   conformation
          conformation                   confirmation
          congresswoman                  congresswomen
          congresswomen                  congresswoman
          councilwoman                   councilwomen
          councilwomen                   councilwoman
          craftsperson                   draftsperson
          draftsperson                   craftsperson
          eavesdropped                   eavesdropper
          eavesdropper                   eavesdropped
          frontiersman                   frontiersmen
          frontiersmen                   frontiersman
          handicraftsman                 handicraftsmen
          handicraftsmen                 handicraftsman
          incommutable                   incomputable
          incomputable                   incommutable
          installation                   instillation
          instillation                   installation
          kaleidescope                   kaleidoscope
          kaleidoscope                   kaleidescope
          neuroanatomy                   neuroanotomy
          neuroanotomy                   neuroanatomy
          newspaperman                   newspapermen
          newspapermen                   newspaperman
          nonagenarian                   nonogenarian
          nonogenarian                   nonagenarian
          onomatopoeia                   onomatopoeic
          onomatopoeic                   onomatopoeia
          philanthrope                   philanthropy
          philanthropy                   philanthrope
          prescription                   proscription
          proscription                   prescription
          schizophrenia                  schizophrenic
          schizophrenic                  schizophrenia
          shakespearean                  shakespearian
          shakespearian                  shakespearean
          spectroscope                   spectroscopy
          spectroscopy                   spectroscope
          underclassman                  underclassmen
          underclassmen                  underclassman
          upperclassman                  upperclassmen
          upperclassmen                  upperclassman
────────────────────────────── 52  changeable words found with a minimum length of  12

Ring

<lang ring> cStr = read("unixdict.txt") wordList = str2list(cStr) num = 0

see "working..." + nl

ln = len(wordList) for n = ln to 1 step -1

   if len(wordList[n]) < 12
      del(wordList,n)
   ok

next

see "Changable words are:" + nl

for n = 1 to len(wordList)

   len = len(wordList[n])
   for m = 1 to len
       abcList = "abcdefghijklmnopqrstuvwxyz"
       for abc in abcList
           str1 = left(wordList[n],m-1)
           str2 = abc
           str3 = right(wordList[n],len-m)
           tempWord = str1 + str2 + str3
           ind = find(wordList,tempWord) 
           bool = (ind > 0) and (wordList[n][m] != abc)
           if bool = 1 
              num = num + 1
              see "" + num + ". " + wordList[n] + " >> " + tempWord + nl
           ok
       next
   next

next

see "done..." + nl </lang> Output:

working...
Changable words are:
1. aristotelean >> aristotelian
2. aristotelian >> aristotelean
3. claustrophobia >> claustrophobic
4. claustrophobic >> claustrophobia
5. committeeman >> committeemen
6. committeemen >> committeeman
7. committeewoman >> committeewomen
8. committeewomen >> committeewoman
9. complementary >> complimentary
10. complimentary >> complementary
11. confirmation >> conformation
12. conformation >> confirmation
13. congresswoman >> congresswomen
14. congresswomen >> congresswoman
15. councilwoman >> councilwomen
16. councilwomen >> councilwoman
17. craftsperson >> draftsperson
18. draftsperson >> craftsperson
19. eavesdropped >> eavesdropper
20. eavesdropper >> eavesdropped
21. frontiersman >> frontiersmen
22. frontiersmen >> frontiersman
23. handicraftsman >> handicraftsmen
24. handicraftsmen >> handicraftsman
25. incommutable >> incomputable
26. incomputable >> incommutable
27. installation >> instillation
28. instillation >> installation
29. kaleidescope >> kaleidoscope
30. kaleidoscope >> kaleidescope
31. neuroanatomy >> neuroanotomy
32. neuroanotomy >> neuroanatomy
33. newspaperman >> newspapermen
34. newspapermen >> newspaperman
35. nonagenarian >> nonogenarian
36. nonogenarian >> nonagenarian
37. onomatopoeia >> onomatopoeic
38. onomatopoeic >> onomatopoeia
39. philanthrope >> philanthropy
40. philanthropy >> philanthrope
41. prescription >> proscription
42. proscription >> prescription
43. schizophrenia >> schizophrenic
44. schizophrenic >> schizophrenia
45. shakespearean >> shakespearian
46. shakespearian >> shakespearean
47. spectroscope >> spectroscopy
48. spectroscopy >> spectroscope
49. underclassman >> underclassmen
50. underclassmen >> underclassman
51. upperclassman >> upperclassmen
52. upperclassmen >> upperclassman
done...

Wren

Library: Wren-fmt

Using the Hamming Distance between two equal length strings which needs to be 1 here: <lang ecmascript>import "io" for File import "/fmt" for Fmt

var hammingDist = Fn.new { |s1, s2|

   s1 = s1.toList // in case there are non-ASCII characters
   s2 = s2.toList // ditto
   var count = 0
   var i = 0
   while (i < s1.count) {
       if (s1[i] != s2[i]) {
           count = count + 1
           if (count == 2) break // don't care about counts > 2
       }
       i = i + 1
   }
   return count

}

var wordList = "unixdict.txt" // local copy var words = File.read(wordList).trimEnd().split("\n").where { |w| w.count > 11 }.toList var count = 0 System.print("Changeable words in %(wordList):") for (word1 in words) {

   for (word2 in words) {
       if (word1 != word2 && word1.count == word2.count) {
           if (hammingDist.call(word1, word2) == 1) {
               count = count + 1
               Fmt.print("$2d: $-14s -> $s", count, word1, word2)
           }
       }
   }

}</lang>

Output:
Changeable words in unixdict.txt:
 1: aristotelean   -> aristotelian
 2: aristotelian   -> aristotelean
 3: claustrophobia -> claustrophobic
 4: claustrophobic -> claustrophobia
 5: committeeman   -> committeemen
 6: committeemen   -> committeeman
 7: committeewoman -> committeewomen
 8: committeewomen -> committeewoman
 9: complementary  -> complimentary
10: complimentary  -> complementary
11: confirmation   -> conformation
12: conformation   -> confirmation
13: congresswoman  -> congresswomen
14: congresswomen  -> congresswoman
15: councilwoman   -> councilwomen
16: councilwomen   -> councilwoman
17: craftsperson   -> draftsperson
18: draftsperson   -> craftsperson
19: eavesdropped   -> eavesdropper
20: eavesdropper   -> eavesdropped
21: frontiersman   -> frontiersmen
22: frontiersmen   -> frontiersman
23: handicraftsman -> handicraftsmen
24: handicraftsmen -> handicraftsman
25: incommutable   -> incomputable
26: incomputable   -> incommutable
27: installation   -> instillation
28: instillation   -> installation
29: kaleidescope   -> kaleidoscope
30: kaleidoscope   -> kaleidescope
31: neuroanatomy   -> neuroanotomy
32: neuroanotomy   -> neuroanatomy
33: newspaperman   -> newspapermen
34: newspapermen   -> newspaperman
35: nonagenarian   -> nonogenarian
36: nonogenarian   -> nonagenarian
37: onomatopoeia   -> onomatopoeic
38: onomatopoeic   -> onomatopoeia
39: philanthrope   -> philanthropy
40: philanthropy   -> philanthrope
41: prescription   -> proscription
42: proscription   -> prescription
43: schizophrenia  -> schizophrenic
44: schizophrenic  -> schizophrenia
45: shakespearean  -> shakespearian
46: shakespearian  -> shakespearean
47: spectroscope   -> spectroscopy
48: spectroscopy   -> spectroscope
49: underclassman  -> underclassmen
50: underclassmen  -> underclassman
51: upperclassman  -> upperclassmen
52: upperclassmen  -> upperclassman

XPL0

<lang XPL0>string 0; \use zero-terminated strings int Dict(26000); \pointers to words (enough for unixdict.txt) int DictSize; \actual number of pointers in Dict

func StrCmp(A, B); \Compare string A to B char A, B; \Returns: >0 if A>B, =0 if A=B, and <0 if A>1 do

       [if A(I) # B(I) then return A(I) - B(I);
        if A(I) = 0 then return 0;
       ];

]; \StrCmp

func LookUp(Word); \Return 'true' if Word is in Dict char Word; int Lo, Hi, I, Cmp; [Lo:= 0; Hi:= DictSize-1; loop [I:= (Lo+Hi) / 2; \binary search

       Cmp:= StrCmp(Word, Dict(I));
       if Cmp < 0 then Hi:= I-1 else Lo:= I+1;
       if Cmp = 0 then return true;
       if Lo > Hi then return false;
       ];

]; \LookUp

int I, J, L, DI, Ch, Count; char Word, ChgWord(25); \longest word in unixdict.txt is 22 chars def Tab=$09, LF=$0A, CR=$0D, EOF=$1A;

[FSet(FOpen("unixdict.txt", 0), ^I); \load dictionary OpenI(3); \assume alphabetical order and all lowercase DI:= 0; \ignore non-alpha characters: 0..9, ' and & repeat Dict(DI):= Reserve(0); \get pointer to memory used to store Word

       Word:= Dict(DI);
       I:= 0;
       loop    [repeat Ch:= ChIn(3) until Ch # CR;     \remove possible CR
               if Ch=LF or Ch=EOF then quit;
               Word(I):= Ch;
               I:= I+1;
               ];
       Word(I):= 0;            \terminate Word string
       I:= Reserve(I+1);       \reserve memory used for Word
       DI:= DI+1;              \next dictionary entry

until Ch = EOF; DictSize:= DI;

DI:= 0; Count:= 0; \print out all changeable words repeat Word:= Dict(DI); \assume all lowercase letters

       I:= 0;  J:= 0;
       loop    [Ch:= Word(I);
               if Ch = 0 then quit;
               ChgWord(I):= Ch;
               I:= I+1;
               ];
       ChgWord(I):= 0;
       if I >= 12 then         \Word must have at least 12 chars
               [for J:= 0 to I-1 do    \for all letter positions in Word
                       [for L:= ^a to ^z do  \for all letters
                           if L # Word(J) then
                               [ChgWord(J):= L;
                               if LookUp(ChgWord) then
                                       [Count:= Count+1;
                                       IntOut(0, Count);  ChOut(0, Tab);
                                       Text(0, Word);  ChOut(0, Tab);
                                       Text(0, ChgWord);
                                       CrLf(0);
                                       ];
                               ChgWord(J):= Word(J);   \undo letter change
                               ];
                       ];
               ];
       DI:= DI+1;

until DI >= DictSize; ]</lang>

Output:
1       aristotelean    aristotelian
2       aristotelian    aristotelean
3       claustrophobia  claustrophobic
4       claustrophobic  claustrophobia
5       committeeman    committeemen
6       committeemen    committeeman
7       committeewoman  committeewomen
8       committeewomen  committeewoman
9       complementary   complimentary
10      complimentary   complementary
11      confirmation    conformation
12      conformation    confirmation
13      congresswoman   congresswomen
14      congresswomen   congresswoman
15      councilwoman    councilwomen
16      councilwomen    councilwoman
17      craftsperson    draftsperson
18      draftsperson    craftsperson
19      eavesdropped    eavesdropper
20      eavesdropper    eavesdropped
21      frontiersman    frontiersmen
22      frontiersmen    frontiersman
23      handicraftsman  handicraftsmen
24      handicraftsmen  handicraftsman
25      incommutable    incomputable
26      incomputable    incommutable
27      installation    instillation
28      instillation    installation
29      kaleidescope    kaleidoscope
30      kaleidoscope    kaleidescope
31      neuroanatomy    neuroanotomy
32      neuroanotomy    neuroanatomy
33      newspaperman    newspapermen
34      newspapermen    newspaperman
35      nonagenarian    nonogenarian
36      nonogenarian    nonagenarian
37      onomatopoeia    onomatopoeic
38      onomatopoeic    onomatopoeia
39      philanthrope    philanthropy
40      philanthropy    philanthrope
41      prescription    proscription
42      proscription    prescription
43      schizophrenia   schizophrenic
44      schizophrenic   schizophrenia
45      shakespearean   shakespearian
46      shakespearian   shakespearean
47      spectroscope    spectroscopy
48      spectroscopy    spectroscope
49      underclassman   underclassmen
50      underclassmen   underclassman
51      upperclassman   upperclassmen
52      upperclassmen   upperclassman