Alexis Rondeau

Newsfeed

Everything I publish, in the order I published it: dispatches from Twitter, videos from YouTube and notes from my vault, archived here in full — text, images, video and links — so they outlive the platforms.

857 dispatches since March 2014 · page 32 of 35

Understanding GaryVee and Julius Dein's Audience

The audience that GaryVee and Julius Dein share on Facebook and Instagram is… a mass-audience of 15M-20M active users per month. They’re mostly men (75%) between 18 and 34. Most of them are single and went to college. The biggest chunk of this audience lives in the US (20%).

Here’s a quick overview to give you a better sense:

Activity

This audience is significantly more active across the board than anyone else. On average, a GaryVee/Julius Dein fan wrote 14 comments, liked 30 posts and clicked 26 ads in the past month.

Who or what is relevant to this audience?

The brands that are highly relevant to this audience are very much personality-driven. As you can see here in this top 25 list, it’s all about people and their success/beauty/fun lifestyle:

A few things I noticed: Not many of the brands this audience also very much likes run ads. GaryVee, Jake Paul and Curtis Lepore are the only ones running full-blown campaigns. From the look of these ads, they’re running mostly on Instagram.

GaryVee’s most interesting campaign is all about teaching entrepreneurship to this younger audience. I think he’s honing in to this age-group very well with attention-grabbing along the lines of “your mom is holding you back.”

What to do with this information?

My first instinct here is that this audience is FAR too large to effectively address. Go much, much smaller and more specific.

This is more of a “generation” of mostly men than a type of person (or persona) that you can serve  – I would highly recommend narrowing your focus down to a smaller sub-niche (250K or smaller) whose pains you can understand deeply and serve better.

Experiment: Field-testing My "Dream/Plan/Execute" Application for Ease-of-Use and Productivity

Theory: If I use my Dream/Plan/Execute MVP, THEN I’ll get more done than using Trello

Method: Use MVP (Monday to Monday) for one week

Prediction: I’ll want to continue to use it onwards

Day 1 (Tuesday)

So far, so good. I had broken down my book down to the chapter level and I felt pretty good not having to think about what’s next, but just have the next task pop up automatically.

Day 2 (Wednesday):

I felt pretty productive today, wrote 4 chapters in the book, and feel, so far, pretty calm about progress. I added the progress-bar yesterday which I enjoy seeing increase.

Day 3 (Thursday):

Today feels really good, I also added “make your bed, first thing” to my morning routine which kicked off a nice flow. Already covered more than 9 points for my book (I consolidated a bunch of “chapters” into paragraphs.)

So far, today, I’ve gotten 19 high-value tasks done which is pretty neat. Oh, I added that count yesterday and I think it’s working well.

Day 4 - 7 (Friday and over the weekend)

I kept using it from Friday through Saturday. I took a break from working on Sunday.

Day 8 – Monday / JuDgEMenT DAy

Results

  • I used the app 6 out of 7 days
  • I’ve completed 112 small, but high-value tasks
  • So far I do not miss Trello.
  • I want continue to use it for another week.

Decision

Persevere! I’ll continue working on and with the application for another week.

Learnings

- What worked well:

  • Slicing down big tasks into tiny, tiny, tiny sub-tasks actually proved to work well for me. I was able to start and pick the smallest thing that usually takes about 5 minutes or less and get started instead of feeling overwhelmed.
  • The small “You finished X high-value tasks” message kept me motivated and excited
  • I feel like I’m making solid progress in key areas of my life
  • I like having the native app

- What was “Okay”:

  • The progress-bar is okay, but it gives me more value to look at today’s tasks
  • I upgraded to a maximum of 6 top-level goals and I’m curious to see if that helps
  • I’m not sure how to “plan for the week” with this interface
  • I’m not really using the “Plan” tab as much. Instead I do my planning on the goal-level
  • I’m unclear how “Meta” Personal Development is in regards to the app.

- What didn’t work so well:

  • I have had moments when I mixed planning and execution where it’s easy to feel overwhelmed.
  • I don’t yet love the “Goal” summary page (but it’s still helpful to have it)
  • I’ve mostly lost track of chores I have to do that live outside my goals
  • Today’s IPM felt messy and all over the place. There were timing factors, too.
  • When planning/adding tasks, the page-reload sucks when you’ve collapsed tasks before
  • It’s not good to represent recurring tasks/habit-forming activities

Related

Book Recommendation: "From Russia With Love" by Ian Fleming

HOLY SHIT. Oof. 

So, I completely forgot just how good this book was, back when I read it. I just finished “David & Goliath” and I felt like fiction and randomly picked up “From Russia With Love.” Many great things have already been said about it, so I will just give my 2 cent here. 

Imagine, this book sitting quietly in your lap. You pick it up. You draw it closer. You open it. And suddenly you’re pulled into a whirlwind of suspense, action and intrigue. Reading this book is like staring straight into the roaring engines of a Concorde. It’s loud. It’s violent. And it’s moving at 700 miles per hour. 

And you just. Can’t. Stop. Reading.

So, if you haven’t read it yet, you should. You can order it here. It’s worth every cent. Just be prepared: You won’t be able to put it down until you’re done with it.

PS: I’m reading so much because I set myself a new rule: No screens (Phone, iPad etc.) after 8pm. To improve my sleep. So, I’ve had much more time to spend reading than usual. It’s working out well so far. 

Productivity Experiment: My Dream/Plan/Execute Model in One App

I’m a huge procrastinator. Unless I set myself a goal, make a plan and have a clear, small next step, I’ll just waste my time.

So knowing that about myself, over the past years, I’ve experimented with different tools to manage my goals, plans and todos in different ways. The problem that I’ve run into over and over again was that all softwares usually just cover one part of my overall flow.

