Worthwhile task shaving

From Rosetta Code
Worthwhile task shaving is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Recreate https://xkcd.com/1205/ which shows a (humorous) table of how long you can work on making a routine task more efficient before spending more time than saved, for various s(h)avings against how often the task is run (over the course of five years).

There are of course several possible interpretations of "day" and "week" in this context. The Phix implementation assumes 8 hour days and 5 day weeks might be more realistic, whereas it seems the original author worked with 24 hour days and 7 day weeks, and, tbh, my interest is piqued to see what built-in facilities other languages might have for handling such non-standard terms, if any. Extra kudos awarded for getting into the mind of the original author and reproducing their results exactly (see talk page), or drumming up non-trivial (but still elegant) and potentially actually useful routines. This task can be made as trivial or as convoluted as you please, and should aim more for a little playfulness than rigid scientific accuracy.


Julia

Translation of: Perl

<lang julia>shaved = [1, 5, 30, 60, 300, 1800, 3600, 21600, 86400] columns = [" 1 Second", " 5 Seconds", "30 Seconds", " 1 Minute", " 5 Minutes", "30 Minutes", " 1 Hour", " 6 Hours", " 1 Day"] diy, minute, hour, day, week = 365.25, 60, 60 * 60, 60 * 60 * 24, 60 * 60 * 24 * 7 month, year = day * diy / 12, day * diy freq = [50 * diy, 5 * diy, diy, diy / 7, 12, 1]

fmt(t, interval) = rpad(lpad(Int(round(t)), 3) * " $interval" * (t > 1 ? "s" : ""), 15)

println(' '^34, "How Often You Do the Task\n") foreach(s -> print(rpad(s, 15)), ["Shaved-off |", " 50/Day", " 5/Day", " Daily", " Weekly", " Monthly", " Yearly"]) println("\n", '-'^100)

for y in 1:9

  row = lpad(columns[y] * " | ", 14)
  for x in 1:6
     t = freq[x] * shaved[y] * 5
     row *= t < minute ? fmt(t, "Second") : t < hour ? fmt(t / minute, "Minute") : t < day ? fmt(t / hour,   "Hour") :
        t < day * 14 ? fmt(t / day, "Day") : t < week * 9 ? fmt(t / week, "Week") : t < year ? fmt(t / month, "Month") : "   n/a         "
  end
  println(row)

end

</lang>

Output:
                                  How Often You Do the Task

Shaved-off  |   50/Day         5/Day          Daily          Weekly         Monthly        Yearly
----------------------------------------------------------------------------------------------------
   1 Second |   1 Days         3 Hours       30 Minutes      4 Minutes      1 Minute       5 Seconds
  5 Seconds |   5 Days        13 Hours        3 Hours       22 Minutes      5 Minutes     25 Seconds
 30 Seconds |   5 Weeks        3 Days        15 Hours        2 Hours       30 Minutes      2 Minutes
   1 Minute |   2 Months       6 Days         1 Days         4 Hours        1 Hour         5 Minutes
  5 Minutes |  10 Months       5 Weeks        6 Days        22 Hours        5 Hours       25 Minutes
 30 Minutes |    n/a           6 Months       5 Weeks        5 Days         1 Days         2 Hours
     1 Hour |    n/a            n/a           2 Months      11 Days         2 Days         5 Hours
    6 Hours |    n/a            n/a            n/a           2 Months       2 Weeks        1 Days
      1 Day |    n/a            n/a            n/a           9 Months       9 Weeks        5 Days


Perl

<lang perl>use strict; use warnings; use feature <say switch>; no warnings 'experimental::smartmatch';

use constant CW => '%-11s'; # set column width

  1. ( scale --> seconds ) (minutes) ( scale --> hours )

my @shaved = map { 60 * $_ } 1/60, 1/12, 1/2, 1, 5, 30, map { 60 * $_ } 1, 6, 24; my @columns = (' 1 Second', ' 5 Seconds', '30 Seconds', ' 1 Minute', ' 5 Minutes', '30 Minutes', ' 1 Hour', ' 6 Hours', ' 1 Day'); my $diy = 365.25; my @freq = ((map { $diy * $_ } 50, 5, 1, 1/7), 12, 1); my $week = 7 * (my $day = 24 * (my $hour = 60 * (my $minute = 60))); my $month = (my $year = $day * $diy) / 12; my $mult = 5;

sub fmt { my($t, $interval) = @_; sprintf CW.' ', (sprintf '%2d', int $t) . ' ' . $interval . ($t > 1 and 's') }

