Worthwhile task shaving

Revision as of 19:25, 5 May 2022 by Depperm (talk | contribs)

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).

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.

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

Vlang

Translation of: Wren

<lang vlang>import math const (

   shaved = [1, 5, 30, 60, 300, 1800, 3600, 21600, 86400] // time shaved off in seconds
   columns = ["1 SECOND", "5 SECONDS", "30 SECONDS", "1 MINUTE", "5 MINUTES",
                  "30 MINUTES", "1 HOUR", "6 HOURS", "1 DAY"]
   diy = 365.25
   minute = 60
   hour = minute * 60
   day = hour * 24
   week = day * 7
   month = day * diy / 12
   year = day * diy
   freq = [50 * diy, 5 * diy, diy, diy/7, 12, 1] // frequency per year
   mult = 5 // multiplier for table

)

fn fmt_time(t f64, interval string) {

   f := int(math.floor(t))
   mut s := interval
   if f>1 {
       s = '${interval}S'
   }
   print(' ${f:-2} ${s:-9}')

}

fn main(){

   title := 'HOW OFTEN YOU DO THE TASK'
   println("${title:58}")
   println('SHAVED OFF   | 50/DAY       5/DAY        DAILY        WEEKLY       MONTHLY      YEARLY')
   println([]string{init:'-',len:93}.join())
   for y in 0..columns.len {
   print('${columns[y]:-12} |')
   for x in 0..6 {
      t := freq[x] * shaved[y] * mult
      if t < minute {
           fmt_time(t, "SECOND")
      } else if t < hour {
           fmt_time(t/minute, "MINUTE")
      } else if t < day {
           fmt_time(t/hour, "HOUR")
      } else if t < 14 * day {
           fmt_time(t/day, "DAY")
      } else if t < 9 * week {
           fmt_time(t/week, "WEEK")
      } else if t < year {
           fmt_time(t/month, "MONTH")
      } else {
           print('             ')
      }
   }
   println()
   }

}</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  

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---