RosettaCodeData/Task/Sum-digits-of-an-integer/Python/sum-digits-of-an-integer-4.py
2020-02-17 23:21:07 -08:00

111 lines
2.8 KiB
Python

'''Sum digits of an integer'''
from functools import reduce
# digitSum :: Int -> Int -> Int
def digitSum(base):
'''The sum of the digits of a
natural number in a given base.
'''
return lambda n: reduce(
lambda a, x: a + digitToInt(x),
showIntAtBase(base)(digitChar)(n)(''),
0
)
# --------------------------TEST---------------------------
# main :: IO ()
def main():
'''Digit sums of numbers in bases 10 and 16:'''
print(
fTable(main.__doc__)(
lambda nb: showIntAtBase(nb[0])(
digitChar
)(nb[1])(' in base ') + str(nb[0])
)(repr)(
uncurry(digitSum)
)([(10, 1), (10, 10), (16, 0xfe), (16, 0xf0e)])
)
# -------------------------DISPLAY-------------------------
# 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-------------------------
# digitChar :: Int to Char
def digitChar(n):
'''A digit char for integers drawn from [0..15]'''
return ' ' if 0 > n or 15 < n else '0123456789abcdef'[n]
# digitToInt :: Char -> Int
def digitToInt(c):
'''The integer value of any digit character
drawn from the 0-9, A-F or a-f ranges.
'''
oc = ord(c)
if 48 > oc or 102 < oc:
return None
else:
dec = oc - 48 # ord('0')
hexu = oc - 65 # ord('A')
hexl = oc - 97 # ord('a')
return dec if 9 >= dec else (
10 + hexu if 0 <= hexu <= 5 else (
10 + hexl if 0 <= hexl <= 5 else None
)
)
# showIntAtBase :: Int -> (Int -> String) -> Int -> String -> String
def showIntAtBase(base):
'''String representation of an integer in a given base,
using a supplied function for the string representation
of digits.
'''
def wrap(toChr, n, rs):
def go(nd, r):
n, d = nd
r_ = toChr(d) + r
return go(divmod(n, base), r_) if 0 != n else r_
return 'unsupported base' if 1 >= base else (
'negative number' if 0 > n else (
go(divmod(n, base), rs))
)
return lambda toChr: lambda n: lambda rs: (
wrap(toChr, n, rs)
)
# uncurry :: (a -> b -> c) -> ((a, b) -> c)
def uncurry(f):
'''A function over a tuple,
derived from a curried function.
'''
return lambda tpl: f(tpl[0])(tpl[1])
# MAIN ---
if __name__ == '__main__':
main()