Esthetic numbers: Difference between revisions

Content added Content deleted
m (Forth - eliminate code duplication)
(→‎{{header|Python}}: Added a functionally composed variant.)
Line 4,629: Line 4,629:


=={{header|Python}}==
=={{header|Python}}==
===Python :: Procedural===

{{works with|Python|3.9.5}}
{{works with|Python|3.9.5}}
<lang python>from collections import deque
<lang python>from collections import deque
Line 4,719: Line 4,719:


</lang>
</lang>

{{out}}
{{out}}
<pre>
<pre>======
======
Task 2
Task 2
======
======
Line 4,833: Line 4,831:
123434345, 123434543, 123434545, 123434565, 123434567, 123454321, 123454323,
123434345, 123434543, 123434545, 123434565, 123434567, 123454321, 123454323,
123454343, 123454345, 123454543, 123454545, 123454565, 123454567, 123456543,
123454343, 123454345, 123454543, 123454545, 123454565, 123454567, 123456543,
123456545, 123456565, 123456567, 123456765, 123456767, 123456787, 123456789
123456545, 123456565, 123456567, 123456765, 123456767, 123456787, 123456789</pre>

</pre>

===Python :: Functional===
{{Trans|Haskell}}
<lang python>'''Esthetic numbers'''

from functools import reduce
from itertools import (
accumulate, chain, count, dropwhile,
islice, product, takewhile
)
from operator import add
from string import digits, ascii_lowercase


# estheticNumbersInBase :: Int -> [Int]
def estheticNumbersInBase(b):
'''Infinite stream of numbers which are
esthetic in a given base.
'''
return concatMap(
compose(
lambda deltas: concatMap(
lambda headDigit: concatMap(
compose(
fromBaseDigits(b),
scanl(add)(headDigit)
)
)(deltas)
)(range(1, b)),
replicateList([-1, 1])
)
)(count(0))


# ------------------------ TESTS -------------------------
def main():
'''Specified tests'''
def samples(b):
i, j = b * 4, b * 6
return '\n'.join([
f'Esthetics [{i}..{j}] for base {b}:',
' '.join([
showInBase(b)(n) for n in compose(
drop(i - 1), take(j)
)(
estheticNumbersInBase(b)
)
])
])

def takeInRange(a, b):
return compose(
dropWhile(lambda x: x < a),
takeWhile(lambda x: x <= b)
)

print(
'\n\n'.join([
samples(b) for b in range(2, 1 + 16)
])
)
for (lo, hi) in [(1000, 9999), (100_000_000, 130_000_000)]:
print(f'\nBase 10 Esthetics in range [{lo}..{hi}]:')
print(unwords(
str(x) for x in takeInRange(lo, hi)(
estheticNumbersInBase(10)
)
))


# ------------------- BASES AND DIGITS -------------------

# fromBaseDigits :: Int -> [Int] -> [Int]
def fromBaseDigits(b):
'''An empty list if any digits are out of range for
the base. Otherwise a list containing an integer.
'''
def go(digitList):
maybeNum = reduce(
lambda r, d: None if r is None or (
0 > d or d >= b
) else r * b + d,
digitList, 0
)
return [] if None is maybeNum else [maybeNum]
return go


# toBaseDigits :: Int -> Int -> [Int]
def toBaseDigits(b):
'''A list of the digits of n in base b.
'''
def f(x):
return None if 0 == x else (
divmod(x, b)[::-1]
)
return lambda n: list(reversed(unfoldr(f)(n)))


# showInBase :: Int -> Int -> String
def showInBase(b):
'''String representation of n in base b.
'''
charSet = digits + ascii_lowercase
return lambda n: ''.join([
charSet[i] for i in toBaseDigits(b)(n)
])


# ----------------------- GENERIC ------------------------

# compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
def go(f, g):
def fg(x):
return f(g(x))
return fg
return reduce(go, fs, lambda x: x)


# 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


# drop :: Int -> [a] -> [a]
# drop :: Int -> String -> String
def drop(n):
'''The sublist of xs beginning at
(zero-based) index n.
'''
def go(xs):
if isinstance(xs, (list, tuple, str)):
return xs[n:]
else:
take(n)(xs)
return xs
return go


# dropWhile :: (a -> Bool) -> [a] -> [a]
# dropWhile :: (Char -> Bool) -> String -> String
def dropWhile(p):
'''The suffix remainining after takeWhile p xs.
'''
return lambda xs: list(
dropwhile(p, xs)
)


# replicateList :: [a] -> Int -> [[a]]
def replicateList(xs):
'''All distinct lists of length n that
consist of elements drawn from xs.
'''
def rep(n):
def go(x):
return [[]] if 1 > x else [
([a] + b) for (a, b) in product(
xs, go(x - 1)
)
]
return go(n)
return rep


# scanl :: (b -> a -> b) -> b -> [a] -> [b]
def scanl(f):
'''scanl is like reduce, but defines a succession of
intermediate values, building from the left.
'''
def go(a):
def g(xs):
return accumulate(chain([a], xs), f)
return g
return go