Currently my flow is :

  • I use Evernote to think through and write down my top-level goals
  • Then I copy over my top-level goals into Omni Outliner to break-down it into smaller, manageable pieces
  • Finally I copy over the tasks into Trello to create a backlog, often having to re-create the break-down by hand again.

Each of these softwares are great at what they do, please don’t get me wrong. But for a ideal, regular planning and execution flow this whole copy-and-pasting hasn’t been great.

What (I think) I want is this: I want one place to set goals, break-down into smaller and smaller tasks, be able to re-arrange and prioritize and finally, have one single list automatically that tells me what my next, highest-priority tasks are based on all the work I did before.

So, I built myself a simple app (Mac native and web) that allows me to:

  1. “Dream.” That’s where I define and write out my high-level goals. Usually once every few months.
  2. “Plan.” That’s where I take my high-level goals and break them down into small, manageable and attackable pieces. Usually weekly.
  3. “Execute.” That’s when I only want to see the most valuable tasks that I need to do right now. This is used daily.

So, here is what I came up with. Each tab represents one step in my workflow.

What I find particularly valuable is to have the Execute tab auto-generated based on my planning before. When I’m in execution mode I don’t want to think about anything except “What’s my next task?”

Dream… 

…Plan**…**

… and Execute!

As you can see, in Personal Development I’m test-driving this app.

IF it helps me move forward significantly better than my prior solution THEN it’s a success. Otherwise, the search continues.

But I can’t stress enough how much I enjoy having this one, single list of high-impact, high-value tasks generated for me (by me, too, during planning, but still…)

Well, I’m excited to see what happens. Either way, I’ll learn something new.

Currently Re-Reading: Malcom Gladwell's "David & Goliath"

It’s funny how, sometimes you can simply ‘ask’ your subconscious to pick the right book for you by just ‘mindlessly’ browsing your bookshelf. 

That exactly happened yesterday to me. I had just finished “A Moveable Feast” the day before and, since my new rule is to have no screen on after 8pm, I was ready to read something new. 

Just looking around, I quickly and without thinking picked out “David & Goliath” and within minutes of reading, I was hooked – again. I remember how much I enjoyed reading it a few years back (Maybe even while still in New York?) And, as magic will have it, It’s one of those stories that I can relate to very much at the moment.

So, I highly recommend it. 

A Moveable Feast

As part of my ongoing challenge to read every day, I recently re-discovered Hemingway’s “A Moveable Feast.” 

I picked it off my shelf the other day and couldn’t put it down. It’s a perfect book to get back into a daily reading habit because of its many short, beautifully-written chapters – or short-stories. It’s deeply personal and – maybe as a cliché 40-year old white man in the thick of his midlife-crisis – the stories pull all the right strings and hit all the right notes. 

I highly recommend you give it a spin.

Microgreen Growth Experiment: Natural Sunlight vs. Indoor LED Light

This was an experiment I ran over the summer.

My question was if there was a significant difference in microgreen plant-growth from the light-source used.

My hypothesis was that sunflower microgreens will grow faster when using sun-light as opposed to growing them indoors under LED lamps.

Result

The result surprised me! I was wrong. In my experiment, the LED-grown sunflowers were significantly taller and faster growing. The LED-grown sunflowers also stayed ahead of the sun-light grown sunflower microgreens after I put them alongside the others on my balcony. The taste of indoor and outdoor plants was not noticeable.

Of course, you could say that because the LEDs are potentially weaker (I don’t know) than sun-light, the plants had to stretch much further quicker. That may totally be it. However, as a fan of eating the greens, I’m all for greens that grow bigger faster.

After about 4 weeks here is the result:

Experiment Setup

My setup was by no means 100% scientific. But I tried to start both growing pods out the exact same way:

  • Same seeds
  • Same amount of seeds
  • Same container
  • Same coconut-rind “soil” that I got from the supermarket.
  • Same germination process
  • Same germination time
  • Same amount of direct light per day ~10 hours
  • Same watering schedule

The difference in lighting was as follows:

  • For sun-light I used my south-facing balcony in the month of June.
  • For LED-light I used my 2 rows of SANLight Flex 20 Growth LEDs

Learnings

Well, first off I learned that my hypothesis was wrong and that the indoor plants grew and matured faster AND continued to keep that head-way until the end. As I mentioned before, there was no (to me) noticeable difference in taste.

Conclusion

Come next year I have already planned the next iteration of this experiment.

In the meantime I will enjoy growing my delicious sunflower microgreens at home, in the middle of the Winter, dreaming of Summer.

See Adventures in Indoor Farming – Growing Mung Bean Microgreens

Ask Yourself: What is the most useful, loving and empowering belief I need right now?

I wanted to share this note I made with you because it has helped me take those ‘down’-moments, usually in-between tasks or activities to reflect and come up with empowering thoughts.

I found that asking this simple question “What is the most useful, loving and empowering belief I need right now?” allows my sub-conscious to come up with great answers. So instead of only rehearsing empowering beliefs once a day, this reminder helps me pick and choose the belief that I need at that moment.

It’s simple and very effective for me and maybe you can use it, too!

Things We Also Like: "Whole Foods" vs. "Trader Joes" in the US

I’m a big fan of Trader Joes. My friend Chris loves Whole Foods.

So I had an idea.

I figured I could generate a visual comparison report. A report that would let me see how the “Trader Joe” people and the “Whole Foods” people relate and how they differ. Just out of curiosity.

My goal is to become a ‘student of markets. And Facebook Audience Insights already provides ample data but it’s not very visual. So I decided to build a small tool that turns a list of related likes for a specific audience into a mosaic of the logos of the pages liked.

Just a few things I noticed off the bat:

  • “Trader Joes” also like Whole Foods but not the other way around.
  • “Trader Joes” seem to like more public figures, like Rachel Maddow and Dan Rather
  • PBS, NPR, Etsy, REI and Burt’s Bees and Pottery Barn are equally liked by both groups

