希望对此有一些反馈。我来自Java背景,有点感觉就像我在Java中所做的一样。有更好的方法吗?

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")
}


#1 楼

FizzBu​​zz是一个困难的例子,因为它的简单性意味着每种语言在外观上都差不多。就是说,我们可以全力以赴强调Scala的功能方面。

首先,我们可以将FizzBu​​zz的核心逻辑包装在其自身的功能中:

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


现在,我们可以通过打印进行组合,并将其映射到我们感兴趣的领域:

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


请注意,在哪里在Java中,我们可能会在每种情况下都重复使用一流的功能在Scala中使用println,我们可以轻松地将其分离出来作为过程中自己的步骤。

当这种情况的优点变得更加明显时,我们考虑重新组合元素。例如,如果我们愿意,我们可以将所有内容连接成一个字符串,而无需重写基本的FizzBu​​zzzing逻辑:

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


因此,希望您可以看到Scala的“更好”方式”是一流的功能。有了它们,我们可以更轻松地组成系统的各个部分。

评论


\ $ \ begingroup \ $
作为免责声明,我不是Scala专家,因此可以对代码进行一些清理。尽管如此,基本要点还是一样。
\ $ \ endgroup \ $
– Beyamor
2014年2月2日,9:59

\ $ \ begingroup \ $
嘿,这很酷-我喜欢。这是我一直在寻找的回应。思想上确实有点转变,我认为像FizzBu​​zz这样的简单示例就很清楚了。
\ $ \ endgroup \ $
– jcm
2014年2月2日,11:19



\ $ \ begingroup \ $
@cookiemonster,您可能还想看看Clojure中的高阶FizzBu​​zz。我认为计算pi(在20个有争议的编程意见中排名第18)是因为它不那么简单(不难,但不那么简单),实际上在功能世界中确实需要一些不同的思考。
\ $ \ endgroup \ $
–user22048
2014年2月2日在18:20

\ $ \ begingroup \ $
尽管x%15 == 0和x%3 == 0 && x%5 == 0得出相同的结果,但它们在概念上并不相同。
\ $ \ endgroup \ $
– jmoreno
14-10-27在2:39

#2 楼

或者,如果您更喜欢匹配/大小写而不是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)


更新:

所以我们在这里正在做的是数字列表并将它们首先映射到元组,其中左侧是数字模块3,右侧是模5。然后,将这些元组匹配到两个都是零,左为零,右为零或都不为零的情况。

实际的逻辑也位于映射块中,而打印的副作用在foreach块中。

评论


\ $ \ begingroup \ $
IMO是迄今为止提出的最灵活的解决方案。 map()优于循环标准,并且此处的匹配比链式if / else优雅得多。
\ $ \ endgroup \ $
–geoffjentry
2014年2月2日在16:24

\ $ \ begingroup \ $
一些评论:#1使用1到100表示​​范围1-99#2我们不需要映射,因为我们不会重复使用fizz,buzz和fizzbuzz字符串的集合。直截了当的foreach也行得通。
\ $ \ endgroup \ $
–龙婆
16/09/23在12:00



#3 楼



而不是同时使用3和5:

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


您可以只使用15:

if (i % 15 == 0)



如果两种情况都不适用,您也应该自己打印数字:

else
    println(i)


对于现有的情况,您只应打印该消息。


最终解决方案:

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)
}


#4 楼

有点老了,但是这是我的两分钱:您可以使用PartialFunction。

基本上,您可以定义要使用的函数并以适当的顺序进行组合:

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,否则f3f5将停止合成。
以同样的方式,id是最后一个后备时间(任何数字都是模1)并打印输入(这就是为什么我定义result作为一个函数,而不仅仅是一个字符串。)

#5 楼

我试图提出一种不同的解决方案。我并不是说它会更好,但是我认为我应该把它放在这里,因为这种方法可能更适合于某些与FizzBu​​zz相关的应用程序。

基本上我会分别处理fizz和buzz问题并合并它们。我还使用了很多Scala功能,例如Stream,Option,currying和case匹配。

  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


#6 楼

这利用了scala的功能类型:

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)


如果包含附加的数字到单词的映射,它也很容易扩展(例如,对于被7整除的所有数字,也打印"Bazz"。所有数字都被“ 10”除以10的整数),只需更改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)


一些相关文献:用Monad过度思考FizzBu​​zz

#7 楼

通常,我们将使该解决方案更加实用。也就是说,将“ fizzbuzz”逻辑移动到返回字符串的内容中,然后使用该代码:

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))


这是一件小事,但它涉及到哲学上的差异。