How to resolve the algorithm Teacup rim text step by step in the Python programming language
How to resolve the algorithm Teacup rim text step by step in the Python programming language
Table of Contents
Problem Statement
On a set of coasters we have, there's a picture of a teacup. On the rim of the teacup the word TEA appears a number of times separated by bullet characters (•). It occurred to me that if the bullet were removed and the words run together, you could start at any letter and still end up with a meaningful three-letter word. So start at the T and read TEA. Start at the E and read EAT, or start at the A and read ATE. That got me thinking that maybe there are other words that could be used rather that TEA. And that's just English. What about Italian or Greek or ... um ... Telugu. For English, we will use the unixdict (now) located at: unixdict.txt. (This will maintain continuity with other Rosetta Code tasks that also use it.)
Search for a set of words that could be printed around the edge of a teacup. The words in each set are to be of the same length, that length being greater than two (thus precluding AH and HA, for example.) Having listed a set, for example [ate tea eat], refrain from displaying permutations of that set, e.g.: [eat tea ate] etc. The words should also be made of more than one letter (thus precluding III and OOO etc.) The relationship between these words is (using ATE as an example) that the first letter of the first becomes the last letter of the second. The first letter of the second becomes the last letter of the third. So ATE becomes TEA and TEA becomes EAT. All of the possible permutations, using this particular permutation technique, must be words in the list. The set you generate for ATE will never included the word ETA as that cannot be reached via the first-to-last movement method. Display one line for each set of teacup rim words.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Teacup rim text step by step in the Python programming language
Overview
This Python program identifies circular anagram groups within a word list, where a circular anagram group consists of words that are anagrams of each other and can be arranged in a loop such that the last letter of one word is the first letter of the next.
Detailed Explanation
1. anagrams
:
- This function takes an integer
n
as an argument and returns another functiongo
. go
groups words in the given word list into sets of anagrams, with each set containing at leastn
words.- It does this by sorting the words alphabetically and grouping them based on their sorted versions.
2. circularGroup
:
- This function takes a list of anagrams
ws
as an argument. - It checks if there is at least one circular subgroup within
ws
. - It does this by rotating each word in
ws
and checking if all rotations are present in the word list (using theisCircular
function).
3. isCircular
:
- This function takes a set of words
lexicon
and a wordw
as arguments. - It checks if all rotations of
w
are found inlexicon
. - If so, it returns
True
, indicating thatw
is part of a circular subgroup.
4. allRotations
:
- This function takes a string
w
as an argument and returns a list of all its rotations. - It does this by repeatedly rotating
w
by one character.
5. concatMap
:
- This is a generic function that combines the results of mapping a function
f
over a listxs
.
6. fst
and snd
:
- These are helper functions that extract the first and second elements of a pair, respectively.
7. groupBy
:
- This generic function groups elements of a list
xs
by their keys, which are determined by the functionf
.
8. lines
:
- This helper function splits a newline-delimited string into a list of strings.
9. mapAccumL
:
- This is a generic function that combines mapping and folding a list
xs
with an initial accumulatoracc
using the functionf
.
10. readFile
:
- This function reads the contents of a file and returns a string.
11. rotated
:
- This function rotates a string
s
by one character to the right.
12. takeIterate
:
- This generic function applies a function
f
multiple times to a given valuex
and returns a list of the results.
13. until
:
- This generic function repeatedly applies a function
f
until a predicatep
holds true.
Main Function:
- The
main
function reads words from a file and groups them into circular anagram groups. - It prints these groups as strings, where each group is represented as a sequence of words with arrows joining them (e.g., "word1 -> word2 -> word3").
Source code in the python programming language
'''Teacup rim text'''
from itertools import chain, groupby
from os.path import expanduser
from functools import reduce
# main :: IO ()
def main():
'''Circular anagram groups, of more than one word,
and containing words of length > 2, found in:
https://www.mit.edu/~ecprice/wordlist.10000
'''
print('\n'.join(
concatMap(circularGroup)(
anagrams(3)(
# Reading from a local copy.
lines(readFile('~/mitWords.txt'))
)
)
))
# anagrams :: Int -> [String] -> [[String]]
def anagrams(n):
'''Groups of anagrams, of minimum group size n,
found in the given word list.
'''
def go(ws):
def f(xs):
return [
[snd(x) for x in xs]
] if n <= len(xs) >= len(xs[0][0]) else []
return concatMap(f)(groupBy(fst)(sorted(
[(''.join(sorted(w)), w) for w in ws],
key=fst
)))
return go
# circularGroup :: [String] -> [String]
def circularGroup(ws):
'''Either an empty list, or a list containing
a string showing any circular subset found in ws.
'''
lex = set(ws)
iLast = len(ws) - 1
# If the set contains one word that is circular,
# then it must contain all of them.
(i, blnCircular) = until(
lambda tpl: tpl[1] or (tpl[0] > iLast)
)(
lambda tpl: (1 + tpl[0], isCircular(lex)(ws[tpl[0]]))
)(
(0, False)
)
return [' -> '.join(allRotations(ws[i]))] if blnCircular else []
# isCircular :: Set String -> String -> Bool
def isCircular(lexicon):
'''True if all of a word's rotations
are found in the given lexicon.
'''
def go(w):
def f(tpl):
(i, _, x) = tpl
return (1 + i, x in lexicon, rotated(x))
iLast = len(w) - 1
return until(
lambda tpl: iLast < tpl[0] or (not tpl[1])
)(f)(
(0, True, rotated(w))
)[1]
return go
# allRotations :: String -> [String]
def allRotations(w):
'''All rotations of the string w.'''
return takeIterate(len(w) - 1)(
rotated
)(w)
# GENERIC -------------------------------------------------
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
'''A concatenated list over which a function has been mapped.
The list monad can be derived by using a function f which
wraps its output in a list,
(using an empty list to represent computational failure).
'''
def go(xs):
return chain.from_iterable(map(f, xs))
return go
# fst :: (a, b) -> a
def fst(tpl):
'''First member of a pair.'''
return tpl[0]
# groupBy :: (a -> b) -> [a] -> [[a]]
def groupBy(f):
'''The elements of xs grouped,
preserving order, by equality
in terms of the key function f.
'''
def go(xs):
return [
list(x[1]) for x in groupby(xs, key=f)
]
return go
# lines :: String -> [String]
def lines(s):
'''A list of strings,
(containing no newline characters)
derived from a single new-line delimited string.
'''
return s.splitlines()
# mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
def mapAccumL(f):
'''A tuple of an accumulation and a list derived by a
combined map and fold,
with accumulation from left to right.
'''
def go(a, x):
tpl = f(a[0], x)
return (tpl[0], a[1] + [tpl[1]])
return lambda acc: lambda xs: (
reduce(go, xs, (acc, []))
)
# readFile :: FilePath -> IO String
def readFile(fp):
'''The contents of any file at the path
derived by expanding any ~ in fp.
'''
with open(expanduser(fp), 'r', encoding='utf-8') as f:
return f.read()
# rotated :: String -> String
def rotated(s):
'''A string rotated 1 character to the right.'''
return s[1:] + s[0]
# snd :: (a, b) -> b
def snd(tpl):
'''Second member of a pair.'''
return tpl[1]
# takeIterate :: Int -> (a -> a) -> a -> [a]
def takeIterate(n):
'''Each value of n iterations of f
over a start value of x.
'''
def go(f):
def g(x):
def h(a, i):
v = f(a) if i else x
return (v, v)
return mapAccumL(h)(x)(
range(0, 1 + n)
)[1]
return g
return go
# until :: (a -> Bool) -> (a -> a) -> a -> a
def until(p):
'''The result of repeatedly applying f until p holds.
The initial seed value is x.
'''
def go(f):
def g(x):
v = x
while not p(v):
v = f(v)
return v
return g
return go
# MAIN ---
if __name__ == '__main__':
main()
You may also check:How to resolve the algorithm Write float arrays to a text file step by step in the C# programming language
You may also check:How to resolve the algorithm Hamming numbers step by step in the Icon and Unicon programming language
You may also check:How to resolve the algorithm Reverse a string step by step in the Self programming language
You may also check:How to resolve the algorithm Unbias a random generator step by step in the Quackery programming language
You may also check:How to resolve the algorithm Grayscale image step by step in the Go programming language