一个Scala中最强大的模式是充实,我的图书馆*模式,它采用隐式转换为出现添加方法,以现有的类,而不需要动态方法解析.例如,如果我们希望所有字符串都有spaces
计算他们有多少个空格字符的方法,我们可以:
class SpaceCounter(s: String) {
def spaces = s.count(_.isWhitespace)
}
implicit def string_counts_spaces(s: String) = new SpaceCounter(s)
scala> "How many spaces do I have?".spaces
res1: Int = 5
Run Code Online (Sandbox Code Playgroud)
不幸的是,这种模式在处理泛型集合时遇到了麻烦.例如,已经询问了许多关于按顺序对项目进行分组的问题.没有内置的东西可以一次性工作,所以这似乎是使用泛型集合C
和泛型元素类型的rich-my-library模式的理想候选者A
:
class SequentiallyGroupingCollection[A, C[A] <: Seq[A]](ca: C[A]) {
def groupIdentical: C[C[A]] = {
if (ca.isEmpty) C.empty[C[A]]
else {
val first = ca.head
val (same,rest) = ca.span(_ == first)
same +: (new SequentiallyGroupingCollection(rest)).groupIdentical
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,除了它不起作用.REPL告诉我们:
<console>:12: error: not found: …
Run Code Online (Sandbox Code Playgroud) 假设有人想建立一个新的通用类,Novel[A]
.这个类将包含许多有用的方法 - 也许它是一种集合 - 因此你想要将它子类化.但是您希望方法返回子类的类型,而不是原始类型.在Scala 2.8中,为了使该类的方法返回相关的子类而不是原始类,必须做的最小工作量是多少?例如,
class Novel[A] /* What goes here? */ {
/* Must you have stuff here? */
def reverse/* What goes here instead of :Novel[A]? */ = //...
def revrev/*?*/ = reverse.reverse
}
class ShortStory[A] extends Novel[A] /* What goes here? */ {
override def reverse: /*?*/ = //...
}
val ss = new ShortStory[String]
val ss2 = ss.revrev // Type had better be ShortStory[String], not Novel[String]
Run Code Online (Sandbox Code Playgroud)
如果你想要Novel
协变,这个最小量会改变吗?
(2.8这些集合除了其他内容之外还会执行此操作,但它们也会以更加花哨(和有用)的方式使用返回类型 - 问题是如果一个人只想要这个子类型 - …
collections scala scala-2.8 higher-kinded-types enrich-my-library
在Scala 2.9中为库类添加自定义方法(丰富或"皮条客")我不得不写这样的东西:
object StringPimper {
implicit def pimpString(s: String) = new {
def greet: String = "Hello " + s
}
}
Run Code Online (Sandbox Code Playgroud)
随着Scala 2.10的发布,我读到它引入了隐式类定义,从理论上讲,它是通过在返回匿名类对象的隐式方法中消除需要来简化上述任务.我以为这会让我写出来
implicit class PimpedString(s: String) {
def greet: String = "Hello " + s
}
Run Code Online (Sandbox Code Playgroud)
这对我来说看起来更漂亮.但是,这样的定义会导致编译错误:
`implicit' modifier cannot be used for top-level objects
Run Code Online (Sandbox Code Playgroud)
通过将代码再次包装在对象中来解决:
object StringPimper {
implicit class PimpedString(s: String) {
def greet: String = "Hello " + s
}
}
Run Code Online (Sandbox Code Playgroud)
不用说,这几乎抵消了改善的意义.
那么,有没有办法把它写得更短?要摆脱包装器对象?
我实际上有一个MyApp.pimps
所有皮条客都去的包(我没有太多,如果我有的话,我会使用一些单独的包)而且我厌倦了导入MyApp.pimps.StringPimper._
而不是MyApp.pimps.PimpedString
或MyApp.pimps._
.当然,我可以将所有隐式类放在一个包装器对象中,但这意味着将它们全部放在一个文件中,这将是相当长的 - 非常难看的解决方案.
Scala 2.10引入了值类,您可以通过使类扩展来指定它们AnyVal
.值类有许多限制,但它们的一个巨大优点是它们允许扩展方法而没有创建新类的惩罚:除非需要装箱,例如将值类放在数组中,它只是旧类加上一组将类作为第一个参数的方法.从而,
implicit class Foo(val i: Int) extends AnyVal {
def +*(j: Int) = i + j*j
}
Run Code Online (Sandbox Code Playgroud)
解开一些不比写i + j*j
自己更贵的东西(一旦JVM内联方法调用).
不幸的是,SIP-15中描述值类的一个限制是
- C的基础类型可能不是值类.
如果你有一个值类,你可以开始,比如说,作为一种提供类型安全单位而不需要拳击开销的方法(除非你真的需要它):
class Meter(val meters: Double) extends AnyVal {
def centimeters = meters*100.0 // No longer type-safe
def +(m: Meter) = new Meter(meters+m.meters) // Only works with Meter!
}
Run Code Online (Sandbox Code Playgroud)
那么有没有办法在Meter
没有对象创建开销的情况下进行充实?SIP-15的限制防止了明显的限制
implicit class RichMeter(m: Meter) extends AnyVal { ... }
Run Code Online (Sandbox Code Playgroud)
做法.
我希望扩展迭代器来创建一个新方法takeWhileInclusive
,它将像takeWhile
最后一个元素一样运行.
我的问题是扩展迭代器以返回一个我希望延迟评估的新迭代器的最佳实践.来自C#背景我正常使用IEnumerable
并使用yield
关键字,但这样的选项似乎不存在于Scala中.
例如,我可以
List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6)
Run Code Online (Sandbox Code Playgroud)
所以在这种情况下,takeWhileInclusive
只有解析值的谓词,直到我得到大于6的结果,并且它将包括第一个结果
到目前为止我有:
object ImplicitIterator {
implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i)
}
class IteratorExtension[T <: Any](i : Iterator[T]) {
def takeWhileInclusive(predicate:(T) => Boolean) = ?
}
Run Code Online (Sandbox Code Playgroud) 我最近发现可以使用Pimp Enrich My Library模式使用.type
以下方法向伴随对象添加方法:
object Whatever { }
implicit class WhateverExtensions(val obj: Whatever.type) {
def greet = println("Hi!")
}
Whatever.greet
Run Code Online (Sandbox Code Playgroud)
不幸的是,这似乎不适用于包对象,如scala.math
:
implicit class MathExtensions(val obj: scala.math.type) {
def min(x: Money, y: Money): Money = ???
}
Run Code Online (Sandbox Code Playgroud)
我得到以下编译器错误:
Error:(153, 47) type mismatch; found : math.type required: AnyRef Note that math extends Any, not AnyRef. Such types can participate in value classes, but instances cannot appear in singleton types or in reference comparisons. implicit class MathExtensions(val obj: …
该toInt
在方法StringLike
不带任何参数,并且只能在小数解析.因此,要解析二进制,十六进制等,我们需要求助于Java Integer#parseInt(String s, int radix)
.
为了解决这种状况,我尝试了以下方法
implicit def strToToIntable(s: String) = new {
def toInt(n: Int) = Integer.parseInt(s, n)
}
Run Code Online (Sandbox Code Playgroud)
然而,
"101".toInt(2)
Run Code Online (Sandbox Code Playgroud)
导致REPL编译器"崩溃"并且在编译的代码中也不起作用.
使用"丰富我的库"模式重载现有方法是否存在一些限制?
这是实现它的两种不同方式.
一个更短
implicit def toR1(s:String) = new { def getLength = s.length)}
Run Code Online (Sandbox Code Playgroud)
第二是更长
class R2(s:String){def getLength2 = s.length)}
implicit def toR2(s:String) = new R2(s)
Run Code Online (Sandbox Code Playgroud)
哪一个更好?
我试图弄清楚如何编写一个适用于任何函数的函数交换函数Traversable[_]
,给定一个集合和交换索引.我想出了以下内容:
def swap[A, CC <% Traversable[A]](xs: CC, i: Int, j: Int): Traversable[A] = {
xs.slice(0, i) ++
xs.slice(j, j+1) ++
xs.slice(i+1, j) ++
xs.slice(i, i+1) ++
xs.slice(j+1, xs.size)
}
swap(List(1,2,3,4,5), 0, 4) // => List(5,2,3,4,1)
Run Code Online (Sandbox Code Playgroud)
我想知道如何将它变成Traversable的隐式扩展,让我可以调用它List(1,2,3,4,5).swap(0, 4)
.我能得到的最接近的是:
import language.implicitConversions
class RichTraversable[A, B <% Traversable[A]](xs: B) {
def swap(i: Int, j: Int): Traversable[A] = {
xs.slice(0, i) ++
xs.slice(j, j+1) ++
xs.slice(i+1, j) ++
xs.slice(i, i+1) ++
xs.slice(j+1, xs.size)
}
}
implicit def richTraversable[A, B <% Traversable[A]](ys: …
Run Code Online (Sandbox Code Playgroud) 我有一个来自图书馆的课程(具体而言com.twitter.finagle.mdns.MDNSResolver
).我想扩展类(我希望它返回Future [Set],而不是Try [Group]).
当然,我知道我可以对它进行子类化并在那里添加我的方法.但是,我正在尝试学习Scala,这似乎是尝试新事物的机会.
我认为这可能是可能的原因是JavaConverters
.以下代码:
class Test {
var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}
Run Code Online (Sandbox Code Playgroud)
不编译,因为asScala
Java上没有方法ArrayList
.但是如果我导入一些新的定义:
class Test {
import collection.JavaConverters._
var lst:Buffer[Nothing] = (new java.util.ArrayList()).asScala
}
Run Code Online (Sandbox Code Playgroud)
然后突然有是一种asScala
方法.所以看起来这个ArrayList
类是透明地扩展的.
我理解JavaConverters
正确的行为吗?我可以(并且应该)复制该方法吗?
scala ×10
collections ×2
extend ×1
implicit ×1
iterator ×1
overloading ×1
scala-2.8 ×1
types ×1
value-class ×1