每个Scala开发人员都应注意的Scala的隐藏功能是什么?
请为每个答案提供一项隐藏功能。
每个Scala开发人员都应注意的Scala的隐藏功能是什么?
请为每个答案提供一项隐藏功能。
Answers:
好的,我必须再添加一个。Regex
Scala中的每个对象都有一个提取器(请参见上面的oxbox_lakes的答案),可让您访问匹配组。因此,您可以执行以下操作:
// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"
如果您不习惯使用模式匹配和提取器,第二行看起来会令人困惑。无论何时定义val
或var
,关键字后面的内容不仅是标识符,而且是模式。这就是为什么这样工作:
val (a, b, c) = (1, 3.14159, "Hello, world")
右边的表达式创建一个Tuple3[Int, Double, String]
可以匹配模式的(a, b, c)
。
在大多数情况下,您的模式都使用属于单例对象成员的提取器。例如,如果您编写类似
Some(value)
那么您将隐式调用提取器Some.unapply
。
但是您也可以在模式中使用类实例,而这就是这里发生的情况。val regex是的实例Regex
,当您在模式中使用val 时,会隐式调用regex.unapplySeq
(unapply
vs unapplySeq
超出了此答案的范围),它将匹配组提取到中,将其匹配Seq[String]
元素分配给变量年,月和日。
结构类型定义-即通过其支持的方法描述的类型。例如:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
请注意,除了具有方法之外,未定义参数的类型closeable
close
例如,如果没有此功能,则可以表达在一个列表上映射一个函数以返回另一个列表,或在一个树上映射一个函数以返回另一个树的想法。但你不能表达这种想法通常不高于种。
使用更高的种类,您可以捕获使用其他类型参数化的任何类型的想法。带有一个参数的类型构造函数被称为同类(*->*)
。例如,List
。返回另一个类型构造器的类型构造器被称为实物(*->*->*)
。例如,Function1
。但是在Scala中,我们有更多的种类,因此我们可以使用其他类型构造函数对类型构造函数进行参数化。所以他们很像((*->*)->*)
。
例如:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
现在,如果您有Functor[List]
,则可以映射列表。如果有Functor[Tree]
,则可以在树上进行映射。但更重要的是,如果您拥有Functor[A]
A的任何一种(*->*)
,都可以将函数映射到A
。
提取器,可让您将杂乱的if-elseif-else
样式代码替换为模式。我知道这些并没有完全隐藏,但是我已经使用Scala几个月了,但并没有真正了解它们的功能。对于(很长的)示例,我可以替换为:
val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
//e.g. GBP20090625.FWD
p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
p = ps.lookupProductByRic(code)
}
有了这个,我认为这要清楚得多
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
我必须在后台做一些腿部工作...
object SyntheticCodes {
// Synthetic Code for a CashProduct
object Cash extends (CashProduct => String) {
def apply(p: CashProduct) = p.currency.name + "="
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
if (s.endsWith("=")
Some(ps.findCash(s.substring(0,3)))
else None
}
}
//Synthetic Code for a ForwardProduct
object Forward extends (ForwardProduct => String) {
def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
if (s.endsWith(".FWD")
Some(ps.findForward(s.substring(0,3), s.substring(3, 9))
else None
}
}
但是,将其将业务逻辑分离到一个明智的地方这一事实值得这样做。我可以实现Product.getCode
以下方法。
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
清单是一种在运行时获取类型信息的方式,就像Scala具有已归类的类型一样。
案例类会自动混入Product特质,从而提供对字段的无类型索引访问,而不会产生任何反射:
case class Person(name: String, age: Int)
val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)
此功能还提供了一种简化方法来更改toString
方法的输出:
case class Person(name: String, age: Int) {
override def productPrefix = "person: "
}
// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28))
它不是完全隐藏的,但肯定是一个广告不足的功能:scalac -Xprint。
为了说明用途,请考虑以下来源:
class A { "xx".r }
使用scalac -Xprint:typer输出进行编译:
package <empty> {
class A extends java.lang.Object with ScalaObject {
def this(): A = {
A.super.this();
()
};
scala.this.Predef.augmentString("xx").r
}
}
请注意scala.this.Predef.augmentString("xx").r
,这是implicit def augmentString
Predef.scala中目前的应用程序。
scalac -Xprint:<phase>将在某个编译器阶段之后打印语法树。要查看可用阶段,请使用scalac -Xshow-phases。
这是了解幕后情况的好方法。
试试看
case class X(a:Int,b:String)
使用typer阶段可以真正感受到它的用处。
您可以定义自己的控制结构。它实际上只是功能和对象以及一些语法糖,但它们的外观和行为类似于真实的事物。
例如,以下代码定义dont {...} unless (cond)
和dont {...} until (cond)
:
def dont(code: => Unit) = new DontCommand(code)
class DontCommand(code: => Unit) {
def unless(condition: => Boolean) =
if (condition) code
def until(condition: => Boolean) = {
while (!condition) {}
code
}
}
现在,您可以执行以下操作:
/* This will only get executed if the condition is true */
dont {
println("Yep, 2 really is greater than 1.")
} unless (2 > 1)
/* Just a helper function */
var number = 0;
def nextNumber() = {
number += 1
println(number)
number
}
/* This will not be printed until the condition is met. */
dont {
println("Done counting to 5!")
} until (nextNumber() == 5)
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
。需要斯卡拉兹。
@switch
Scala 2.8中的注释:
应用于匹配表达式的注释。如果存在,则编译器将验证匹配是否已编译到tableswitch或lookupswitch,并且如果将其编译为一系列条件表达式,则发出错误。
例:
scala> val n = 3
n: Int = 3
scala> import annotation.switch
import annotation.switch
scala> val s = (n: @switch) match {
| case 3 => "Three"
| case _ => "NoThree"
| }
<console>:6: error: could not emit switch for @switch annotated match
val s = (n: @switch) match {
邓诺(Dunno),如果这确实是隐藏的,但我觉得很好。
带有2个类型参数的类型构造函数可以用中缀表示法编写
object Main {
class FooBar[A, B]
def main(args: Array[String]): Unit = {
var x: FooBar[Int, BigInt] = null
var y: Int FooBar BigInt = null
}
}
var foo2barConverter: Foo ConvertTo Bar
,将使类型参数的顺序显而易见。
Scala 2.8引入了默认和命名参数,这使得可以添加新的“复制”方法,Scala将其添加到案例类中。如果您定义此:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
并且您想要创建一个与现有Foo相似的新Foo,但仅具有不同的“ n”值,那么您可以说:
foo.copy(n = 3)
扩展语言。我一直想在Java中做这样的事情(不能)。但是在Scala中,我可以拥有:
def timed[T](thunk: => T) = {
val t1 = System.nanoTime
val ret = thunk
val time = System.nanoTime - t1
println("Executed in: " + time/1000000.0 + " millisec")
ret
}
然后写:
val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed { // "timed" is a new "keyword"!
numbers.sortWith(_<_)
}
println(sorted)
并得到
Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
您可以为函数指定一个按名称调用参数(编辑:此参数不同于延迟参数!),直到该函数使用该参数时才对其求值(编辑:实际上,每次使用该函数时都会对其进行重新评估。用过的)。见这个常见问题的详细信息
class Bar(i:Int) {
println("constructing bar " + i)
override def toString():String = {
"bar with value: " + i
}
}
// NOTE the => in the method declaration. It indicates a lazy paramter
def foo(x: => Bar) = {
println("foo called")
println("bar: " + x)
}
foo(new Bar(22))
/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
lazy val xx: Bar = x
在方法中那样做某事,并且从那一刻起您才使用它,则可以将其用作惰性参数xx
。
您可以locally
用来引入局部块而不会引起分号推断问题。
用法:
scala> case class Dog(name: String) {
| def bark() {
| println("Bow Vow")
| }
| }
defined class Dog
scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)
scala> locally {
| import d._
| bark()
| bark()
| }
Bow Vow
Bow Vow
locally
在“ Predef.scala”中定义为:
@inline def locally[T](x: T): T = x
内联,不增加任何额外的开销。
trait AbstractT2 {
println("In AbstractT2:")
val value: Int
val inverse = 1.0/value
println("AbstractT2: value = "+value+", inverse = "+inverse)
}
val c2c = new {
// Only initializations are allowed in pre-init. blocks.
// println("In c2c:")
val value = 10
} with AbstractT2
println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)
输出:
In AbstractT2:
AbstractT2: value = 10, inverse = 0.1
c2c.value = 10, inverse = 0.1
我们实例化一个匿名内部类,
value
在with AbstractT2
子句之前初始化块中的字段。如运行脚本时所示,这可以确保value
在执行主体之前对其进行初始化AbstractT2
。
您可以使用“ with”关键字组成结构类型
object Main {
type A = {def foo: Unit}
type B = {def bar: Unit}
type C = A with B
class myA {
def foo: Unit = println("myA.foo")
}
class myB {
def bar: Unit = println("myB.bar")
}
class myC extends myB {
def foo: Unit = println("myC.foo")
}
def main(args: Array[String]): Unit = {
val a: A = new myA
a.foo
val b: C = new myC
b.bar
b.foo
}
}
匿名函数的占位符语法
从Scala语言规范中:
SimpleExpr1 ::= '_'
一个表达式(属于语法类别
Expr
)可以_
在标识符合法的位置包含嵌入的下划线符号。这样的表达式表示一个匿名函数,其后出现的下划线表示连续的参数。
_ + 1 x => x + 1
_ * _ (x1, x2) => x1 * x2
(_: Int) * 2 (x: Int) => x * 2
if (_) x else y z => if (z) x else y
_.map(f) x => x.map(f)
_.map(_ + 1) x => x.map(y => y + 1)
使用此方法,您可以执行以下操作:
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
隐式定义,特别是转换。
例如,假设有一个函数将输入字符串的中间部分替换为“ ...”,从而格式化输入字符串以适合其大小:
def sizeBoundedString(s: String, n: Int): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
您可以将其与任何String一起使用,并且当然可以使用toString方法转换任何内容。但是您也可以这样写:
def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
然后,您可以通过执行以下操作来传递其他类型的类:
implicit def double2String(d: Double) = d.toString
现在,您可以调用该函数并传递一个double值:
sizeBoundedString(12345.12345D, 8)
最后一个参数是隐式的,并且由于隐式de声明而被自动传递。此外,在sizeBoundedString内部将 “ s” 当作 String对待,因为存在从它到String的隐式转换。
更好地为不常见的类型定义此类型的隐式,以避免意外的转换。您还可以显式传递一个转换,它仍将在sizeBoundedString内部隐式使用:
sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)
您也可以具有多个隐式参数,但是您必须要么全部传递它们,要么不传递任何一个。还有一个用于隐式转换的快捷语法:
def sizeBoundedString[T <% String](s: T, n: Int): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
使用方法完全相同。
隐式可以有任何价值。例如,它们可以用于隐藏库信息。以以下示例为例:
case class Daemon(name: String) {
def log(msg: String) = println(name+": "+msg)
}
object DefaultDaemon extends Daemon("Default")
trait Logger {
private var logd: Option[Daemon] = None
implicit def daemon: Daemon = logd getOrElse DefaultDaemon
def logTo(daemon: Daemon) =
if (logd == None) logd = Some(daemon)
else throw new IllegalArgumentException
def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}
class X extends Logger {
logTo(Daemon("X Daemon"))
def f = {
log("f called")
println("Stuff")
}
def g = {
log("g called")(DefaultDaemon)
}
}
class Y extends Logger {
def f = {
log("f called")
println("Stuff")
}
}
在此示例中,在Y对象中调用“ f”会将日志发送到默认守护程序,在X的实例上将日志发送到Daemon X守护程序。但是,在X的实例上调用g会将日志发送到显式给定的DefaultDaemon。
尽管可以使用重载和私有状态来重写此简单示例,但是隐式不需要私有状态,并且可以通过导入将其带入上下文。
也许不太隐藏,但是我认为这很有用:
@scala.reflect.BeanProperty
var firstName:String = _
这将自动为与bean约定匹配的字段生成一个getter和setter方法。
developerWorks上的进一步描述
使用Scala的构建无限的数据结构Stream
:http :
//www.codecommit.com/blog/scala/infinite-lists-for-the-finitely- Patient
结果类型取决于隐式分辨率。这可以给您多种分派的形式:
scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc
scala> implicit val stringToInt = new PerformFunc[String,Int] {
def perform(a : String) = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137
scala> implicit val intToDouble = new PerformFunc[Int,Double] {
def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4
scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B
scala> foo("HAI")
res16: Int = 5
scala> foo(1)
res17: Double = 1.0
foo
使用的定义a
。我想你是故意的z.perform(x)
。
Scala允许您使用该类的主体(构造函数)创建一个匿名子类,该主体包含用于初始化该类实例的语句。
当构建基于组件的用户界面(例如Swing和Vaadin)时,此模式非常有用,因为它允许创建UI组件并更简洁地声明其属性。
这是创建Vaadin按钮的示例:
val button = new Button("Click me"){
setWidth("20px")
setDescription("Click on this")
setIcon(new ThemeResource("icons/ok.png"))
}
import
声明中排除成员假设您要使用Logger
包含println
和printerr
方法的,但只希望将其用于错误消息,而将旧的保留Predef.println
为标准输出。您可以这样做:
val logger = new Logger(...)
import logger.printerr
但是如果logger
还包含您要导入和使用的另外十二种方法,则列出它们很不方便。您可以尝试:
import logger.{println => donotuseprintlnt, _}
但这仍然“污染”了导入成员的列表。输入超级强大的通配符:
import logger.{println => _, _}
这将做正确的事 ™。
require
方法(在中定义Predef
),使您可以定义在运行时将检查的其他功能约束。想象一下,您正在开发另一个Twitter客户端,并且您需要将tweet长度限制为最多140个符号。此外,您不能发布空的推文。
def post(tweet: String) = {
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
}
现在使用不适当的length参数调用post将导致异常:
scala> post("that's ok")
that's ok
scala> post("")
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:145)
at .post(<console>:8)
scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet")
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:145)
at .post(<console>:8)
您可以编写多个需求,甚至可以为每个需求添加描述:
def post(tweet: String) = {
require(tweet.length > 0, "too short message")
require(tweet.length < 140, "too long message")
println(tweet)
}
现在,异常很冗长:
scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:8)
这里还有一个例子。
每当需求失败时,您都可以执行操作:
scala> var errorcount = 0
errorcount: Int = 0
def post(tweet: String) = {
require(tweet.length > 0, {errorcount+=1})
println(tweet)
}
scala> errorcount
res14: Int = 0
scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:9)
...
scala> errorcount
res16: Int = 1
require
不是保留字。但这只是在中定义的方法Predef
。
具有abstract override
方法的特性是Scala的一项功能,没有像其他许多功能那样广为宣传。使用abstract override
修饰符的方法的目的是进行一些操作并将委托委托给super
。然后,必须将这些特征与它们的abstract override
方法的具体实现混合在一起。
trait A {
def a(s : String) : String
}
trait TimingA extends A {
abstract override def a(s : String) = {
val start = System.currentTimeMillis
val result = super.a(s)
val dur = System.currentTimeMillis-start
println("Executed a in %s ms".format(dur))
result
}
}
trait ParameterPrintingA extends A {
abstract override def a(s : String) = {
println("Called a with s=%s".format(s))
super.a(s)
}
}
trait ImplementingA extends A {
def a(s: String) = s.reverse
}
scala> val a = new ImplementingA with TimingA with ParameterPrintingA
scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a
虽然我的示例实际上只不过是一个穷人的AOP,但我还是很喜欢使用这些Stackable Traits来构建具有预定义导入,自定义绑定和类路径的Scala解释器实例。该堆叠特征使得能够沿着线创建我的工厂new InterpreterFactory with JsonLibs with LuceneLibs
,然后有有用的进口和范围变量下为用户脚本。