RosettaCodeData/Task/Determine-if-a-string-has-all-the-same-characters/Python/determine-if-a-string-has-all-the-same-characters-1.py
2023-07-01 13:44:08 -04:00

100 lines
2.6 KiB
Python

'''Determine if a string has all the same characters'''
from itertools import groupby
# firstDifferingCharLR :: String -> Either String Dict
def firstDifferingCharLR(s):
'''Either a message reporting that no character changes were
seen, or a dictionary with details of the first character
(if any) that differs from that at the head of the string.
'''
def details(xs):
c = xs[1][0]
return {
'char': repr(c),
'hex': hex(ord(c)),
'index': s.index(c),
'total': len(s)
}
xs = list(groupby(s))
return Right(details(xs)) if 1 < len(xs) else (
Left('Total length ' + str(len(s)) + ' - No character changes.')
)
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Test of 7 strings'''
print(fTable('First, if any, points of difference:\n')(repr)(
either(identity)(
lambda dct: dct['char'] + ' (' + dct['hex'] +
') at character ' + str(1 + dct['index']) +
' of ' + str(dct['total']) + '.'
)
)(firstDifferingCharLR)([
'',
' ',
'2',
'333',
'.55',
'tttTTT',
'4444 444'
]))
# GENERIC -------------------------------------------------
# either :: (a -> c) -> (b -> c) -> Either a b -> c
def either(fl):
'''The application of fl to e if e is a Left value,
or the application of fr to e if e is a Right value.
'''
return lambda fr: lambda e: fl(e['Left']) if (
None is e['Right']
) else fr(e['Right'])
# identity :: a -> a
def identity(x):
'''The identity function.'''
return x
# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function -> fx display function ->
f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# Left :: a -> Either a b
def Left(x):
'''Constructor for an empty Either (option type) value
with an associated string.
'''
return {'type': 'Either', 'Right': None, 'Left': x}
# Right :: b -> Either a b
def Right(x):
'''Constructor for a populated Either (option type) value'''
return {'type': 'Either', 'Left': None, 'Right': x}
# MAIN ---
if __name__ == '__main__':
main()