Update: After a bit of coding, I’m now able to show interests they have in common:

 

If you’d like, you can download the comparison as PDF as well.

And in case you’re interested, the full list of interests per audience….

The “Brand World” of people in the US who like Whole Foods:

The “Brand World” of people in the US who like Trader Joes:

Source of data

Facebook Audience Insights

Mars and Venus Markets – US Male and Female Interest Comparison

So, since I built this market research tool earlier today, I felt like playing around with it.

One question I had was “What mental worlds, according to Facebook, do men and women in the US inhabit?” and “Do they intersect anywhere?”

Well, see for yourself.

Women in the US gravitate towards the following brands, personalities and services…

…while men in the US gravitate towards…

Interestingly, when comparing the two sets, there are no shared interests among the top 50 liked pages. I had expected at least a few brands to appear in the middle.

Anyways, just a quick note really.

Whiteboard Mastery – Step Up Your Handwriting Game Today!

Hey there! You can directly download the cheat sheet and all my complete manuals here:

If this helps you or you have questions or feedback, please leave a comment.

Description:
Today I want to share with you the NUTS and BOLTS of clear, confident and bold handwriting on whiteboards.

If you've felt awkward about your current writing, fret no more! I'm going to show you a completely NEW way to write. You can learn it yourself with a few minutes of simple exercises.

I've attached a cheat sheet for you that you can download and print out right away.

The script you're going to learn today is called "Whiteboard Casual", it's easy to learn and based on the old-time sign-painting brush scripts.

In the I'll show you a demo on how to write each letter. Preferably with a chisel-tipped pen. (Although it'll work with a round-tip pen as well.)

After you've watched the demo I want you to download the PDF, print it out and use any pen to trace the letter by hand a few times. With a little practice you'll learn the script and use it every day from now on.

Your whiteboarding will be clearer, more confident and more fun!

Get your FREE Whiteboard Handwriting Cheat Sheet here: alexis-rondeau.clickfunnels.com/optin8pin0v6b

Transcript:

Hi there. My name is Alexis and this is whiteboard mastery. Super excited to have you here today. today we're gonna tackle the nuts and bolts on how to write clearly and confidently and boldly and beautifully on whiteboards. you probably, if you've, if you're like most people, you've probably not practiced your handwriting, since, since first grade. And because of that, if you felt awkward or self confident, self conscious when you're, you know, working with other people or even by yourself and you thought to yourself, hm, my handwriting isn't so good. then I have great news when I'm going to share with you today is not how to improve your existing handwriting. We're going to completely learn a whole new type of script that, that I think is a beautiful, it's bold, it's confident, it's super fast, right? Once you get the hang out of it.

And I think it's, it's the first thing that you want to learn if you want to really sort of kick ass on the whiteboard. Okay. So, super excited. I'm super excited. I hope you are excited. if this sounds good to you. So, then we'll, we'll continue. All right. The script that you're going to learn today is called casual whiteboard casual. And it's a, it's a handwriting script. It's all caps. So we're gonna start with a very simple version of it. it's, it has its origins in the old art of sign painting. So, it ha, it's, it used to be a brush painting script and I've used it, I've used it a lot and I've adopted it to be used with whiteboard markers, specifically the chisel tip markers that I recommended you to get in another video. If you only have, you know, the round tip one, it's also fine....

7:03 long

Growing Delicious Mung Bean Microgreens – Adventures In Indoor Farming

601e8d3ab079c995b5c91214338eef18.jpg

In my last post I showed you how Ikea’s hydroponics system works. I’m a great fan and it has allowed me to add cut-fresh herbs like basil, water-cress and baby-leaf salad to my day-to-day cooking.

It’s been absolutely great. The plants keep on growing even when I cut off a few leaves every day and all I have to do is add growth-solution and water once a week.

Now, from hydroponics – that’s plants growing in soil-less medium using light, water and growth-solution – I discovered the world of growing Microgreens.

I bought a bare-minimum set of lights, trays, seeds and soil and less than a week later I’m already eating mung-bean microgreens by the spoonful with practically every meal. They’re delicious!

What are Microgreens?

Here’s what Wikipedia has to say:

A microgreen is a young vegetable green that is used both as a visual and flavor component or ingredient primarily in fine dining restaurants. Fine dining chefs use microgreens to enhance the attractiveness and taste of their dishes with their delicate textures and distinctive flavors. Smaller than “baby greens,” and harvested later than sprouts, microgreens can provide a variety of leaf flavors, such as sweet and spicy. They are also known for their various colors and textures. Among upscale markets, they are now considered a specialty genre of greens that are good for garnishing salads, soups, plates, and sandwiches.

Edible young greens and grains are produced from various kinds of vegetables, herbs or other plants. They range in size from 1 to 3 inches (2.5 to 7.6 cm), including the stem and leaves. A microgreen has a single central stem which has been cut just above the soil line during harvesting. It has fully developed cotyledon leaves and usually has one pair of very small, partially developed true leaves. The average crop-time for most microgreens is 10–14 days from seeding to harvest.

So, they’re not sprouts and not baby greens, and as far as I know, there isn’t even a German word for it yet.

I easily get excited about this kind of stuff so I had to try it out.

My Setup

The basic setup that I bought from a grow-shop here in Berlin included:

  • 2 Strips of SanLight Flex20 for 80 Euro each
  • 1 power-adapter for 20 Euro
  • 3 plastic trays for 10 Euro each
  • 10 Liters of soil for 5 Euro
  • 1 meter of chain for 4 Euro
  • 4 square meters of plastic tarp for light control for 4 Euro

