How to resolve the algorithm Teacup rim text step by step in the Python programming language

Published on 12 May 2024 09:40 PM

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 function go.
  • go groups words in the given word list into sets of anagrams, with each set containing at least n 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 the isCircular function).

3. isCircular:

  • This function takes a set of words lexicon and a word w as arguments.
  • It checks if all rotations of w are found in lexicon.
  • If so, it returns True, indicating that w 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 list xs.

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 function f.

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 accumulator acc using the function f.

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 value x and returns a list of the results.

13. until:

  • This generic function repeatedly applies a function f until a predicate p 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