Please critique my FizzBuzz!

(For anyone needing an excuse to procrastinate.)

Is this a very julian way to write this? How could it be improved?

function fizzbuzz(i, ps)
    s = [p.second for p in ps if p.first(i)] |> join
    return isempty(s) ? string(i) : s
end

ps = ((i->i%3==0) => "Fizz",
      (i->i%5==0) => "Buzz")

fizzbuzz.(1:20, Ref(ps))
2 Likes

Here’s a prosaic version I might write in “real code”:

function fizzbuzz(i)
    f = i % 3 == 0
    b = i % 5 == 0
    f && b ? "FizzBuzz" :
    f      ? "Fizz"     :
    b      ? "Buzz"     : string(i)
end

for i in 1:20
    println(fizzbuzz(i))
end

Arguably this has the benefit of being straightforward, keeping the logic related to fizzing and buzzing in one place, separating the side effects, and not allocating more than “necessary” (maybe could be better in that way if we went direct to IO). It has the downsides of being boring and not generalizing to arbitrary predicates :smiley:

7 Likes
ps = ((i->i%3==0) => "Fizz",
      (i->i%5==0) => "Buzz",
i->i%7 ==0)=>"Zap")

function fizzbuzz(n,ps)
    res = ""
    for (key,value) in ps
        if key(n) == true 
            res *= value
        end
    end
    if res == ""
        res *= string(n)
    end
    return res
end

julia> for i = 1:21
       println(fizzbuzz(i,ps))
       end

1
2
Fizz
4
Buzz
Fizz
Zap
8
Fizz
Buzz
11
Fizz
13
Zap
FizzBuzz
16
17
Fizz
19
Buzz
FizzZap

2 Likes

maybe using a function to access the values rather that accessing the field directly?

s = [last(p) for p in ps if first(p)(i)] |> join
1 Like

Yes, this is boring. But I looked at my code less than 24 hours after writing it and didn’t immediately understand it! So maybe boring is not such a bad thing.

Also, good point about removing allocations.

4 Likes

Thanks. As far as I can see this is essentially the same fizzbuzz, except it avoids creating an array of strings. Is that the point here?

I just wanted to do my own version :sweat_smile:, but there are esencially the same, as they use the same arguments.

1 Like

Just realised the array allocation

    s = [p.second for p in ps if p.first(i)] |> join

can be replaced with a generator.

    s = join(p.second for p in ps if p.first(i))

It’s faster too.

1 Like

I’m so lazy here’s how I’d probably write it…

FizzBuzz1(x) = ( (x % 3 == 0) ? "Fizz" : "") * ( (x % 5 == 0) ? "Buzz" : "")
FizzBuzz1.(1:30)

FizzBuzz2(x) = reduce( *, ["Fizz","Buzz"][(x .% [3,5]) .== 0])
FizzBuzz2.(1:30)

Awkward looks like I just failed my code interview! Here’s a quick monkey patch…

function IfNothingThenX(fn, x) 
    result = fn(x)
    return ( length(result) == 0 ) ? x : result
end

IfNothingThenX.(FizzBuzz1, 1:30)
#Or...
IfNothingThenX.(FizzBuzz2, 1:30)
1 Like

The reduce version gets my vote!

1 Like

Right! I feel it’s an uncomfortable fact that boring is often great for maintenance. Thought provoking reading: Dan McKinley :: Choose Boring Technology

6 Likes

Boring is usually the best for maintenance! Often good for performance too. People who write languages, write them to be used pragmatically (usually).

I have a proclivity to solve small units of code as quickly as possible. Sometimes it bites me, sometimes it saves me a bunch of trouble because 70% of it goes to the garbage heap or the metaphorical “never to be touched again” code file.

An ironic suggestion on the Julia mailing list :grimacing:

I was working at Etsy with Dan (author of that blog post) when Kellan became the head of engineering. The effort to get rid of miscellaneous “exciting” technology was clearly the right thing to do. We did not need web services built in Clojure in company where most things were written in PHP. We also didn’t need MongoDB for one service when other things were all backed by MySQL.