say ' ' x 34 . 'How Often You Do the Task'; say ; say sprintf CW.' | '.(' '.CW)x6, <Shaved-off 50/Day 5/Day Daily Weekly Monthly Yearly>; say ;

for my $y (0..8) {

  my $row = sprintf CW.' | ', $columns[$y];
  for my $x (0..5) {
     given ($freq[$x] * $shaved[$y] * $mult) {
        when ($_ < $minute) { $row .= fmt $_,         "Second" }
        when ($_ < $hour  ) { $row .= fmt $_/$minute, "Minute" }
        when ($_ < $day   ) { $row .= fmt $_/$hour,   "Hour"   }
        when ($_ < 14*$day) { $row .= fmt $_/$day,    "Day"    }
        when ($_ < 9*$week) { $row .= fmt $_/$week,   "Week"   }
        when ($_ < $year  ) { $row .= fmt $_/$month,  "Month"  }
        default             { $row .= ' ' . sprintf CW, ' '    }
     }
  }
  say $row;

}</lang>

Output:
                                  How Often You Do the Task

Shaved-off  |  50/Day      5/Day       Daily       Weekly      Monthly     Yearly

 1 Second   |  1 Days      2 Hours    30 Minutes   4 Minutes   1 Minute    5 Seconds
 5 Seconds  |  5 Days     12 Hours     2 Hours    21 Minutes   5 Minutes  25 Seconds
30 Seconds  |  4 Weeks     3 Days     15 Hours     2 Hours    30 Minutes   2 Minutes
 1 Minute   |  2 Months    6 Days      1 Days      4 Hours     1 Hour      5 Minutes
 5 Minutes  | 10 Months    4 Weeks     6 Days     21 Hours     5 Hours    25 Minutes
30 Minutes  |              6 Months    5 Weeks     5 Days      1 Days      2 Hours
 1 Hour     |                          2 Months   10 Days      2 Days      5 Hours
 6 Hours    |                                      2 Months    2 Weeks     1 Days
 1 Day      |                                      8 Months    8 Weeks     5 Days

Phix

with javascript_semantics
constant SEC = 1,
         MIN = 60,
         HOUR = 60*MIN,
         DAY = 8*HOUR,      -- (allow some sleepage)
         WEEK = 5*DAY,      -- (omit weekends)
         MONTH = 4*WEEK,
         YEAR = 12*MONTH,   -- (as 48 weeks/omit holidays)
         shavings = {1,5,30,MIN,5*MIN,30*MIN,HOUR,6*HOUR,DAY},
         frequencies = {{50,DAY},{5,DAY},{1,DAY},{1,WEEK},{1,MONTH},{1,YEAR}},
         roundto = {SEC, MIN, HOUR, DAY, WEEK, MONTH, YEAR},
         ts = {"sec", "min", "hour", "day", "week", "month", "year"}

function duration(atom a)
    string es
    for rdx=1 to length(roundto) do
        atom t = trunc(a/roundto[rdx])
        if rdx>1 and t<1 then exit end if
        es = sprintf("%d %s%s",{t,ts[rdx],iff(t=1?"":"s")})
    end for
    return es
end function

printf(1,"               50/day       5/day       daily      weekly     monthly      yearly\n")
for s=1 to length(shavings) do
    integer si = shavings[s]
    string line = sprintf("%10s ",duration(si))
    for f=1 to length(frequencies) do
        integer {per,slot} = frequencies[f]
        if si*per > slot then
            line &= sprintf("%10s  ","n/a")
        else
            atom shaving = (5*YEAR/slot * per) * si
            line &= sprintf("%10s  ",duration(shaving))
        end if
    end for
    printf(1,"%s\n",line)
end for
Output:

One outlier here is 1hr 5/day ==> 3 years vs original 10 months: as per notes above for 5/8ths the cutoff is indeed 3 years.
Note that the standard builtins such as elapsed() have no facilities for non-standard terms such as 8 hour working days.

               50/day       5/day       daily      weekly     monthly      yearly
     1 sec     2 days      1 hour     20 mins      4 mins       1 min      5 secs
    5 secs    2 weeks       1 day      1 hour     20 mins      5 mins     25 secs
   30 secs   3 months      1 week       1 day     2 hours     30 mins      2 mins
     1 min   6 months     2 weeks      2 days     4 hours      1 hour      5 mins
    5 mins    2 years    3 months     2 weeks      2 days     5 hours     25 mins
   30 mins        n/a      1 year    3 months     3 weeks      3 days     2 hours
    1 hour        n/a     3 years    7 months     1 month      1 week     5 hours
   6 hours        n/a         n/a     3 years    9 months    2 months      3 days
     1 day        n/a         n/a     5 years      1 year    3 months      1 week

