17 comments

[ 3.1 ms ] story [ 41.9 ms ] thread
If you looked at the assembly code, though, there still is a branch (aka an if statement) -- it's just hidden inside the "choice" function.
Indeed.

But I think that the point of functional programming is to provide a layer of programming that exposes a clean API. You know: now side effects, no global variables etc..

And maybe: no if!

This is very nice. Here's my Python port:

  from itertools import cycle, imap, count, islice
  from operator import add

  def fizzbuzz(n):
      fizzes = cycle(["", "", "Fizz"])
      buzzes = cycle(["", "", "", "", "Buzz"])
      words = imap(add, fizzes, buzzes)
      numbers = imap(str, count(1))
      _fizzbuzz = imap(max, words, numbers)
      return list(islice(_fizzbuzz, n))
It's amazing Python code and clojure code are almost the same.

(Except infix/prefix)

Here's a different sort-of-functional python fizzbuzz.

While it doesn't use the spiffy lazy lists of itertools, and does have the typical "if x % n == 0" test, it does generalize easily to the "and print Baz every 37th number" case.

  def joinmap(func, elements, separator):
    return separator.join(map(func, elements))

  def fb_value(x, fb={3:'Fizz', 5:'Buzz'}):
    words = joinmap(lambda n: fb[n] if x % n == 0 else '', fb.keys(), '')
    return words if words else str(x)

  def fb_range(low=1, high=100):
    return joinmap(fb_value, range(low, 1 + high), '\n')

  print fb_range()
That's excellent!

Treating a dictionary as an iterator automatically iterates over its .keys(), so you can remove the .keys() in fb.keys() and just map over fb itself.

>>> map(lambda x: x+x, {2: "who", 4: "do", 6: "we", 8: "appreciate"})

[16, 4, 8, 12]

A problem that this highlights is that the order in which the Fizz and Buzz and other strings get concatenated depends on the order of the underlying dictionary hash, which can be thought of as random. In Python 3 it even changes from run to run.

https://docs.python.org/3/whatsnew/3.3.html#summary-release-...

$ python3 -c 'print(list({"foo": "bar", "baz": "qux"}))'

['foo', 'baz']

$ python3 -c 'print(list({"foo": "bar", "baz": "qux"}))'

['baz', 'foo']

For predictable behavior across larger sets of divisors, and for consistent behavior on Python 3, you'd probably want to use collections.OrderedDict.

All I've learned is I can't stand any code called "elegant."
Probably a valid complaint against code like this. In an organization with a lot of more junior developers I might strike this code in a code review as being non obvious. On the other hand, I trust this code more than similar basic conditional based fizzbuzz since there's no conditionals or nested logic to get wrong. It's also simple to extend, ie, a 'Bazz' on 7 requirement would be adding one more cycle to the mix.
(comment deleted)
That's a slick way to remove the conditionals indeed. Inspired by it, here's a Scala version (checked with v2.11):

  object Fizzbuzz
  {
      val fizzes = List (None, None, Some ("Fizz")).toStream;
      val buzzes = List (None, None, None, None, Some ("Buzz")).toStream;
      val reducer : (Option[String], Option[String]) => Option[String] =
          (a, b) => Option ((a ++ b).mkString).filterNot (_.isEmpty);

      def apply (amount : Int) : Seq[String] =
          Stream.continually (fizzes)
              .flatten
              .zip (Stream.continually (buzzes).flatten)
              .map (reducer.tupled)
              .zipWithIndex
              .map {
                  case (s, n) => s.getOrElse ((n + 1).toString);
                  }
              .take (amount);
  }

  Fizzbuzz (100).foreach (println)
It's amazing how scala syntax is verbose - not to say ugly!
I was being intentionally verbose so that readers not familiar with Scala or the collections types/methods used would have an easier time following it. I also was avoiding use of pattern matching as they are cousins to conditionals IMHO.

However, below is another version which employs idioms more commonly found:

  object Fizzbuzz
  {
      val fizzes = None :: None :: Some ("Fizz") :: Nil;
      val buzzes = None :: None :: None :: None :: Some ("Buzz") :: Nil;

      def apply (amount : Int) : TraversableOnce[String] =
      {
          val alternates = Stream.continually (fizzes).flatten
            .zip (Stream.continually (buzzes).flatten)
            .take (amount)
            .toArray;

          0.until (amount).map { n =>
              alternates (n) match {
                  case (Some (f), Some (b)) => f + b;
                  case (Some (f), None) => f;
                  case (None, Some (b)) => b;
                  case _ => (n + 1).toString
                  }
              }
      }
  }

  Fizzbuzz (100).foreach (println);
Oh! I see. It's much more concise and clean now.

But still I prefer the clojure version using pattern matching over the scala one: http://blog.klipse.tech/clojure/2016/10/25/core-match.html#f...

Gotcha. Then here's a translation into Scala from the link you provided which I believe maintains the spirit of the clojure version:

  object Fizzbuzz
  {
      def apply (amount : Int) : TraversableOnce[String] =
          1.to (amount).map { n =>
              (n % 3, n % 5) match {
                  case (0, 0) => "FizzBuzz";
                  case (0, _) => "Fizz";
                  case (_, 0) => "Buzz";
                  case _ => n.toString
                  }
              }
  }
  
  Fizzbuzz (100).foreach (println);

EDIT: When I said "Gotcha", I meant that as in "understood" and not in an adversarial manner. Quite the opposite, actually, as this is a respectful thread which I believe both parties have enjoyed.
Not that I subscribe to 'elegance' but...this example was in a recent article on core.match, and it's really terse:

  (with-out-str (doseq [n (range 1 11)]
    (println
      (match [(mod n 3) (mod n 5)]
             [0 0] "FizzBuzz"
             [0 _] "Fizz"
             [_ 0] "Buzz"
             :else n))))