However, at one point Kellan tried to convince me that it would be better to write a recommender system in PHP. And that’s where things become less clear cut. Could you write code that computes SVDs of sparse matrices in PHP? Honestly, I don’t know. For that application, PHP becomes an exciting technology rather than a boring one. You know what’s a boring technology for building a SVD-based recommender system? Matlab. So that’s what I used. Unfortunately, that entails deploying Matlab—fortunately not for serving recommendations, we precomputed those and stored them in MySQL to be served from PHP—but we did deploy Matlab for generating new recommendations each day;
not a critical component since when it failed all that happened was that recommendations didn’t get updated, but still unpleasant and risky. Deploying Matlab is back in rather “exciting“ territory again. However, that seemed like the least exciting option available at the time.

That was in 2010, and Julia was just a weird side project that everyone made fun of me for working on in my spare time. (Having your own programming language is the tech equivalent of having your own religion—people back away slowly when you start to tell them about it, until it becomes well known and then it’s cool, I guess?) If it was today and I needed to build and deploy such a recommender system, Julia would be a nice boring technology for that. Python would also be a fine and arguably even more boring technology for it. Deploying Julia is, in any case, way more reasonable and boring than deploying Matlab was.

I think the bottom line is that you have a certain amount of novelty budget for an endeavor, so think hard about where you want to spend it and what you get in exchange.

38 Likes

Haha! The irony was not lost on me was partly lost on me due to being several levels deep, thanks for the story! By the way I really do like that blog post but it makes me strangely uncomfortable at the same time. Perhaps it’s the problem of being a research scientist who knows enough systems engineering to be dangerous :smiley: (or is it the other way around… I’m never quite sure anymore :thinking:)

Having a novelty budget is a good way to think about the problem.

6 Likes

This rings familiar from a developer and team lead perspective.

1 - we’ve been using PHP for over 10 years. It’s the least exciting language, almost as uncool as your neighbour’s 1997 Prius. However, it just works. The upgrade path from PHP 5 (or was it 4 when we started?!) to current 7 was simple and cheap. And you get very good performance with minimum tweaks (99% of the time the RDBMS is the bottleneck).

2 - however, there are things that a language will just be bad at, up to the point where it’s just not worth optimising for that. For us, it was processing very large amounts of XML data with PHP in real-time. We ended up choosing Go, but it was part of that research that I discovered Julia :heart_eyes: (Julia was just too young at that point, it must’ve been 0.4). We ended up with a pretty efficient services architecture in PHP and Go.

3 - as a developer, getting to work with new technology is fun and exciting and as the team lead I see this as a reward for the team. But as a team lead/CTO one must very thoroughly weigh that against the fact that you’re adopting new languages which need expertise, recruiting, training, etc for long term maintenance. If your only Go dev leaves, you need to find a solution fast. So once you adopt a language, best to adopt it for a wide class of projects and tasks and understand that you’re committing to that language.

9 Likes

With some coauthors, we are nearing a milestone (an actual draft of preliminary results, yay :tada:) of a medium-sized numerical project (solving a particular model), so at some point I will be tempted to write up a similar account of our experience for scientific computing, particularly in Julia, because we have a nice trail of benchmarks from the very beginning to a more than 100x speedup using various tricks (so now the runtime is just 2 days :wink:).

The most important lesson at this point is to get one working version of the whole computation stack before any algorithmic or optimization. This is mentally difficult to do, because we all know that whatever ends up there will be completely rewritten N times (currently, at least N = 6 for us) so it all seems like wasted effort. But without something to profile and benchmark, we would probably misallocate our “optimization budget”.

The unexpectedly nice thing about Julia is how easy it is to write “loosely coupled” code that can be optimized and benchmarked in small pieces. So, we can experient with replacing a Vector with a StaticVector, or allocating in an object pool for some parts, or switch AD libraries, pretty much without rewriting anything else. This in itself makes up for the wholes in the ecosystem.

18 Likes

Not strictly related but just a delightful rant form Dan about even earlier days at Etsy than I was there for:

Part of the moral of the story is that even though Python + Twisted is a perfectly boring technology, boringness is context dependent: as a middleware later in a PHP stack, Python is totally unnecessary.

7 Likes

39 posts were split to a new topic: Code-golfing FizzBuzz