25
\$\begingroup\$

Would love some feedback on this. I'm coming from a Java background and kinda feel like I've just done exactly what I would do in Java. Is there a better way?

for (i <- 1 to 100) {
  if ( i % 3 == 0 && i% 5 == 0) 
    println(i +" = FizzBuzz")
  else if (i % 3 == 0) 
    println(i +" = Fizz")
  else if (i % 5 == 0) 
    println(i +" = Buzz")
}
\$\endgroup\$
30
\$\begingroup\$

FizzBuzz is kind of a difficult example because its simplicity means it looks more or less the same in every language. That said, we can go out of our way to emphasize the functional aspect of Scala.

First, we can wrap the core logic of FizzBuzz up in its own function:

def fizzBuzz(x:Int) = {
  if (x % 15 == 0)
    "FizzBuzz"
  else if (x % 3 == 0)
    "Fizz"
  else if (x % 5 == 0)
    "Buzz"
  else
    x
}

Now, we can compose this with printing and map it over the domain we're interested in:

(1 until 100).map(fizzBuzz _ andThen println)

Notice that, where in Java we would probably repeat the println in every case, in Scala, with first-class functions, we can easily separate it out as its own step in the process.

The advantage of this becomes clearer when we think about recombining elements. If we wanted to, we could, for example, join everything in a string without rewriting the essential FizzBuzzzing logic:

println((1 until 100).map(fizzBuzz).mkString(", "))

So, hopefully you can see that Scala's "better way" is first-class functions. With them, we more easily compose the pieces of our system.