A few things I’ve learned so far

  • It took less than a week for the beans to grow into harvestable, edible and delicious microgreens.
  • Stacking the trays for germination worked really well. I assume the pressure, darkness and moisture kick-starts the seed’s lifecycle rapidly.
  • Mung bean microgreens taste light and fresh and slightly buttery.
  • Supermarket-bought beans are completely ready to germinate and grow. The mung beans I got from a local store at 2 Euro per Kilo. I used most of that across the trays.
  • I had to water the trays twice in one week.
  • It was far easier than I expected to grow my own microgreens. The process is relatively hands-off, once you’ve gotten your seeds to sprout.

What’s Next?

I’ve read up on the most flavorful microgreens and learned that sunflower and pea greens are especially yummy.

So, the next round will be those: Sunflowers and peas.

Stay tuned, stay healthy!

Alexis

Growing Yummy, Hyper-Local Herbs – Adventures In Hydroponics

It’s been a month since I started my own little herb garden at home using Ikea’s Vaxer system.

The short of it is:

It works. Really well.

The whole setup cost me less than $120 and I’ve germinated, sprouted, grown and harvested salad, water-cress and basil in just a few weeks.

I’m a fan and wanted to show you the progress:

ids=“685,684,683,682,681,680,679,678,677,676,675,674,673,672”

I’m so excited to have more, fresh and tasty greens at my fingertips that I’m going to expand my Ikea system with a custom-made lighting and tray system that I’ll write about in the future.

I strongly believe that growing your own food while living in a big city is a good idea.

Next up: Growing pea, sunflower and hemp microgreens!

Thanks for reading,

Alexis

Tracking Meetup Growth Rates in Berlin

I’ve meant to build this for months but never got around to it until this weekend.

Basically, out of sheer curiosity, I want to know the fastest-growing Meetups in Berlin. I want to keep a finger on the pulse of this city. For that I find that Meetup memberships are one clear, physical indicator of what people are subscribing to. And what they consider worth their time to eventually meet up for.

So, anyways, I came up with a system that scrapes Berlin’s Meetups every day and calculates the member growth-rate in percent. It allows me to look at the daily report via a simple web UI:

First, the fastest growing Meetups per day…

…and yesterday’s Meetups compared and sorted:

I also added a simple page for each Meetup that summarizes its growth-journey:

A few things I learned

The Meetup API gives me varying responses

The Meetup API responses vary in their results – probably because I’m doing something wrong.

Basically, for Berlin, I have to page through 26 pages of results with 100 Meetups each. That’s about 2,660 Meetups in Berlin.

When I run my job once a day, sometimes I only get 1 page, sometimes 10, sometimes 14 and very rarely the full 26 pages. The API just eventually responds with an empty array on which I finish. I always get 200 OK responses, too, which makes it harder to understand why the variation occurs.

To better understand the issue, I’ve created a monitoring table. If my job isn’t able to fetch the latest data for all 2,660+ Meetups I already know exist, the job is marked as “Incomplete”.

As you can see, two days ago, I fetched all Meetups while the next time I ran the job only 1,400 (14 pages with 100 records each) were returned until the API stopped giving data.

I looked up their terms for fair use and experimented with 5- and 10-second delays between each HTTP request but the responses are still unpredictable.

So, yeah, this probably is all based on how I use their API but between the rate limit documentation which – to my knowledge I’m not over-using – I can’t figure out what the problem is.

Recently created Meetups have a natural growth-burst in their first days

I’ve only had data for the last 4 days but usually the fastest-growing ones were less than a month old. That totally makes sense, the organizer is excited, promotes the Meetup etc.

To counter-point this, I’m looking forward to having more than a week’s worth of data, then a month to compare.

I need more data; and data costs

The value of this is directly related to how much data I can report on. It’ll take at least a week for me to really start using it for real. I think a report on the fastest-growing Meetup in the last month will even be more valuable.

Speaking of data, I quickly exceeded 10,000 rows of the free hobby database on Heroku. I added a $9/mo. mini database on the third day.

Each day, I’m adding ~2,600 Member Count records plus ~2,600 Reporting Cube records to load the UI fast. I guess, that’s 5,200 records per day in total or 156,000 records per month. Nothing crazy but just interesting.

Anyways, just a few thoughts I wanted to share!

Let me know what you think.

Alexis

How To Get Better At What You Do Every Day With Retrospectives

I mentioned the Retrospectives I have been doing for the past years.

Retros are great fun. I use them to improve myself and my work week-over-week. I love them.

In case you’re every interested in doing one, here are three templates that I use regularly. Both, by yourself and as a team.

They’re a lot of fun.

  • Happy, Meh, Sad + Action Items
  • Start, Stop, More, Less, Keep
  • Sailboat to Paradise

Calculating Nonces That Result In Zero-Padded SHA256 Hashes

Build Status

Full Github Repo here

Yesterday I watched Anders Brownworth’s awesome Blockchain demo and got inspired to follow along in Clojure.

I had to play around with implementing a toy (read: unoptimized!) algorithm to “mine” for nonces that, when added to the hashing input, produces n zeros at the beginning of the hash:

I came up with make-nonce-for-zeros-finder, a function which takes the numbers of zeros you want to have at the beginning and a maximum search-depth and returns a function that will calculate the right nonce for your inputs.

What I personally enjoyed about this little exercise was to express an iterative process in a simple recursive function. And to make a function-maker once I wanted to play around with different types of hashes/nonces I wanted to find.