Raku

Translation of: Wren

<lang perl6># 20220207 Raku programming solution

my \shaved = [1, 5, 30, 60, 300, 1800, 3600, 21600, 86400]; # time shaved off in seconds my \columns = [ "1 SECOND", "5 SECONDS", "30 SECONDS", "1 MINUTE", "5 MINUTES",

               "30 MINUTES", "1 HOUR", "6 HOURS", "1 DAY" ];

my \diy = 365.25; my \minute = 60; my \hour = minute * 60; my \day = hour * 24; my \week = day * 7; my \month = day * diy / 12; my \year = day * diy; my \freq = [50 * diy, 5 * diy, diy, diy/7, 12, 1]; # frequency per year my \mult = 5; # multiplier for table

sub fmtTime (\t, \interval) { printf "%-12s ", t.floor~" "~interval~(t == 1 ?? "" !! "S") }

say ' ' x 34~"HOW OFTEN YOU DO THE TASK"; printf("%-12s | %-12s %-12s %-12s %-12s %-12s %-12s\n",

  ["SHAVED OFF", "50/DAY", "5/DAY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY"]);

say '-' x 93;

for ^9 -> \y {

  printf "%-12s | ", columns[y];
  for ^6 -> \x {
     given my \t = freq[x] * shaved[y] * mult {
        when t < minute  { fmtTime t,        "SECOND" } 
        when t < hour    { fmtTime t/minute, "MINUTE" } 
        when t < day     { fmtTime t/hour,   "HOUR"   } 
        when t < 14*day  { fmtTime t/day,    "DAY"    } 
        when t < 9*week  { fmtTime t/week,   "WEEK"   }
        when t < year    { fmtTime t/month,  "MONTH"  }
        default          { print   '   N/A       '    }
     }
  }
  print "\n"

}</lang>

Output:
                                  HOW OFTEN YOU DO THE TASK
SHAVED OFF   | 50/DAY       5/DAY        DAILY        WEEKLY       MONTHLY      YEARLY
---------------------------------------------------------------------------------------------
1 SECOND     | 1 DAYS       2 HOURS      30 MINUTES   4 MINUTES    1 MINUTE     5 SECONDS
5 SECONDS    | 5 DAYS       12 HOURS     2 HOURS      21 MINUTES   5 MINUTES    25 SECONDS
30 SECONDS   | 4 WEEKS      3 DAYS       15 HOURS     2 HOURS      30 MINUTES   2 MINUTES
1 MINUTE     | 2 MONTHS     6 DAYS       1 DAYS       4 HOURS      1 HOUR       5 MINUTES
5 MINUTES    | 10 MONTHS    4 WEEKS      6 DAYS       21 HOURS     5 HOURS      25 MINUTES
30 MINUTES   |    N/A       6 MONTHS     5 WEEKS      5 DAYS       1 DAYS       2 HOURS
1 HOUR       |    N/A          N/A       2 MONTHS     10 DAYS      2 DAYS       5 HOURS
6 HOURS      |    N/A          N/A          N/A       2 MONTHS     2 WEEKS      1 DAYS
1 DAY        |    N/A          N/A          N/A       8 MONTHS     8 WEEKS      5 DAYS


Wren

Library: Wren-fmt

This is quite close to the original table but no cigar. <lang ecmascript>import "./fmt" for Fmt

var shaved = [1, 5, 30, 60, 300, 1800, 3600, 21600, 86400] // time shaved off in seconds var columns = ["1 SECOND", "5 SECONDS", "30 SECONDS", "1 MINUTE", "5 MINUTES",

              "30 MINUTES", "1 HOUR", "6 HOURS", "1 DAY"]

var diy = 365.25 var minute = 60 var hour = minute * 60 var day = hour * 24 var week = day * 7 var month = day * diy / 12 var year = day * diy

var freq = [50 * diy, 5 * diy, diy, diy/7, 12, 1] // frequency per year var mult = 5 // multiplier for table

var fmtTime = Fn.new { |t, interval|

  t = t.floor
  var pl = (t == 1) ? "" : "S"
  Fmt.write("$-12s ", t.toString + " " + interval + pl)

}

