在 Scala 中为经常遇到的问题建立了哪些常见的模式/解决方案
Posted
技术标签:
【中文标题】在 Scala 中为经常遇到的问题建立了哪些常见的模式/解决方案【英文标题】:What common patterns/solutions have been established in Scala for frequently-encountered problems 【发布时间】:2011-05-15 16:32:29 【问题描述】:我会开始滚动的。
给定一个元素序列,其中一些元素可以包含多次,一个典型的要求是对它们进行计数 - 以计数或直方图的形式。
经常引用的解决方案是:
ss.groupBy(identity).mapValues(_.size)
那么对于 Scala 中类似的常见问题,还有哪些其他解决方案?
【问题讨论】:
【参考方案1】:我多次错过了为 Scala 集合生成笛卡尔积的方法。在 Haskell 中你可以写
import Control.Applicative
(,) <$> [1,2,3] <*> ["a","b"]
-- [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]
Scala 解决方案
for(x <- List(1,2,3); y <- List("a","b")) yield (x,y)
太笨拙了。
【讨论】:
斯卡拉兹:(List(1, 2, 3) |@| List("a", "b"))((_,_))
或者 scalaz: (xs ⊛ ys) ×(_)(_)
如果涉及到那【参考方案2】:
不是我又在敲同样的鼓,而是……
我们有许多进程可能会产生成功输出或失败并显示一些错误消息的问题的解决方案。目标是汇总成功的结果,如果所有流程都成功,如果有一个或多个失败,则汇总所有错误消息。
这可以通过 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
【讨论】:
【参考方案3】:如果某个条件cond
成立,则返回Some(x)
,否则返回None
:
Some(x) filter cond
[摘自 Paul Phillips 在邮件列表中的帖子]
【讨论】:
【参考方案4】:无耻地从 oxbow_lakes 对这个问题的回答中窃取:Instantiating a case class from a list of parameters
使用元组调用方法/函数以提供参数:
case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)
这可以用于任何功能。
【讨论】:
【参考方案5】:使用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
的集合,我想总结它们:
val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(
但为了做到这一点,我需要有一个隐含的Monoid
。但是当然,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 :-)
【讨论】:
我最喜欢这个!这是我的问题,所以我可以主观:)【参考方案6】:有时您必须使用 while 而不是 for,而且您通常正在收集结果:
val buf = new Listbuffer[Int]
while(cond())
val x = fancyStuff()
buf += calculation(x)
我认为让while
有与for
一样的“屈服”某些东西的可能性会非常有帮助,消除命令式和函数式风格之间的一些毛刺:
val buf = while(cond())
val x = fancyStuff()
yield calculation(x)
【讨论】:
以上是关于在 Scala 中为经常遇到的问题建立了哪些常见的模式/解决方案的主要内容,如果未能解决你的问题,请参考以下文章