我将开始滚动。
给定一系列元素,其中一些元素可以包含多次,一个典型的要求是对它们进行计数 - 以计数或直方图的形式。
经常引用的解决方案是:
ss.groupBy(identity).mapValues(_.size)
那么对于 Scala 中类似的常见问题,还有哪些其他解决方案呢?
我将开始滚动。
给定一系列元素,其中一些元素可以包含多次,一个典型的要求是对它们进行计数 - 以计数或直方图的形式。
经常引用的解决方案是:
ss.groupBy(identity).mapValues(_.size)
那么对于 Scala 中类似的常见问题,还有哪些其他解决方案呢?
并不是说我又在敲同样的鼓,而是……
对于我们有许多可能产生成功输出或失败并显示一些错误消息的进程的问题的解决方案。目标是聚合成功的结果,如果所有进程都产生成功并且如果一个或多个失败,则聚合所有错误消息。
这可以通过scalaz验证来解决:首先,设置一些导入
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
现在让我们定义我们的“流程”
scala> def fooI(s : String) : ValidationNEL[Exception, Int] = s.parseInt.liftFailNel
fooI: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Int]
scala> def fooF(s : String) : ValidationNEL[Exception, Float] = s.parseFloat.liftFailNel
fooF: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Float]
scala> def fooB(s : String) : ValidationNEL[Exception, Boolean] = s.parseBoolean.liftFailNel
fooB: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Boolean]
现在用于Applicative
聚合失败/成功:
scala> def attempt(ss : String*) = (fooI(ss(0)) <|**|> (fooF(ss(1)), fooB(ss(2)))) match {
| case Success((i, f, b)) => println("Found " + i + " " + f + " " + b)
| case Failure(es) => es foreach println
| }
attempt: (ss: String*)Unit
现在让我们尝试一些失败:
scala> attempt("a", "b", "true")
java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "b"
现在让我们尝试成功:
scala> attempt("1", "2.3", "false")
Found 1 2.3 false
我错过了好几次为 Scala 集合生成笛卡尔积的方法。在 Haskell 中你可以写
import Control.Applicative
(,) <$> [1,2,3] <*> ["a","b"]
-- [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]
斯卡拉解决方案
for(x <- List(1,2,3); y <- List("a","b")) yield (x,y)
太笨拙了。
无耻地从 oxbow_lakes 对这个问题的回答中窃取:从参数列表中实例化案例类
使用元组调用方法/函数以提供参数:
case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)
这可用于任何功能。
使用Monoid
s 或Numeric
s 为富类定义合理的操作,必要时使用隐式。
case class Money(ccy: Currency, amount : BigDecimal) {
def +(that : Money) = {
require(this.currency == that.curency)
copy(amount = this.amount + that.amount)
}
def abs = copy(amount = amount.abs)
}
那么假设我有一个Money
's 的集合,我想总结它们:
val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(
但为了做到这一点,我需要有一个隐含的Monoid
. 但是,当然,只有当我已经有一些货币时,a 的零值Money
才有意义!
implicit def CurrencyMonoid(implicit currency : Currency) = new Monoid[Currency] {
def zero = Money(currency, 0)
def append(m1 : Money, m2 : Money) = m1 + m2
}
所以现在 Scala 将使用这两个隐式:
implicit val usd = Currency.USD
val turnover = trades.map(_.usdValue.abs).∑ //yay for monoids :-)
如果某个条件cond
成立 return Some(x)
,否则 return None
:
Some(x) filter cond
[摘自 Paul Phillips 在邮件列表中的帖子]
有时您必须使用 while 而不是 for,而且通常您正在收集结果:
val buf = new Listbuffer[Int]
while(cond()) {
val x = fancyStuff()
buf += calculation(x)
}
while
我认为拥有与 for相同的可能性来“屈服”某些东西会非常有帮助for
,消除命令式和函数式风格之间的一些毛刺:
val buf = while(cond()) {
val x = fancyStuff()
} yield calculation(x)