You can use recursion in real life!

They make you learn recursion during first-year intro to programming courses. Some people get fed up at that point and drop out to become car salesmen or Liberal Arts majors instead. Some people never quite get it, but bodge their way through another four years of school anyway. For those who do get it, there’s a neat “aha!” moment where the entire world suddenly makes perfect sense, and it feels like the future of your brilliant programming career, and all the wealth that goes with it, is spread out before you. “The function calls itself! Of course! That’s what they were saying all along!”

My pseudocode

My pseudocode

Then they pile another dumptruck of homework on you and you forget about it for a while. Because honestly, you don’t use recursion that often in everyday programming. It’s a neat trick, but very rarely the most efficient solution to a problem. It’s also very easy to write a recursive function that goes out of control and brings down your entire office’s network and causes your boss to come your desk and raise his eyebrows at you.

So this week when I found a legitimate excuse to use recursion at work, I was pretty excited.

The Goal

We have a mailing list about real estate. Real estate agents write down some stuff about a property listing, give the date of an open house, upload some pics, and pay 70 bucks for us to send the email to everyone who is subscribed to the list. If you want to buy real estate in Victoria, it comes in handy, I guess.

The only thing is, each email has to be approved by our secretary before it goes out. Also, since we don’t want to spam the mailing list, we have to space them out a little, so the system isn’t fully automated. Nevertheless, when a customer asks for an email to be sent out on Monday morning, they expect it to be done or get an explanation.

The secretary needs some time in the morning to approve the scheduled emails and request changes if needed, so the following rules exist for the datepicker:

  • If it’s a weekend, the date is unavailable.
  • If it’s a holiday, the date is unavailable.
  • Today is unavailable.
  • If it’s after 1pm, tomorrow is unavailable.
  • If it’s after 1pm and tomorrow is a holiday or a weekend, the first business day following the holiday or weekend is unavailable.

Stop here and code up your own solution according to the following spec.

/**
* Takes a Unix timestamp. Returns the
* Unix timestamp of the next available day.
*
* params: int $today
* returns: int $nextAvail
**/

function nextAvail($today) {
    return $nextAvail;
}

Okay, you ready? I look forward to being made a fool of by all the people who came up with better solutions than mine. Nevertheless, here’s my answer, which took a full work day to hammer out but made me very happy.

First, I’ve got three helper functions: one returns weekend dates. One returns holiday dates. And one returns the day after a given date.

Here’s the code.

Solution

I only brought down our office network once in the writing of this class.

The function is called like this:

$this->nextAvail(time());

$nextAvailableDay = $this->nextAvail;

You pass nextAvail() the current timestamp. It checks whether that time falls on an unavailable day. If it does, the time is incremented by one day by calling $this->tomorrow($today); then nextAvail() is called again with the incremented timestamp.

And so on, until we find a day that works.

 

***Edit: Since this is somehow one of my highest ranking posts, I might as well plug my company: Radar Hill Web Design in Victoria, BC.

Tags: , , , , , , , , , ,

10 comments

  1. You are amazing.

  2. I took the challenge

    I don’t think it is a “better” solution, but takes advantage of ruby expressivenes, it’s under 50 lines, needs no recursion (no pobrem with it, just didn’t need it), and patches native Time class.

    class Time
    Days = Day = 24 * 60 * 60
    FixHolidays = Hash.new([]).merge(1 => [1,6], 5 => [1], 10 => [12], 12 => [25])

    def self.holidays
    @@holidays ||= Hash.new(Hash.new([])) # Year Specific holidays { 2013 => { 1 => [7,8]} }
    end

    # Add a year spefic holiday: Time.add_holiday 2014,2,10
    def self.add_holiday(hyear, hmonth, hday)
    holidays[hyear][hmonth] |= [hday]
    end

    def holiday?
    FixHolidays[month].include?(day) || Time.holidays[year][month].include?(day)
    end

    def weekend?
    saturday? || sunday? # wday > 5
    end

    # If it’s a weekend, the date is unavailable.
    # If it’s a holiday, the date is unavailable.
    def available?
    !weekend? && !holiday? && !today?
    end

    def late?
    hour >= 13
    end

    def today?
    (self – Time.now).abs Time.add_holiday 2014,2,10 # to make next monday holiday
    => [10]
    > now = Time.now
    => 2014-02-09 00:37:09 +0100
    > Time.available_after now
    => 2014-02-12 00:37:09 +0100

    I hope you like it and maybe consider trying Ruby on Rails, which you will find very similar to CakePHP.

    Regards

  3. In case you want to actually be able to read previous comment:

    https://gist.github.com/oinak/8892108

  4. To go along with the post theme I updated the gist with a recursive version and benchmark comparison of the two. Same url

  5. As I have learned recently in one of my own programs: You should never use + 86400 to calculate the next day.
    It will not work on days on which a time correction due to Daylight Saving Times occur.

  6. In a more functional language with lazieness, I would do it like this:
    - create a lazy seq of all days after a given day
    - filter the available days (no weekends, no holidays)
    - if it’s before 1pm take the first of this seq, otherwise take the second

    for my solution in clojure using clj-time look here:
    https://gist.github.com/IamDrowsy/1757211fbaeab71d80f5
    (the holiday predicate does nothing, but it’s about the concept)

    I think your recuring solution isn’t that different, but lazy seqs and functions acting on it like map/filter/remove is more abstract und concise.

  7. For the reason that the admin of this site is working, no doubt very shortly
    it will be famous, due to its quality contents.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>