Determine if a string has all unique characters: Difference between revisions
Content added Content deleted
(→Python :: Functional: Added a variant in terms of sorting and grouping) |
|||
Line 859: | Line 859: | ||
=={{header|Python}}== |
=={{header|Python}}== |
||
===Functional=== |
===Functional=== |
||
Defined in terms of itertools.groupby: |
|||
<lang python>'''Determine if a string has all unique characters''' |
|||
from itertools import groupby |
|||
# duplicatedCharIndices :: String -> Maybe (Char, [Int]) |
|||
def duplicatedCharIndices(s): |
|||
'''Just the first duplicated character, and |
|||
the first two indices of its occurrence, |
|||
of Nothing if there are no duplications. |
|||
''' |
|||
def go(xs): |
|||
if 1 < len(xs): |
|||
duplications = list(filter(lambda kv: 1 < len(kv[1]), [ |
|||
(k, list(v)) for k, v in groupby( |
|||
sorted(xs, key=lambda ix: (ix[1], ix[0])), |
|||
key=snd |
|||
) |
|||
])) |
|||
if duplications: |
|||
dup = sorted(duplications, key=lambda kv: kv[1][0])[0] |
|||
return Just(([x[0] for x in dup[1]], dup[0])) |
|||
else: |
|||
return Nothing() |
|||
else: |
|||
return Nothing() |
|||
return go(list(enumerate(s))) |
|||
# TEST ---------------------------------------------------- |
|||
# main :: IO () |
|||
def main(): |
|||
'''Test over various strings.''' |
|||
def showSample(s): |
|||
return repr(s) + ' (' + str(len(s)) + ')' |
|||
def showDuplicate(ixc): |
|||
ix, c = ixc |
|||
return repr(c) + ( |
|||
' (' + hex(ord(c)) + ') at ' + repr(ix) |
|||
) |
|||
print( |
|||
fTable('First duplicated character, if any:')( |
|||
showSample |
|||
)(maybe('None')(showDuplicate))(duplicatedCharIndices)([ |
|||
'', '.', 'abcABC', 'XYZ ZYX', |
|||
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' |
|||
]) |
|||
) |
|||
# FORMATTING ---------------------------------------------- |
|||
# 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 |
|||
) |
|||
# GENERIC ------------------------------------------------- |
|||
# Just :: a -> Maybe a |
|||
def Just(x): |
|||
'''Constructor for an inhabited Maybe (option type) value. |
|||
Wrapper containing the result of a computation. |
|||
''' |
|||
return {'type': 'Maybe', 'Nothing': False, 'Just': x} |
|||
# Nothing :: Maybe a |
|||
def Nothing(): |
|||
'''Constructor for an empty Maybe (option type) value. |
|||
Empty wrapper returned where a computation is not possible. |
|||
''' |
|||
return {'type': 'Maybe', 'Nothing': True} |
|||
# maybe :: b -> (a -> b) -> Maybe a -> b |
|||
def maybe(v): |
|||
'''Either the default value v, if m is Nothing, |
|||
or the application of f to x, |
|||
where m is Just(x). |
|||
''' |
|||
return lambda f: lambda m: v if ( |
|||
None is m or m.get('Nothing') |
|||
) else f(m.get('Just')) |
|||
# snd :: (a, b) -> b |
|||
def snd(tpl): |
|||
'''Second member of a pair.''' |
|||
return tpl[1] |
|||
# MAIN --- |
|||
if __name__ == '__main__': |
|||
main()</lang> |
|||
{{Out}} |
|||
<pre>First duplicated character, if any: |
|||
'' (0) -> None |
|||
'.' (1) -> None |
|||
'abcABC' (6) -> None |
|||
'XYZ ZYX' (7) -> 'X' (0x58) at [0, 6] |
|||
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' (0x30) at [9, 24]</pre> |
|||
Or, as an alternative to sorting and grouping: |
|||
<lang python>'''Determine if a string has all unique characters''' |
<lang python>'''Determine if a string has all unique characters''' |
||