\$\endgroup\$
  • 1
    \$\begingroup\$ As a disclaimer, I'm not a Scala guy, so that code can probably be cleaned up a little. Still, the fundamental takeaway is the same. \$\endgroup\$ – Beyamor Feb 2 '14 at 9:59
  • \$\begingroup\$ Hey, that's cool - I like it. This is the kind of response I was looking for. It's really a bit of a shift in thinking, I think that a simple example like FizzBuzz makes it really clear. \$\endgroup\$ – jcm Feb 2 '14 at 11:19
  • 1
    \$\begingroup\$ @cookiemonster you might want to also glance at Higher-order FizzBuzz in Clojure. I think that calculating pi (#18 on 20 controversial programming opinions) because it isn't quite as simple (not hard, but not quite as simple) and really does require some different thinking in the functional world. \$\endgroup\$ – user22048 Feb 2 '14 at 18:20
  • \$\begingroup\$ While x % 15 == 0 and x%3==0 && x%5==0 give the same result, they are NOT conceptually the same. \$\endgroup\$ – jmoreno Oct 27 '14 at 2:39
46
\$\begingroup\$

Or if you prefer match/case to if/else:

(1 until 100).map(i => (i % 3, i % 5) match {
  case (0, 0) => "FizzBuzz"
  case (0, _) => "Fizz"
  case (_, 0) => "Buzz"
  case _ => i
}).foreach(println)

Update:

So what we are doing here is taking list of numbers and mapping them first to tuples where on the left side is that number module 3 and on the right side modulo 5. Then we are matching those tuples to cases where both are zero, left is zero, right is zero or neither is zero.

Also the actual logic is in the map-block and side-effect of printing is in the foreach-block.

\$\endgroup\$
  • 3
    \$\begingroup\$ This is by far the most scala-y solution proposed yet, IMO. map() is preferred over standard for loops, and the match here is much more elegant than the chained if/else. \$\endgroup\$ – geoffjentry Feb 2 '14 at 16:24
  • \$\begingroup\$ Some comments: #1 using 1 until 100 means range 1-99 #2 we don't need map as we are not going to re-use the collection of fizz, buzz, fizzbuzz strings. straightaway foreach would have worked too. \$\endgroup\$ – Dragonborn Sep 23 '16 at 12:00
14
\$\begingroup\$
  • Instead of using both 3 and 5:

    if (i % 3 == 0 && i % 5 == 0)
    

    you can just use 15:

    if (i % 15 == 0)
    
  • You're also supposed to print the number by itself if neither case applies:

    else
        println(i)
    

    For your existing cases, however, you're only supposed to print the message.

Final solution:

for (i <- 1 to 100) {
  if (i % 15 == 0) 
    println("FizzBuzz")
  else if (i % 3 == 0) 
    println("Fizz")
  else if (i % 5 == 0) 
    println("Buzz")
  else
    println(i)
}
\$\endgroup\$
6
\$\begingroup\$

A bit old but here's my two cents: you could use PartialFunction.

Basically you define the functions you want to use and compose them in the proper order:

def f(divisor: Int, result: Int => String): PartialFunction[Int, String] = {
  case i if (i % divisor == 0) => result(i)
}
val f3  = f(3,  _ => "Fizz")
val f5  = f(5,  _ => "Buzz")
val f15 = f(15, x => f3(x) + f5(x)) // DRY
val id  = f(1,  _.toString)

val fizzBuzz = f15 orElse f3 orElse f5 orElse id
(1 to 100).map(fizzBuzz andThen println)

f15 must be called first otherwise f3 or f5 will stop the composition. In the same way id is the last fallback (any number is modulo 1) and print the input (that's why I defined result as a function, and not just a string).

\$\endgroup\$
5
\$\begingroup\$

I tried to come up with a different looking solution. I am not claiming it is better, but I thought I should put it here since this approach might be more appropriate for some applications which are somewhat related to FizzBuzz.

Basically I treat the fizz and buzz problems separately and merge them. I also use a lot of Scala features such as Stream, Option, currying and case matching.

  def transformPeriodic(period: Int, word: String)(counter: Int) =
    if (counter % period == 0) Some(word) else None

  val fizzTransform = transformPeriodic(3, "Fizz") _
  val buzzTransform = transformPeriodic(5, "Buzz") _

  def mergeFizzBuzz(fizzOpt: Option[String], buzzOpt: Option[String]): Option[String] =   {
    (fizzOpt, buzzOpt) match {
       case (None, None) => None
       case _ => Some(fizzOpt.getOrElse("") + buzzOpt.getOrElse(""))
    }
  }

  val streamInt = Stream.from(1)
  val tripletStream = streamInt.map(i => (i, fizzTransform(i), buzzTransform(i)))
  val doubletStream = tripletStream.map { case (i, fizzOpt, buzzOpt) => (i, mergeFizzBuzz(fizzOpt, buzzOpt)) }
  val fizzBuzzStream = doubletStream.map { case (i, fizzBuzzOpt) => fizzBuzzOpt.getOrElse(i) }

  fizzBuzzStream take 15 foreach println
\$\endgroup\$
2
\$\begingroup\$

This takes advantage of scala's functional types:

val wordMap = Seq(3 -> "Fizz", 5 -> "Buzz")
(1 to 100).map(i => {
  Some(
    wordMap.collect({ case (num, str) if i % num == 0 => str}).mkString
  ).filterNot(_.isEmpty).getOrElse(i.toString)
}).foreach(println)

It's also easily extensible if additional number to word mappings are included (e.g. also print "Bazz" for all numbers divisible by 7, and "Ten" for all numbers divisible by 10), just change the wordMap:

val wordMap = Seq(3 -> "Fizz", 5 -> "Buzz", 7 -> "Bazz", 10 -> "Ten")
(1 to 100).map(i => {
  Some(
    wordMap.collect({ case (num, str) if i % num == 0 => str}).mkString
  ).filterNot(_.isEmpty).getOrElse(i.toString)
}).foreach(println)

Some related literature: Overthinking FizzBuzz with Monads

\$\endgroup\$
1
\$\begingroup\$

We'd usually make the solution more functional. That is, move the "fizzbuzz" logic into something that returns a string, and use that:

def fizzbuzz(i: Int) = 
  if (i % 3 == 0 && i % 5 == 0) "FizzBuzz"
  else if (i % 3 == 0) "Fizz"
  else if (i % 5 == 0) "Buzz"
  else s"$i"

for (i <- 1 to 100) println(fizzbuzz(i))

It's a small thing, but it goes to the core of the philosophical difference.

\$\endgroup\$

protected by Jamal Sep 14 '17 at 2:06

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.