# Go deh!

Mainly Tech projects on Python and Electronic Design Automation.

# A little background

Stephan Fitzpatrick had a blog post entitled Recursion, what is it good for? this week where a friend introduced him to a problem and he decided to solve it in a tutorial style using recursion.
I liked the question and gave an iterative solution on reddit.
I had some time, and couldn't help myself from riffing on the question and expanded on the theme.
Later on, I started to get a niggle, and started to search Rosetta Code for that question, and sure enough, the task appears, from June 2015, where I had indeed posted two Python solutions!

# The Original question

On RC it begins:
Write a function or program which:
• takes a positive integer representing a duration in seconds as input (e.g., `100`), and
• returns a string which shows the same duration decomposed into weeks, days, hours, minutes, and seconds as detailed below (e.g., "`1 min, 40 sec`"). ...
Not knowing I had already solved it I posted this solution on reddit

## Reddit solution

In [ ]:
```def say_secs(seconds):
names = 'sec min hour day week'.split()
modulos = [60, 60, 24, 7]
units, remainder = [], seconds
for mod in modulos:
remainder, unit = divmod(remainder, mod)
units.append(unit)
units.append(remainder)
plurals = [('' if count == 1 else 's') for count in units]
out = ' '.join(f'{count} {name}{p}'
for count, name, p
in (zip(units[::-1], names[::-1], plurals[::-1]))
if count)
return out or '0 secs'

if __name__ == '__main__':
for s in [0, 100, 66400, 172801, 987987]:
print(f'{s:6} seconds is: {say_secs(s)}')
```

# What's it good for?

I looked at my reddit solution and thought "I could make that work for other units by modifying the `modulos` name appropriately".
A quick look on Wikipedia gave me info on Imperial units of measurement and I decided to add length in inches.

# Refactorings to add units of length

I was going to need to change the `names` and `modulos` variables for another system of units, that was obvious. Looking at the new names I had though made me realise that plurals can't always be made by simply adding an "s". Inch becomes inches; foot becomes feet, so I had to pull `names``modulos`, and `plurals`out of the function above.
This stage of the code, supporting two units of measurement became:
In [ ]:
```def say_secs(seconds):
names = 'sec min hour day week'.split()
plurals = [f'{n}s' for n in names]
modulos = [60, 60, 24, 7]  # next unit is x times this  for unit in names
return say_units(seconds, names, plurals, modulos)

def say_inches(inches, progression=False):
names = 'inch foot yard chain furlong mile'.split()
plurals = 'inches feet yards chains furlongs miles'.split()
modulos = [12, 3, 22, 10, 8]
return say_units(inches,  names, plurals, modulos)

def say_units(amount, names, plurals, modulos):
units, remainder = [], amount
for mod in modulos:
remainder, unit = divmod(remainder, mod)
units.append(unit)
units.append(remainder)
named = [(singular if count == 1 else plural) for
count, singular, plural in zip(units, names, plurals)]
out = ' '.join(f'{count} {name}'
for count, name
in (zip(units[::-1], named[::-1]))
if count)
return out or f'0 {plurals[0]}'

if __name__ == '__main__':
print('\n###')
for s in [0, 100, 66400, 172801, 987987]:
print(f'{s:6} seconds is: {say_secs(s)}')
print('\n###')
say_inches(0, progression=True)
for i in [0, 100, 66400, 172801, 987987]:
print(f'{i:6} inches are: {say_inches(i)}')
```

# Progressions

Too aid me I found I added the comment on the first assignment to `modulos` of
``````# next unit is x times this  for unit in names
``````
The above was to remind me of how each measure relates to the next. Thinking about it, I thought it would make a nice printout too so added it requirements.

I wanted to add imperial units of volume, pints and what not. I found some reference material here.
Hmm, one, two, many - that's my mantra for checking if I need to maybe create a function and loop over something if I am repeating myself.
I now have three units of measurements which would have three pretty similar sections of code in the `if __name__ == ...` block.
With the additional requirement of wanting to print the progressions" of the units I decided to Use a class. One instantiaon per unit of measurement; make it callable for it's main action.