Fmt.print("$93m", "HOW OFTEN YOU DO THE TASK") Fmt.lprint("$-12s | $-12s $-12s $-12s $-12s $-12s $-12s", ["SHAVED OFF", "50/DAY", "5/DAY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY"]) System.print("-" * 93) for (y in 0..8) {

   Fmt.write("$-12s | ", columns[y])
   for (x in 0..5) {
       var t = freq[x] * shaved[y] * mult
       if (t < minute) {
            fmtTime.call(t, "SECOND")
       } else if (t < hour) {
            fmtTime.call(t/minute, "MINUTE")
       } else if (t < day) {
            fmtTime.call(t/hour, "HOUR")
       } else if (t < 14 * day) {
            fmtTime.call(t/day, "DAY")
       } else if (t < 9 * week) {
            fmtTime.call(t/week, "WEEK")
       } else if (t < year) {
            fmtTime.call(t/month, "MONTH")
       } else {
            System.write(" " * 13)
       }
   }
   System.print()

}</lang>

Output:
                                  HOW OFTEN YOU DO THE TASK                                  
SHAVED OFF   | 50/DAY       5/DAY        DAILY        WEEKLY       MONTHLY      YEARLY      
---------------------------------------------------------------------------------------------
1 SECOND     | 1 DAY        2 HOURS      30 MINUTES   4 MINUTES    1 MINUTE     5 SECONDS    
5 SECONDS    | 5 DAYS       12 HOURS     2 HOURS      21 MINUTES   5 MINUTES    25 SECONDS   
30 SECONDS   | 4 WEEKS      3 DAYS       15 HOURS     2 HOURS      30 MINUTES   2 MINUTES    
1 MINUTE     | 2 MONTHS     6 DAYS       1 DAY        4 HOURS      1 HOUR       5 MINUTES    
5 MINUTES    | 10 MONTHS    4 WEEKS      6 DAYS       21 HOURS     5 HOURS      25 MINUTES   
30 MINUTES   |              6 MONTHS     5 WEEKS      5 DAYS       1 DAY        2 HOURS      
1 HOUR       |                           2 MONTHS     10 DAYS      2 DAYS       5 HOURS      
6 HOURS      |                                        2 MONTHS     2 WEEKS      1 DAY        
1 DAY        |                                        8 MONTHS     8 WEEKS      5 DAYS       

Yabasic

Translation of: Phix

<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Worthwhile_task_shaving // by Galileo, 02/2022

SEC = 1 : MINU = 60 : HOUR = 60 * MINU DAY = 8 * HOUR : WEEK = 5 * DAY : MONTH = 4 * WEEK : YEAR = 12 * MONTH // (as 48 weeks/omit holidays) dim shavings$(1) : ls = token("1, 5, 30, MINU, 5 * MINU, 30 * MINU, HOUR, 6 * HOUR, DAY", shavings$(), ",") dim frequencies$(1) : lf = token("50, DAY, 5, DAY, 1, DAY, 1, WEEK, 1, MONTH, 1, YEAR", frequencies$(), ",") dim roundto$(1) : lr = token("SEC, MINU, HOUR, DAY, WEEK, MONTH, YEAR", roundto$(), ",") dim ts$(1) : lt = token("sec, min, hour, day, week, month, year", ts$(), ",")

sub format$(line$, n)

   return right$("                                                                                           " + line$, n)

end sub

sub duration$(a)

   local es$, rdx, t
   
   for rdx = 1 to lr
       t = int(a/eval(roundto$(rdx)))
       if rdx > 1 and t < 1 break
       es$ = str$(t) + " " + ts$(rdx) : if t > 1 es$ = es$ + "s" : es$ = es$
   next
   
   return es$

end sub

print " 50/day 5/day daily weekly monthly yearly\n"

for s = 1 to ls

   si = eval(shavings$(s))
   line$ = format$(duration$(si), 10) + "  "
   for f = 1 to lf step 2
       per = eval(frequencies$(f)) : slot = eval(frequencies$(f + 1))
       if si * per > slot then
           line$ = line$ + format$("n/a", 10) + "  "
       else
           shaving = (5 * YEAR / slot * per) * si
           line$ = line$ + format$(duration$(shaving), 10) + "  "
       end if
   next
   print line$

next</lang>

Output:
                50/day       5/day       daily      weekly     monthly      yearly

     1 sec     2  days     1  hour    20  mins     4  mins      1  min      5 secs
    5 secs    2  weeks      1  day     1  hour    20  mins     5  mins     25 secs
   30 secs   3  months     1  week      1  day    2  hours    30  mins     2  mins
    1  min   6  months    2  weeks     2  days    4  hours     1  hour     5  mins
   5  mins    2  years   3  months    2  weeks     2  days    5  hours    25  mins
  30  mins         n/a     1  year   3  months    3  weeks     3  days    2  hours
   1  hour         n/a    3  years   7  months    1  month     1  week    5  hours
  6  hours         n/a         n/a    3  years   9  months   2  months     3  days
    1  day         n/a         n/a    5  years     1  year   3  months     1  week
---Program done, press RETURN---