(with-test
  (defn make-nonce-for-zeros-finder [z-count]
    (fn
      ([num data]
       ((make-nonce-for-zeros-finder z-count) num data 0))
      ([num data nonce]
       (cond (= (generate-zeros-string z-count) (subs (generate-hash num data nonce) 0 z-count)) nonce
             :else (recur num data (inc nonce))))))

  (is (= ((make-nonce-for-zeros-finder 1) nil nil) 39))
  (is (= ((make-nonce-for-zeros-finder 1) 1 nil) 25))
  (is (= ((make-nonce-for-zeros-finder 1) 1 1) 11))
  (is (= ((make-nonce-for-zeros-finder 1) "foo" "bar" 0) 20))
  (is (= ((make-nonce-for-zeros-finder 2) nil nil) 286))
  (is (= ((make-nonce-for-zeros-finder 4) nil nil) 88484))
  (is (= ((make-nonce-for-zeros-finder 4) 1 1) 64840))
  (is (= ((make-nonce-for-zeros-finder 4) "foo" "bar") 42515)))

With it, I can quickly create finders for different lengths of zero paddings:

(def find-nonce-for-one-zero-padded-hash
  (make-nonce-for-zeros-finder 1))

(def find-nonce-for-two-zeros-padded-hash
  (make-nonce-for-zeros-finder 2))

(def find-nonce-for-three-zeros-padded-hash
  (make-nonce-for-zeros-finder 3))

(def find-nonce-for-four-zeros-padded-hash
  (make-nonce-for-zeros-finder 4))

(def find-nonce-for-five-zeros-padded-hash
  (make-nonce-for-zeros-finder 5))

(def find-nonce-for-six-zeros-padded-hash
  (make-nonce-for-zeros-finder 6))

;; and so on...

With these guys set up, we can now calculate the nonce for hashes with 1, 2, 3, 4 and 5 zeros padded. Let’s see how that looks:

(find-nonce-for-one-zero-padded-hash "foo" "bar")
;; => 20
;; takes less than a millisecond

;; we can verify that the hash has one zero at the beginning:
(generate-hash "foo" "bar" 20)
;; => "0fdc57809f5917eba08907d2805e43ce83f4c933a090b4a2b2549923a35e43d7"

(find-nonce-for-two-zeros-padded-hash "foo" "bar")
;; => 102
;; takes less than a millisecond

;; we can verify that the hash has two zeros at the beginning:
(generate-hash "foo" "bar" 102)
;; => "006668bba91b7e2d5b5357b56600784edb77a72ecf86dc09d515853a841485f6"

(find-nonce-for-three-zeros-padded-hash "foo" "bar")
;; => 4663
;; takes less than a millisecond

;; we can verify that the hash has three zeros at the beginning:
(generate-hash "foo" "bar" 4663)
;; => "0005d9cd6c13fe6bf56e169fd2a7008003fc3a4c6539f8a1cf7d82975d00210e"

And that’s really all there is to it. Once you cross 6 padded zeros, you’ll notice an increase in time it takes to compute.

How to test

I’ve been a huge fan of the with-test macro. Because of that, you’ll find all test-coverage in core.clj instead of a separate “_test.clj” file.

To run all tests, use lein test

Related

The Little Schemer in Clojure – Recap Chapter 6

Shadows

Using helper functions increases readability and helps abstract away representations.