# Result supporting three units of measurement and using classes

In [3]:
```class Say_unit():
def __init__(self, names, plurals, modulos):
self.names = names
self.plurals = plurals
self.modulos = modulos

def __call__(self, amount):
return self.say_units(amount)

def say_units(self, amount):
names, plurals, modulos = self.names, self.plurals, self.modulos
units, remainder = [], amount
for mod in modulos:
remainder, unit = divmod(remainder, mod)
units.append(unit)
units.append(remainder)
named = [(singular if count == 1 else plural) for
count, singular, plural in zip(units, names, plurals)]
out = ' '.join(f'{count} {name}'
for count, name
in (zip(units[::-1], named[::-1]))
if count)
return out or f'0 {plurals[0]}'

def print_progression(self):
for i, mod in enumerate(self.modulos):
print('  '* i + f'{mod} {self.plurals[i]} in 1 {self.names[i+1]}')
print()

def sample_unit_conversion(title, names, plurals, modulos):
print(f'\n### {title}')
unit_convertor = Say_unit(names, plurals, modulos)
unit_convertor.print_progression()
for units in [0, 100, 66400, 172801, 987987]:
print(f'{units:6} {plurals[0]} is: {unit_convertor(units)}')

if __name__ == '__main__':
title = 'Seconds of time'
names = 'sec min hour day week'.split()
plurals = [f'{n}s' for n in names]
modulos = [60, 60, 24, 7]
sample_unit_conversion(title, names, plurals, modulos)

title = 'Inches of length'
names = 'inch foot yard chain furlong mile'.split()
plurals = 'inches feet yards chains furlongs miles'.split()
modulos = [12, 3, 22, 10, 8]
sample_unit_conversion(title, names, plurals, modulos)

title = 'Fluid ounces of volume'
names = 'fluid ounce, gill, cup, pint, quart, gallon, peck'.split(', ')
plurals = [f'{n}s' for n in names]
modulos = [5, 2, 2, 2, 4, 2]
sample_unit_conversion(title, names, plurals, modulos)

```
```### Seconds of time
60 secs in 1 min
60 mins in 1 hour
24 hours in 1 day
7 days in 1 week

0 secs is: 0 secs
100 secs is: 1 min 40 secs
66400 secs is: 18 hours 26 mins 40 secs
172801 secs is: 2 days 1 sec
987987 secs is: 1 week 4 days 10 hours 26 mins 27 secs

### Inches of length
12 inches in 1 foot
3 feet in 1 yard
22 yards in 1 chain
10 chains in 1 furlong
8 furlongs in 1 mile

0 inches is: 0 inches
100 inches is: 2 yards 2 feet 4 inches
66400 inches is: 1 mile 3 chains 18 yards 1 foot 4 inches
172801 inches is: 2 miles 5 furlongs 8 chains 4 yards 1 inch
987987 inches is: 15 miles 4 furlongs 7 chains 10 yards 3 inches

### Fluid ounces of volume
5 fluid ounces in 1 gill
2 gills in 1 cup
2 cups in 1 pint
2 pints in 1 quart
4 quarts in 1 gallon
2 gallons in 1 peck

0 fluid ounces is: 0 fluid ounces
100 fluid ounces is: 2 quarts 1 pint
66400 fluid ounces is: 207 pecks 1 gallon
172801 fluid ounces is: 540 pecks 1 fluid ounce
987987 fluid ounces is: 3087 pecks 3 quarts 1 pint 1 gill 2 fluid ounces
```
We have a unit of volume that is two words `fluid ounce` so I had to make a slight change of its `name` string initial value and splitting. `Say_unit.print_progression` is new, and function `sample_unit_conversion` cuts down on the cut-n-paste coding.
END.
In [ ]:
` `

#### 1 comment:

Sis here.
Just started to do some programming myself.
Well laid out and the thought processes you went through make it very readable, even for a beginner.
Not able to follow all of it as yet, but I get the general jist.