# take :: Int -> [a] -> [a]
# take :: Int -> String -> String
def take(n):
'''The prefix of xs of length n,
or xs itself if n > length xs.
'''
def go(xs):
return list(islice(xs, n))
return go


# takeWhile :: (a -> Bool) -> [a] -> [a]
# takeWhile :: (Char -> Bool) -> String -> String
def takeWhile(p):
'''The longest (possibly empty) prefix of xs
in which all elements satisfy p.
'''
return lambda xs: list(
takewhile(p, xs)
)


# unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
def unfoldr(f):
'''Dual to reduce or foldr.
Where catamorphism reduces a list to a summary value,
the anamorphic unfoldr builds a list from a seed value.
As long as f returns (a, b) a is prepended to the list,
and the residual b is used as the argument for the next
application of f.
When f returns None, the completed list is returned.
'''
def go(v):
xr = v, v
xs = []
while True:
xr = f(xr[1])
if None is not xr:
xs.append(xr[0])
else:
return xs
return go


# unwords :: [String] -> String
def unwords(xs):
'''A space-separated string derived
from a list of words.
'''
return ' '.join(xs)


# MAIN ---
if __name__ == '__main__':
main()</lang>
{{Out}}
<pre>Esthetics [8..12] for base 2:
10101010 101010101 1010101010 10101010101 101010101010

Esthetics [12..18] for base 3:
1210 1212 2101 2121 10101 10121 12101

Esthetics [16..24] for base 4:
323 1010 1012 1210 1212 1232 2101 2121 2123

Esthetics [20..30] for base 5:
323 343 432 434 1010 1012 1210 1212 1232 1234 2101

Esthetics [24..36] for base 6:
343 345 432 434 454 543 545 1010 1012 1210 1212 1232 1234

Esthetics [28..42] for base 7:
345 432 434 454 456 543 545 565 654 656 1010 1012 1210 1212 1232

Esthetics [32..48] for base 8:
432 434 454 456 543 545 565 567 654 656 676 765 767 1010 1012 1210 1212

Esthetics [36..54] for base 9:
434 454 456 543 545 565 567 654 656 676 678 765 767 787 876 878 1010 1012 1210

Esthetics [40..60] for base 10:
454 456 543 545 565 567 654 656 676 678 765 767 787 789 876 878 898 987 989 1010 1012

Esthetics [44..66] for base 11:
456 543 545 565 567 654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 a98 a9a 1010

Esthetics [48..72] for base 12:
543 545 565 567 654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 9ab a98 a9a aba ba9 bab

Esthetics [52..78] for base 13:
545 565 567 654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 9ab a98 a9a aba abc ba9 bab bcb cba

Esthetics [56..84] for base 14:
565 567 654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 9ab a98 a9a aba abc ba9 bab bcb bcd cba cbc cdc

Esthetics [60..90] for base 15:
567 654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 9ab a98 a9a aba abc ba9 bab bcb bcd cba cbc cdc cde dcb dcd

Esthetics [64..96] for base 16:
654 656 676 678 765 767 787 789 876 878 898 89a 987 989 9a9 9ab a98 a9a aba abc ba9 bab bcb bcd cba cbc cdc cde dcb dcd ded def edc

Base 10 Esthetics in range [1000..9999]:
1010 1012 1210 1212 1232 1234 2101 2121 2123 2321 2323 2343 2345 3210 3212 3232 3234 3432 3434 3454 3456 4321 4323 4343 4345 4543 4545 4565 4567 5432 5434 5454 5456 5654 5656 5676 5678 6543 6545 6565 6567 6765 6767 6787 6789 7654 7656 7676 7678 7876 7878 7898 8765 8767 8787 8789 8987 8989 9876 9878 9898

Base 10 Esthetics in range [100000000..130000000]:
101010101 101010121 101010123 101012101 101012121 101012123 101012321 101012323 101012343 101012345 101210101 101210121 101210123 101212101 101212121 101212123 101212321 101212323 101212343 101212345 101232101 101232121 101232123 101232321 101232323 101232343 101232345 101234321 101234323 101234343 101234345 101234543 101234545 101234565 101234567 121010101 121010121 121010123 121012101 121012121 121012123 121012321 121012323 121012343 121012345 121210101 121210121 121210123 121212101 121212121 121212123 121212321 121212323 121212343 121212345 121232101 121232121 121232123 121232321 121232323 121232343 121232345 121234321 121234323 121234343 121234345 121234543 121234545 121234565 121234567 123210101 123210121 123210123 123212101 123212121 123212123 123212321 123212323 123212343 123212345 123232101 123232121 123232123 123232321 123232323 123232343 123232345 123234321 123234323 123234343 123234345 123234543 123234545 123234565 123234567 123432101 123432121 123432123 123432321 123432323 123432343 123432345 123434321 123434323 123434343 123434345 123434543 123434545 123434565 123434567 123454321 123454323 123454343 123454345 123454543 123454545 123454565 123454567 123456543 123456545 123456565 123456567 123456765 123456767 123456787 123456789</pre>


=={{header|Raku}}==
=={{header|Raku}}==