5 ms·
The most elegant implementation of FizzBuzz
- cbhl 10y agoIf you looked at the assembly code, though, there still is a branch (aka an if statement) -- it's just hidden inside the "choice" function.
- viebel 10y agoIndeed. 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!
- schoen 10y agoThis 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))
- viebel 10y agoIt's amazing Python code and clojure code are almost the same. (Except infix/prefix)
- schoen 10y agoPeople don't always write Python in a functional style, but I imagine that the itertools module was created out of admiration (or jealousy) for other languages that can do some of these things. :-)
- viebel 10y agoThe klipse plugin can also features python interactive snippets - using skulpt - http://www.skulpt.org/ http://www.skulpt.org/. Take a look here: http://blog.klipse.tech/klipse/2016/09/09/klipse-languages.html http://blog.klipse.tech/klipse/2016/09/09/klipse-languages.h... But unfortunately, itertools is not (yet?) supported by skulpt :(
- jimmahoney 10y agoHere'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()
- schoen 10y agoThat'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-highlights 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.
- ursus_bonum 10y agoAll I've learned is I can't stand any code called "elegant."
- milesvp 10y agoProbably 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.
- deleted 10y ago[deleted]
- AdieuToLogic 10y agoThat'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)
- viebel 10y agoIt's amazing how scala syntax is verbose - not to say ugly!
- AdieuToLogic 10y agoI 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);
- viebel 10y agoOh! 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#fizzbuzz http://blog.klipse.tech/clojure/2016/10/25/core-match.html#f...
- spacemanmatt 10y agoNot 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))))