In particular, the example is a simple calculating function value such as (value '(1 + (7 + 11)) ;; => 19

We can implement this with standard procedures and recursion:

(with-test
  (def value
    (fn [u] 
      (cond (atom? u) u
            (eq? (car (cdr u)) '+) (+ (value (car u))
                                          (value (car (cdr (cdr u)))))
            (eq? (car (cdr u)) '*) (- (value (car u))
                                           (value (car (cdr (cdr u)))))
            :else (int (java.lang.Math/pow (value (car u))
                                           (value (car (cdr (cdr u)))))))))

  (is (= (value 13) 13))
  (is (= (value '(1 + 3)) 4))
  (is (= (value '(1 + (3 pow 4))) 82)))

Now, you can already see a lot of visual noise. There is a lot of car-ing and cdr-ing about. If you squint your eyes a little, you’ll also notice that they all look similar. in fact, there is a lot of repetition in this function:

We see this pattern three times and each it’s along the lines of:

((eq? (car (cdr u)) 'OPERATOR) (OPERATOR (value (car u)) (value (car (cdr (cdr u))))))

Since we know that our “language” right now is infix arithmetic expressions such as

(1 + 2) and (1 + (4 * 999))

…we can extract helper functions that get the operator, the first sub expression - 1 and the second sub expression - 2 like so:

(with-test
  (def operator
    (fn [aexp]
      (car (cdr aexp))))

  (is (= (operator '()) nil))
  (is (= (operator '(1)) nil))
  (is (= (operator '(1 + )) '+))
  (is (= (operator '(1 + 2)) '+)))

(with-test
  (def first-sub-exp
    (fn [aexp]
      (car aexp)))

  (is (= (first-sub-exp '()) nil))
  (is (= (first-sub-exp '(1)) 1))
  (is (= (first-sub-exp '(1 + 2)) 1))
  (is (= (first-sub-exp '(2 + 1)) 2)))

(with-test
  (def second-sub-exp
    (fn [aexp] (car (cdr (cdr aexp)))))

  (is (= (second-sub-exp '()) nil))
  (is (= (second-sub-exp '(1)) nil))
  (is (= (second-sub-exp '(1 +)) nil))
  (is (= (second-sub-exp '(1 + 2)) 2))
  (is (= (second-sub-exp '(1 + 3)) 3)))

And then, since we have test-coverage already, refactor our value function to be…

(with-test
  (def value
    (fn [u] 
      (cond (atom? u) u
            (eq? (operator u) '+) (+ (value (first-sub-exp u))
                                          (value (second-sub-exp u)))
            (eq? (operator u) '*) (- (value (first-sub-exp u))
                                           (value (second-sub-exp u)))
            :else (int (java.lang.Math/pow (value (first-sub-exp u))
                                           (value (second-sub-exp u)))))))

  (is (= (value 13) 13))
  (is (= (value '(1 + 3)) 4))
  (is (= (value '(1 + (3 pow 4))) 82)))

Dandy. The new help functions help read this much better.

Changing Your Mind

Now, say, you wake up today and would like to change your newly-born arithmetic language to be written in prefix notation, such as (+ 1 2). In a way, I want to say:

I’ve changed my mind. Now, I want have (+ 1 2) express the common arithmetic expression that evaluates to 4.

The changes needed to make this happen are now trivial since we have used help functions to hide the representation of operator, first sub expression and second sub expression.

Simply changing the helper functions as follows implements the prefix notation without having to change our value function.

You have to adjust your tests accordingly, of course, and, in a way, doing so is as much as saying

“I want to change this and it should work as follows…”

(with-test
  (def operator
    (fn [aexp]
      (car aexp)))

  (is (= (operator '()) nil))
  (is (= (operator '(+)) '+))
  (is (= (operator '(+ 1)) '+)))

(with-test
  (def first-sub-exp
    (fn [aexp]
      (car (cdr aexp))))

  (is (= (first-sub-exp '()) nil))
  (is (= (first-sub-exp '(+)) nil))
  (is (= (first-sub-exp '(+ 1)) 1))
  (is (= (first-sub-exp '(+ 1 2)) 1)))

(with-test
  (def second-sub-exp
    (fn [aexp]
      (car (cdr (cdr aexp)))))

  (is (= (second-sub-exp '()) nil))
  (is (= (second-sub-exp '(+)) nil))
  (is (= (second-sub-exp '(+ 1)) nil))
  (is (= (second-sub-exp '(+ 1 2)) 2))
  (is (= (second-sub-exp '(+ 1 2 3)) 2)))

(with-test
  (def value-prefix
    (fn [nexp]
      (cond (atom? nexp) nexp
            (eq? (operator nexp) '+) (+ (value-prefix (first-sub-exp nexp))
                                         (value-prefix (second-sub-exp nexp)))
            (eq? (operator nexp) '*) (* (value-prefix (first-sub-exp nexp))
                                        (value-prefix (second-sub-exp nexp)))
            :else (int (java.lang.Math/pow (value-prefix (first-sub-exp nexp))
                                           (value-prefix (second-sub-exp nexp)))))))

  (is (= (value-prefix 1) 1))
  (is (= (value-prefix '(+ 1 3)) 4))
  (is (= (value-prefix '(+ 1 (* 2 2))) 5))
  (is (= (value-prefix '(+ 1 (pow 3 4))) 82)))

This is nice. We have changed values language by changing the helper functions without changing its own structure.

This way, we are able to demonstrate that we can represent the arithmetic expression 1 + 1 in several ways:

  • (+ 1 1) to make it feel lispy
  • (1 + 1) to make it feel ‘normal’ and
  • (1 1 +) if we wanted to accommodate friends that read from right to left more naturally

After all, they mean the same thing but in different ways.

Parental Controls – Braces as Numbers

Now, another example: Let’s say we wanted to represent numbers differently but wanted, as reasonable human beings, to maintain the idea of plus, minus, multiplication and division.

So, instead of using the numeral…

  • 0, I would like to use () (a list of zero items)
  • 1, I would like to use (()) (a list of one items)
  • 2, I would like to use (() ()) (a list of two items)
  • 3, I would like to use (() () ()) (a list of three items)
  • and so on…

In terms of API, I would like then to be able to write 1 + 1 as follows:

(+ '( () ) '( () ))
;; => 2

Clearly, the idea of addition or subtraction doesn’t change just because I’ve changed how I represent my numbers, right?

Our function to represent the operator + was

  (def + (fn [n m] 
                (cond (zero? m) n
                      :else (add1 (+ n (sub1 m))))))

To make our dream of (fairly unreadable) parenthesis number crunching real, all we have to do is rewrite our helper functions zero?, add1 and sub1 and we’re off to the races:

(with-test
  (def zero? 
    (fn [n] (null? n)))

  (is (= (zero? '()) true))
  (is (= (zero? '(())) false)))

(with-test
  (def add1
    (fn [n]
      (cons '() n)))
  (is (= (add1 '()) '(())))
  (is (= (add1 '(())) '(() ())))
  (is (= (add1 '(() ())) '(() () ()))))

(with-test
  (def sub1
    (fn [n] (cdr n)))

  (is (= (sub1 '()) '()))
  (is (= (sub1 '(())) '()))
  (is (= (sub1 '(() ())) '(()))))

(with-test
  (def + (fn [n m] 
            (cond (zero? m) n
                  :else (add1 (+ n (sub1 m))))))

  (is (= (+ '() '()) '()))
  (is (= (+ '(()) '())) '(()))
  (is (= (+ '(() ()) '(())) '(() () ()))))

Anyways, that’s what I’ve taken away from the chapter.

My Journey Into Deep Work – Retrospective Week 16

I managed to work deeply for 9.5 hours. I also traveled over the weekend, so my week was a bit shot.

Also, I have decided to extend my retros to look at my overall life not just deep work. Since I am independent right now, I want to make sure I maximize my time spent and minimize distractions. I figured that “Life Retro” combined with action items to improve myself every week is a good idea.

What went well

  • Using my Psycho-Cybernetics Imagineering script
  • It was easy to get into work-flow
  • With everything considered, 9.5 hours is still great!
  • Traveled to Mallorca to see my parents :)
  • Had good insight into my “stop-and-go” way of working
  • Had a good mentoring session with my friend Philip on Friday
  • Overall, I have a more positive outlook on my life than last week
  • Inspired by reading “Give and Take” [1]
  • Inspired by “Amateurs vs. Pros” [2]
  • Got up most days before 9am
  • Getting back into a daily Yoga and Meditation routine
  • I fixed my vaporizer myself
  • Feeling less distracted
  • Got to hang out with Nina for an afternoon :)
  • Went out to a cool bar on Friday with friends
  • Made bubbles again
  • Cooked delicious food with my father

What went okay

  • I didn’t follow up on my Whiteboard experiment
  • I haven’t followed up on the Meetup idea I had
  • There was a lot of organizational stuff this week
  • Waiting for my accountant on paperwork
  • The dating-pool in Berlin is rough. Didn’t do well to my self-image
  • Didn’t hear back from someone I needed information from

What went bad

  • I still don’t have one specific idea/topic that I want to deep-dive into. Instead I jump around from idea to idea.
  • Jumping between tasks and ideas is exhausting.

Action Items

  • Choose 1 main point of focus for coming week
  • Buy a yoga mat while in Mallorca to continue practice there
  • Create a visible backlog of tasks

[1] https://www.amazon.com/dp/B00AFPTSI0/ref=dp-kindle-redirect?\_encoding=UTF8&btkr=1 [2] https://medium.com/the-mission/the-7-differences-between-professionals-and-amateurs-f8efc4840861

My Journey Into Deep Work – Retrospective Week 17

I spent this week in Mallorca with my parents. Stil I got to take time to sit down and get 13 (!) hours of deep work done.

Here’s my recap:

What went well

  • Still very inspired by “Amateurs vs. Pros”
  • Feel like I’ve made good progress
  • 1 Focus for the week: Work through “The Little Schemer” in Clojure. Finished Chapter 1 - 4.
  • Wrote and published 4 blogposts
  • Daily Yoga and especially Pranayama exercises
  • Had an idea for a meditation video
  • Very happy with my Psycho-Cybernetic Imagineering script rehearsals
  • Cooking good food with the family
  • Have a fireplace at home
  • Have a minimal but functional work-space set up

What went okay

  • Didn’t spend a lot of time outside, especially when there’s the mediterranean just across from me.
  • “Give and Take” is getting harder to read
  • Access to internet only sometimes
  • Still jumping a lot from idea to idea
  • Didn’t celebrate/appreciate/acknowledge my progress
  • Didn’t edit the blogposts
  • Lost interest in RedditGrowth while it still could become something valuable

What went bad

  • Have procrastinated on writing the meet up summary
  • Too reserved talking about my feelings with my parents

Action items

  • Take a daily walk
  • Celebrate successes and progress more often
  • Edit posts before publishing
  • Make a decision if I want to continue reading “Give and Take”

Set Your Priorities Straight In 2 Minutes Without Thinking Hard

“I have so much to do but I’m not sure which of the things I should do first.”

If you, like me, have struggled with figuring out what should be my No.1priority, then I have good news.

Prioritizing is hard, especially when your items can be important and/or urgent. The app that I just released gives you the answer to:

What’s the most important and urgent thing I should do next? (The answer might surprise you!)

I would like to have someone help me with this and say to me:

Your most important and urgent next step is… Make a balance sheet

Now, I just released a simple web-app that helps you (and me) prioritize a list (a todo-list, a list of chores, whatever you want) by two measures:

Importance and Urgency. Some things are important, some are urgent. And some are both. Make sure you tackle the one that’s both first.

The app guides you through comparing each item with each other based on both measures and comes up with the math and sorting automatically.

Here’s the result for the tasks I had on my list today:

Clearly, I need to create my balance sheet. After that I can do the other stuff.

The process is simple.

Go to the homepage and click the start button

Then, insert your list of tasks like so:

Then, the app will guide you through comparing each item with each other. This is called a binary sort and will make sure that what item is at the top is really the most important and urgent one.

Finally, you’ll see your result.

Now, what are you waiting for? Get your priorities straight in a few short minutes and tackle your next, most important and urgent task right away.

Click here: https://prioio.herokuapp.com

Have fun!

Alexis

*

PS: You can check out the source at https://github.com/akaalias/prio in case you want to poke around a bit.

PPS: Let me know if you, too, have found a surprising result of your top priority after sorting.

Thinking and Sleeping Better With Clojure

To prioritize a list of todo-items, as in my Prioritization-app I recently launched, I needed to get a list of the unique combinations for the user to choose from:

It goes a little bit like this:

Which is more important: A or B? Which is more important: A or C? Which is more important: B or C?

I wrote a version in Ruby for the app and then a sketch in Clojure to compare readability and expressiveness.

In my opinion, the Clojure version is much more readable and was far easier to reason about.

Probably because I’m not good to solve problems in Ruby but there might be more than meets the eye here.

Creating Comparisons

Let’s say I have a todo-list of A, B, C and D.

The list of comparisons I want is A ↔ B, A ↔ C, A ↔ D, B ↔ C, B ↔ D and C ↔ D to use later in my app.

From the matrix below, you can see, I want only the unique comparisons (A ↔ B and B ↔ A are interchangeable for my purposes) and skip dupes that compare an element with itself (A ↔ A, B ↔ B, C ↔ C, D ↔ D)

To demonstrate, I only want the top-right triangle of comparisons:

×ABCD
Askip (dupe)A ↔ BA ↔ CA ↔ D
Bskipskip (dupe)B ↔ CB ↔ D
Cskipskipskip (dupe)C ↔ D
Dskipskipskipskip (dupe)

Thinking, Testing and Implementing in Ruby

Now, since I started the app in Ruby, I translated the behavior above as follows:

describe '.unique_combinations' do
    context 'input list is empty' do
      it 'returns an empty list' do
        result = Combinator.unique_combinations([])
        expect(result).to eq([])
      end
    end

    context 'input list only has one element' do
      it 'returns an empty list' do
        result = Combinator.unique_combinations([1])
        expect(result).to eq([])
      end
    end

    context 'input list only has two elements' do
      it 'returns a list with combination a' do
        result = Combinator.unique_combinations([1, 2])
        expect(result).to eq(<span class="wikilink unresolved">1,2</span>)
      end

      it 'returns a list with combination b' do
        result = Combinator.unique_combinations([:foo, :bar])
        expect(result).to eq(<span class="wikilink unresolved">:foo, :bar</span>)
      end
    end

    context 'input list only has three elements' do
      it 'returns a list with 3 combinations' do
        result = Combinator.unique_combinations([1, 2, 3])
        expect(result).to eq([[1, 2], [1, 3], [2, 3]])
      end
    end

    context 'input list only has four elements' do
      it 'returns a list with 6 combinations' do
        result = Combinator.unique_combinations([1, 2, 3, 4])
        expect(result).to eq([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]])
      end
    end
  End
end

…and wrote, after several failed attempts and one sleepless night, the following, working but not very elegant solution. I felt weird about it, because even looking at it now, I can’t fully explain it well. It works, it has reasonable test-coverage. But I’m not proud of it.

def self.unique_combinations(ids)
    return [] if ids.length < 2

    all_combinations = []

    for i1 in ids
      for i2 in ids
        if i1 != i2
          contains = false
          for c in all_combinations
            if c[1] <mark> i1 and c[0] </mark> i2
              contains = true
            end
          end
          unless contains
            all_combinations << [i1, i2]
          end
        end
      end
    end

    return all_combinations
  end

Okay, fine I thought. How would I solve this issue in Lisp?

Thinking, Testing and Implementing in Clojure

I don’t know why but thinking in terms of lists and recursion instead of iteration made it simpler for me.

The top-level function works as follows:

(final-combinations '(A B C D)) 
;; => '((A B) (A C) (A D) (B C) (B D) (C D))

Yup, that’s the list I want. Thinking about it I had an idea to break the problem further down:

The complete list of unique comparisons, the one I want, is…

  1. the union of each row of one element combined with all the others. That’s A combined with B, C and D
  2. Each row has an increasing offset from the left that starts at 0 for row 1 and increases by 1 for each following row.
  3. I have to subtract the dupes such as A ↔ A because I don’t need them.

For illustration purposes, here is the table again:

×ABCD
Askip (dupe)A ↔ BA ↔ CA ↔ D
Bskipskip (dupe)B ↔ CB ↔ D
Cskipskipskip (dupe)C ↔ D
Dskipskipskipskip (dupe)

Given the above 3 steps, I basically just wrote them out as functions.

I derived the first function, combine which takes x and l and returns a list of all possible combinations, including the ones we’ll later skip.

(with-test 
  (defn combine 
    ([x l] 
     (combine x l '()))
    ([x l acc]
     (cond (empty? l) (reverse acc)
           :else (recur x (rest l) (conj acc (list x (first l)))) )))

  (is (= (combine 1 '()) '()))
  (is (= (combine 1 '(1)) '((1 1))))
  (is (= (combine 1 '(2)) '((1 2))))
  (is (= (combine 1 '(1 2)) '((1 1) (1 2))))
  (is (= (combine 1 '(1 2 3)) '((1 1) (1 2) (1 3)))))

Then, I use combine to create basically the table above but with nothing yet removed.

(with-test
  (defn combine-lists 
    ([l1 l2] 
     (combine-lists l1 l2 '()))
    ([l1 l2 acc]
     (cond (empty? l1) (reverse acc)
           :else (recur (rest l1) l2 (cons (combine (first l1) l2) acc)) )))

  (is (= (combine-lists '() '()) '()))
  (is (= (combine-lists '(1) '(1)) '(((1 1)))))
  (is (= (combine-lists '(1 2) '(1 2)) '(((1 1) (1 2)) 
                                         ((2 1) (2 2))))))

Then I introduce the idea of an offset. To only take those elements in a list that are after a certain offset.

(with-test
  (defn take-rest
    ([offset l]
     (cond (= offset 0) l
           :else (recur (dec offset) (rest l)))))

  (is (= (take-rest 0 '()) '()))
  (is (= (take-rest 1 '()) '()))
  (is (= (take-rest 0 '(1)) '(1)))
  (is (= (take-rest 0 '(1 2)) '(1 2)))
  (is (= (take-rest 1 '(1 2)) '(2)))
  (is (= (take-rest 1 '(1 2 3)) '(2 3)))
  (is (= (take-rest 2 '(1 2 3)) '(3)))
  (is (= (take-rest 2 '(1 2 3 4)) '(3 4))))

Now I can use take-rest and apply it to each row in my matrix…

(defn unique-combinations 
  ([offset ll] 
   (unique-combinations offset ll '()))
  ([offset ll acc]
   (cond (empty? ll) (reverse acc)
         :else (recur (inc offset) (rest ll) (cons (take-rest offset (first ll)) acc)))))

And finally remove the identity items such as A ↔ A

(with-test
  (defn final-combinations 
    [l]
    (filter #((= (first %) (second %)))) (partition 2 (flatten (unique-combinations 1 (combine-lists l l)))))

  (is (= (final-combinations '()) '()))
  (is (= (final-combinations '(1)) '()))
  (is (= (final-combinations '(1 2)) '((1 2))))
  (is (= (final-combinations '(1 2 3)) '((1 2) (1 3) (2 3))))
  (is (= (final-combinations '(1 2 3 4)) '((1 2) (1 3) (1 4) (2 3) (2 4) (3 4)))))

Granted, this code is not yet refactored and in total more lines than the Ruby version, but here is the rub:

It took me 20 minutes to think up and implement the solution in Clojure and 2 days including a sleepless night to do it in Ruby.

I don’t know what that means but I’ll probably use a Lisp like Clojure for problems like this in the future. Just to make sure I get some sleep.