Anadromes: Difference between revisions

17,788 bytes added ,  2 months ago
New post.
(Created Nim solution.)
(New post.)
 
(14 intermediate revisions by 12 users not shown)
Line 1:
{{draft task}}
 
 
Line 18:
 
 
 
=={{header|Ada}}==
<syntaxhighlight lang="ada">
-- Find all anadromes of length > 6
-- J. Carter 2023 Mar
 
with Ada.Containers.Indefinite_Ordered_Sets;
with Ada.Text_IO;
 
procedure Anadromes is
package Word_Sets is new Ada.Containers.Indefinite_Ordered_Sets (Element_Type => String);
function Reversed (S : in String) return String is
(if S = "" then S else S (S'Last) & Reversed (S (S'First .. S'Last - 1) ) );
File : Ada.Text_IO.File_Type;
W : Word_Sets.Set;
begin -- Anadromes
Ada.Text_IO.Open (File => File, Mode => Ada.Text_IO.In_File, Name => "words.txt");
Fill : loop
exit Fill when Ada.Text_IO.End_Of_File (File);
W.Insert (New_Item => Ada.Text_IO.Get_Line (File) );
end loop Fill;
Ada.Text_IO.Close (File => File);
Search : for Word of W loop
if Word'Length > 6 then
Backwards : declare
Rev : constant String := Reversed (Word);
begin -- Backwards
if Word < Rev and W.Contains (Rev) then
Ada.Text_IO.Put_Line (Item => Word & (1 .. 10 - Word'Length => ' ') & Rev);
end if;
end Backwards;
end if;
end loop Search;
end Anadromes;
</syntaxhighlight>
{{out}}
<pre>
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
desserts stressed
deviler relived
dioramas samaroid
gateman nametag
leveler relevel
pat-pat tap-tap
redrawer rewarder
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
</pre>
 
=={{header|ALGOL 68}}==
Reads the words from standard input, stopping when a word = ZZZ is found (which is the last word in words.txt).<br>
Unfortunately, Algol 68G doesn't like an array of STRINGs with more than 300 000 elements, even though it allows INT arrays to have millions - at least under Windows<br>
(I haven't tried it with Linux).<br>
Line 70 ⟶ 131:
list[ low ] = item
END # contains # ;
 
# set the end of file handler for stand in #
on logical file end( stand in, ( REF FILE f )BOOL: at eof := TRUE );
 
[ 1 : 500 000 ]STRING words;
Line 76 ⟶ 140:
INT max length := 0;
BOOL at eof := FALSE;
WHILE NOT at eof
DO
STRING word;
read( ( word, newline ) );
NOT at eof := word = "ZZZ";
DO
t count +:= 1;
INT w length := 1 + ( UPB word - LWB word );
Line 114 ⟶ 178:
OD;
print( ( newline, "Found ", whole( a count, 0 ), " anadromes", newline ) )
END
END</syntaxhighlight>
</syntaxhighlight>
{{out}}
<pre>
Line 141 ⟶ 206:
 
Found 17 anadromes
</pre>
 
=={{header|Ada}}==
<syntaxhighlight lang="ada">
-- Find all anadromes of length > 6
-- J. Carter 2023 Mar
 
with Ada.Containers.Indefinite_Ordered_Sets;
with Ada.Text_IO;
 
procedure Anadromes is
package Word_Sets is new Ada.Containers.Indefinite_Ordered_Sets (Element_Type => String);
function Reversed (S : in String) return String is
(if S = "" then S else S (S'Last) & Reversed (S (S'First .. S'Last - 1) ) );
File : Ada.Text_IO.File_Type;
W : Word_Sets.Set;
begin -- Anadromes
Ada.Text_IO.Open (File => File, Mode => Ada.Text_IO.In_File, Name => "words.txt");
Fill : loop
exit Fill when Ada.Text_IO.End_Of_File (File);
W.Insert (New_Item => Ada.Text_IO.Get_Line (File) );
end loop Fill;
Ada.Text_IO.Close (File => File);
Search : for Word of W loop
if Word'Length > 6 then
Backwards : declare
Rev : constant String := Reversed (Word);
begin -- Backwards
if Word < Rev and W.Contains (Rev) then
Ada.Text_IO.Put_Line (Item => Word & (1 .. 10 - Word'Length => ' ') & Rev);
end if;
end Backwards;
end if;
end loop Search;
end Anadromes;
</syntaxhighlight>
{{out}}
<pre>
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
desserts stressed
deviler relived
dioramas samaroid
gateman nametag
leveler relevel
pat-pat tap-tap
redrawer rewarder
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
</pre>
 
Line 380 ⟶ 384:
revotes <-> setover
sallets <-> stellas
</pre>
 
=={{header|Common Lisp}}==
 
The hash-table's <code>equalp</code> test is case-insensitive, and so we find 32 pairs rather than 17. However, the table maps each word to itself, and so looking up a reversed word gets it in its original case, as can be seen in the output.
 
<syntaxhighlight lang="lisp">
(defun read-words (filename)
(let ((words '()))
(with-open-file (s filename :direction :input)
(loop
(let ((word (read-line s nil nil)))
(if word
(when (> (length word) 6)
(setq words (cons word words)))
(return (reverse words))))))))
 
(defun anadromes ()
(let ((words (read-words "notes/words.txt"))
(dict (make-hash-table :test #'equalp)))
(dolist (word words)
(setf (gethash word dict) word))
(mapcar
(lambda (word)
(list word (gethash (reverse word) dict)))
(remove-if-not
(lambda (word)
(let ((rev (reverse word)))
(and (string-lessp word rev)
(gethash rev dict))))
words))))
 
(format t "~%~:{~10A ~10A~%~}~%"
(anadromes))
</syntaxhighlight>
 
{{Out}}
 
<pre>
amaroid diorama
anacara Aracana
Annabal Labanna
Artamus Sumatra
Colbert Trebloc
degener reneged
deifier reified
Delbert trebled
Delevan naveled
deliver reviled
dessert tressed
desserts stressed
deviler relived
dioramas samaroid
Eimmart trammie
Emmeram maremme
gateman nametag
Latimer remital
Lattimer remittal
lessees Seessel
leveler relevel
Nicolaus Sualocin
pat-pat tap-tap
redrawer rewarder
reknits stinker
relever reveler
reliver reviler
revotes setover
ROYGBIV vibgyor
Rotanev Venator
sallets stellas
sennits Stinnes
</pre>
 
Line 437 ⟶ 512:
{ "sallets" "stellas" }
}
</pre>
 
=={{header|FutureBasic}}==
<syntaxhighlight lang="future basic">
 
begin globals
ptr gwordsPtr
uint32 gfilen
end globals
 
local fn loadDict
window 1, @"Anadromes",(0,0,209,502)
CFURLRef url = fn URLWithString( @"file:///Applications/FutureBasic/words.txt" )
if ( url )
open "I", 1, url
gfilen = lof( 1 )
gwordsPtr = fn malloc( gfilen )
read file 1, gwordsPtr, gfilen
close
end if
end fn
 
local fn anadromes
uint32 r, i, top, bot, med, count = 0
CFStringRef string = lcase(fn StringWithBytes( gwordsPtr, gfilen, NSASCIIStringEncoding ))
CFArrayRef array = fn StringComponentsSeparatedByString( string, @"\n" )
: array = fn arraySortedarrayUsingSelector( array, @"caseInsensitiveCompare:" )
print
for r = 0 to len(array)-1
if len( array[r] ) < 7 then continue
string = @""
for i = 0 to len(array[r]) - 1
string = fn StringByAppendingString( mid( array[r],i,1 ), string )
next
if fn StringIsEqual( array[r], string ) then continue
bot = r+1 : top = len( array )-1
while ( top - bot ) > 1
med = ( bot + top )>>1
select fn StringCompare( string, array[med] )
case NSOrderedAscending : top = med
case NSOrderedDescending : bot = med
case else : count++
print ,count,array[r];left( @" ",9-len(string) );string
break
end select
wend
next
end fn
 
fn loadDict
CFTimeInterval t = fn CACurrentMediaTime
fn anadromes
printf @"\t\t%.3f secs",fn CACurrentMediaTime - t
 
handleevents
</syntaxhighlight>
{{out}}
[[File:32 Anadromes.png]]
 
=={{header|Go}}==
{{works with|go|1.21}}
<syntaxhighlight lang="go">package main
 
import (
"bufio"
"cmp"
"fmt"
"log"
"os"
"slices"
"strings"
"unicode/utf8"
)
 
func main() {
words := readUniqueWords("words.txt", 6)
anadromes := getAnadromes(words)
lefts := sortedKeys(anadromes)
for _, left := range lefts {
right := anadromes[left]
fmt.Printf("%9s ↔ %s\n", left, right)
}
}
 
func readUniqueWords(filename string, minLen int) map[string]struct{} {
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
words := map[string]struct{}{}
for scanner.Scan() {
word := scanner.Text()
if utf8.RuneCountInString(word) > minLen {
word = strings.ToLower(word)
words[word] = struct{}{}
}
}
return words
}
 
func getAnadromes(words map[string]struct{}) map[string]string {
seen := map[string]struct{}{}
anadromes := map[string]string{}
for _, word := range sortedKeys(words) {
rword := reverse(word)
if rword != word {
if _, found := words[rword]; found {
if _, found := seen[word]; !found {
if _, found := seen[rword]; !found {
anadromes[word] = rword
seen[word] = struct{}{}
seen[rword] = struct{}{}
}
}
}
}
}
return anadromes
}
 
func sortedKeys[K cmp.Ordered, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for key := range m {
keys = append(keys, key)
}
slices.Sort(keys)
return keys
}
 
func reverse(text string) string {
runes := []rune(text)
slices.Reverse(runes)
return string(runes)
}</syntaxhighlight>
 
{{out}}
<pre>
amaroid ↔ diorama
anacara ↔ aracana
annabal ↔ labanna
artamus ↔ sumatra
colbert ↔ trebloc
degener ↔ reneged
deifier ↔ reified
delbert ↔ trebled
delevan ↔ naveled
deliver ↔ reviled
dessert ↔ tressed
desserts ↔ stressed
deviler ↔ relived
dioramas ↔ samaroid
eimmart ↔ trammie
emmeram ↔ maremme
gateman ↔ nametag
latimer ↔ remital
lattimer ↔ remittal
lessees ↔ seessel
leveler ↔ relevel
nicolaus ↔ sualocin
pat-pat ↔ tap-tap
redrawer ↔ rewarder
reknits ↔ stinker
relever ↔ reveler
reliver ↔ reviler
revotes ↔ setover
rotanev ↔ venator
roygbiv ↔ vibgyor
sallets ↔ stellas
sennits ↔ stinnes
</pre>
 
Line 545 ⟶ 791:
│sallets │stellas │
└────────┴────────┘</syntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
 
public class Anadromes {
 
public static void main(String[] args) throws IOException {
List<String> words = Files.lines(Path.of("words.txt")).filter( word -> word.length() > 6 ).sorted().toList();
System.out.println("The anadrome pairs with more than 6 letters are:");
for ( String word : words ) {
String wordReversed = new StringBuilder(word).reverse().toString();
if ( wordReversed.compareTo(word) > 0 && Collections.binarySearch(words, wordReversed) > 0 ) {
System.out.println(word + " <--> " + wordReversed);
}
}
}
 
}
</syntaxhighlight>
{{ out }}
<pre>
The anadrome pairs with more than 6 letters are:
amaroid <--> diorama
degener <--> reneged
deifier <--> reified
deliver <--> reviled
dessert <--> tressed
desserts <--> stressed
deviler <--> relived
dioramas <--> samaroid
gateman <--> nametag
leveler <--> relevel
pat-pat <--> tap-tap
redrawer <--> rewarder
reknits <--> stinker
relever <--> reveler
reliver <--> reviler
revotes <--> setover
sallets <--> stellas
</pre>
 
=={{header|JavaScript}}==
<syntaxhighlight lang="JavaScript">
const fs = require('fs');
 
fs.readFile("words.txt", "utf8", (err, data) => {
if (err) {
console.error("Error reading the file:", err);
return;
}
 
const words = data.split(/\r?\n/); // This regex splits on both Unix and Windows line endings
const wordSet = new Set(words);
const seenWords = new Set();
const wordList = [];
 
for (let word of wordSet) {
if (word.length > 6) {
const reversedWord = word.split('').reverse().join('');
const wordPair = `${word}:${reversedWord}`;
const reversedWordPair = `${reversedWord}:${word}`;
 
if (wordSet.has(reversedWord) && reversedWord !== word && !seenWords.has(reversedWordPair)) {
wordList.push([word, reversedWord]);
seenWords.add(wordPair); // Store seen words as a string pair to make it easy to check
}
}
}
 
console.log(wordList.length);
 
wordList.sort((a, b) => a[0].localeCompare(b[0])).forEach(([word, reversedWord]) => {
console.log(`${word.padStart(9)} ${reversedWord}`);
});
});
</syntaxhighlight>
{{out}}
<pre>
17
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
desserts stressed
deviler relived
dioramas samaroid
gateman nametag
leveler relevel
pat-pat tap-tap
redrawer rewarder
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
</pre>
 
 
=={{header|jq}}==
Line 631 ⟶ 981:
sallets <=> stellas
sennits <=> stinnes
</pre>
 
=={{header|MiniScript}}==
This implementation is for use with the [http://miniscript.org/MiniMicro Mini Micro] version of MiniScript. The command-line version does not include a HTTP library. Modify the declaration of wordList object to use the file class instead of the http class.
<syntaxhighlight lang="miniscript">getPairs = function(words)
pairs = []
for i in range(0, words.len - 2)
for j in range(i + 1, words.len - 1)
pairs.push([words[i], words[j]])
end for
end for
return pairs
end function
 
isReversed = function(word1, word2)
for i in range(0, word1.len - 1)
if word1[i] != word2[-(i+1)] then return false
end for
return true
end function
 
makeKey = function(word)
return word.split("").sort.join("")
end function
 
wordList = http.get("https://raw.githubusercontent.com/dwyl/english-words/master/words.txt").split(char(10))
 
wordSets = {}
 
for word in wordList
k = makeKey(word)
if not wordSets.hasIndex(k) then
wordSets[k] = [word]
else
wordSets[k].push(word)
end if
end for
 
anadromes = []
for wordSet in wordSets.values
if wordSet.len > 1 and wordSet[0].len > 6 then
pairs = getPairs(wordSet)
for pair in pairs
if isReversed(pair[0], pair[1]) then print pair
end for
end if
end for
</syntaxhighlight>
{{out}}
<pre>["amaroid", "diorama"]
["degener", "reneged"]
["deifier", "reified"]
["deliver", "reviled"]
["deviler", "relived"]
["dessert", "tressed"]
["desserts", "stressed"]
["dioramas", "samaroid"]
["gateman", "nametag"]
["leveler", "relevel"]
["relever", "reveler"]
["revotes", "setover"]
["pat-pat", "tap-tap"]
["redrawer", "rewarder"]
["reknits", "stinker"]
["reliver", "reviler"]
["sallets", "stellas"]
</pre>
 
Line 838 ⟶ 1,254:
   leper <=> repel   
   lever <=> revel   
</pre>
=={{header|Python}}==
<syntaxhighlight lang="python"># anadrome.py by Xing216
with open("words.txt","r") as f:
words = f.read().splitlines()
word_set = set(words)
seen_words = []
word_list = []
for word in word_set:
if len(word) > 6:
reversed_word = word[::-1]
if reversed_word in word_set and reversed_word != word \
and (reversed_word,word) not in seen_words:
word_list.append((word, reversed_word))
seen_words.append((word,reversed_word))
for word,reversed_word in sorted(word_list, key=lambda x: x[0]):
print(f"{word:>9} {reversed_word}")
</syntaxhighlight>
{{out}}
</pre>
amaroid diorama
deifier reified
desserts stressed
dioramas samaroid
leveler relevel
nametag gateman
redrawer rewarder
reknits stinker
relived deviler
reneged degener
reveler relever
reviled deliver
reviler reliver
setover revotes
stellas sallets
tap-tap pat-pat
tressed dessert
</pre>
 
Line 923 ⟶ 1,376:
}
}
</syntaxhighlight>
{{out}}
<pre>
amaroid <-> diorama
degener <-> reneged
Line 1,008 ⟶ 1,464:
sallets <-> stellas
</pre>
 
 
=={{header|SETL}}==
<syntaxhighlight lang="setl">program find_anadromes;
read_file;
anadromes := {
{word, reverse word}
: word in words
| reverse word in words
and reverse word /= word
};
loop for anadrome in anadromes do
print(anadrome);
end loop;
read_file::
words := {};
dictfile := open("words.txt", "r");
loop doing
geta(dictfile, word);
while word /= om do
if #word <= 6 then continue; end if;
words with:= word;
end loop;
close(dictfile);
end program;</syntaxhighlight>
{{out}}
<pre>{amaroid diorama}
{degener reneged}
{deifier reified}
{deliver reviled}
{dessert tressed}
{desserts stressed}
{deviler relived}
{dioramas samaroid}
{gateman nametag}
{leveler relevel}
{'pat-pat' 'tap-tap'}
{redrawer rewarder}
{reknits stinker}
{relever reveler}
{reliver reviler}
{revotes setover}
{sallets stellas}</pre>
 
=={{header|Sidef}}==
Line 1,041 ⟶ 1,543:
dessert <=> tressed
</pre>
=={{header|Swift}}==
A complete implementation of a partitioning approach with async processing.
 
Supporting class:
<syntaxhighlight lang="swift">
import Foundation
import Algorithms
 
extension String {
var firstAndLast: String { String(self.first!) + String(self.last!) }
var backwards: String { String(self.reversed()) }
}
 
class AnadromeIndex {
 
// Stucture to index by length and the first and last character
// [ length: [ firstAndLast: [ words ] ] ]
/// Only compare words of same length, and where
/// firstAndLast corresponds to its reverse
var dict: [Int: [String: [String]]] = [:]
 
func loadFile(
fileURL: URL,
minLen: Int,
byLine: Bool = false
) async -> (Int, Int) {
var linesRead = 0
var wordsIndexed = 0
do {
if byLine {
for try await line in fileURL.lines {
linesRead += 1
if line.count < minLen { continue }
wordsIndexed += 1
self.addWord( line
.trimmingCharacters(in: .whitespacesAndNewlines)
)
}
} else {
let fileString = try String(contentsOf: fileURL, encoding: .utf8)
for line in fileString.components(separatedBy: .newlines) {
linesRead += 1
if line.count < minLen { continue }
wordsIndexed += 1
self.addWord( line
.trimmingCharacters(in: .whitespacesAndNewlines)
)
}
}
} catch {
debugPrint(error)
}
return (linesRead, wordsIndexed)
}
 
func addWord(_ str: String) {
let len = str.count
let index = str.firstAndLast
if let azDict = dict[len] {
if azDict[index] != nil {
self.dict[len]![index]!.append(str)
} else {
self.dict[len]!.updateValue([str], forKey: index)
}
} else {
dict.updateValue([index: [str]], forKey: len)
}
}
 
func findAnadromes() async -> [String] {
var done: [String] = []
var results: [String] = []
let allResults = await withTaskGroup(
of: [String].self,
returning: [String].self) { group in
// By length
for len in self.dict.keys.sorted() {
if let lenSet = self.dict[len] {
// By firstAndLast characters
for az in lenSet.keys.sorted() {
let za = az.backwards
if done.contains(where: { $0 == "\(len)\(az)" || $0 == "\(len)\(za)" })
|| lenSet[za] == nil { continue }
done += ["\(len)\(az)", "\(len)\(za)"]
group.addTask {
let lh = lenSet[az]!
let rh = lenSet[za]!
let f = await self.searchProduct(lh: lh, rh: rh)
return f
}
}
}
}
for await result in group {
results += result
}
return results
}
return allResults
}
 
func searchProduct(lh: [String], rh: [String]) async -> [String] {
var found: [String] = []
for ( s1, s2 ) in product(lh, rh) {
if s1 == s2 { continue } // palindrome
if s1 == String(s2.reversed())
&& !found.contains(where: {$0 == s2 || $0 == s1}) {
found.append(s1)
}
}
return found
}
}
</syntaxhighlight>
Usage:
<syntaxhighlight lang="swift">
import Foundation
import ArgumentParser
 
@main
struct AnadromeFinder: AsyncParsableCommand {
@Argument(help: "File containing words", transform: URL.init(fileURLWithPath:))
var file: URL
 
@Option(name: .shortAndLong)
var minLen: Int = 6
 
@Flag(name: .shortAndLong)
var verbose = false
 
mutating func run() async throws {
let indexSet = AnadromeIndex()
var start = Date()
let (lines, words) = await indexSet.loadFile(fileURL: file, minLen: minLen)
if verbose {
printStderr("\(lines.formatted()) lines read,",
"\(words.formatted()) hashed in",
"\(since(start).formatted()) sec")
}
start = Date()
let found = await indexSet.findAnadromes()
if verbose {
printStderr("\(found.count.formatted()) matches found in",
"\(since(start)) seconds")
}
// Print results
for e in found.sorted(by: {a, b in
if a.count == b.count { return a < b }
return a.count < b.count
}) {
print(e, e.backwards)
}
}
func since(_ dt: Date) -> TimeInterval {
return Date().timeIntervalSince(dt)
}
func printStderr(
_ items: Any...,
separator: String = " ",
terminator: String = "\n"
) {
let output = items
.map { String(describing: $0) }
.joined(separator: separator) + terminator
FileHandle.standardError.write(output.data(using: .utf8)!)
}
}
</syntaxhighlight>
Output:
<syntaxhighlight lang="sh">
% ./Anadromes --verbose /Users/username/Downloads/words.txt
466,551 lines read, 427,054 hashed in 5.52172 sec
83 matches found in 7.691561937332153 seconds
abrood doorba
agenes senega
amunam manuma
animal lamina
animes semina
bruted deturb
darter retrad
decart traced
decurt truced
deflow wolfed
degami imaged
denier reined
denies seined
depots stoped
derats stared
dessus sussed
dewans snawed
dialer relaid
diaper repaid
dibrom morbid
dorter retrod
drawer reward
elides sedile
elutes setule
enserf fresne
ergate etagre
eviler relive
gangan nagnag
gawgaw wagwag
golfer reflog
hallan nallah
lemmas sammel
lesson nossel
liever reveil
looter retool
manitu utinam
mooder redoom
nedder redden
pupils slipup
rebuts stuber
recaps spacer
recart tracer
redips spider
reflow wolfer
reives sevier
reknit tinker
remeet teemer
repins sniper
report troper
repots stoper
retros sorter
scares seracs
secret terces
selahs shales
serves sevres
sinnet tennis
skeets steeks
sleeps speels
sleets steels
sloops spools
snoops spoons
spirts strips
sports strops
sprits stirps
struts sturts
territ tirret
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
deviler relived
gateman nametag
leveler relevel
pat-pat tap-tap
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
desserts stressed
dioramas samaroid
redrawer rewarder
</syntaxhighlight>
 
=={{header|Wren}}==
{{libheader|Wren-sort}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="ecmascriptwren">import "io" for File
import "./sort" for Sort, Find
import "./fmt" for Fmt
871

edits