Scala的隐藏function

Scala的每个Scala开发人员应该注意哪些隐藏的function?

每个答案一个隐藏的function,请。

好吧,我不得不再添加一个。 Scala中的每个Regex对象都有一个提取器(请参阅上面的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" 

如果你不习惯使用模式匹配和提取器,第二行看起来很混乱。 每当你定义一个valvar ,关键字后面的内容不仅仅是一个标识符,而是一个模式。 这就是为什么这个工程:

 val (a, b, c) = (1, 3.14159, "Hello, world") 

右手expression式创build了一个可以匹配模式(a, b, c)Tuple3[Int, Double, String]

大多数情况下,您的模式使用单身对象的成员提取器。 例如,如果你写了一个模式

 Some(value) 

那么你隐式调用提取器Some.unapply

但是你也可以在模式中使用类实例,这就是发生在这里的事情。 val regex是Regex一个实例,当你在一个模式中使用它时,你隐式地调用了regex.unapplySequnapplySequnapplySeq超出了这个答案的范围),它将匹配组抽取成一个Seq[String] ,其中的元素是按variables年,月,日分配的。

结构types定义 – 即它所支持的方法描述的types。 例如:

 object Closer { def using(closeable: { def close(): Unit }, f: => Unit) { try { f } finally { closeable.close } } } 

请注意, closeable的参数的types除了具有close方法以外,没有定义

types构造器多态性 (又名更高types)

例如,如果没有这个特性,你可以expression将函数映射到列表上的想法,以返回另一个列表,或者在树上映射一个函数以返回另一个树。 但是,如果没有更高的种类,你不能expression这个想法。

更高的种类,你可以捕捉任何types的想法,用另一种types参数化。 有一个参数的types构造函数被认为是类(*->*) 。 例如, List 。 一个返回另一个types构造函数的types构造函数被认为是类(*->*->*) 。 例如, Function1 。 但是在Scala中,我们有更高的types,所以我们可以使用其他types构造函数进行参数化的types构造函数。 所以它们的types就像((*->*)->*)

例如:

 trait Functor[F[_]] { def fmap[A, B](f: A => B, fa: F[A]): F[B] } 

现在,如果您有Functor[List] ,则可以映射列表。 如果你有一个Functor[Tree] ,你可以在树上映射。 但是更重要的是,如果你有任何types的 Functor[A] (*->*) ,你可以通过A映射一个函数。

提取器 ,允许你用模式replace凌乱的if-elseif-else样式代码。 我知道这些并不完全隐藏,但我已经使用了Scala几个月,却没有真正理解它们的力量。 对于我可以取代的一个很长的例子:

 val code: String = ... val ps: ProductService = ... var p: Product = null if (code.endsWith("=")) { p = ps.findCash(code.substring(0, 3)) //eg USD=, GBP= etc } else if (code.endsWith(".FWD")) { //eg 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) } 

清单是在运行时获取types信息的一种方式,就好像Scala已经具体化types一样。

在scala 2.8中,你可以通过使用包scala.util.control.TailCalls(实际上是蹦床)来实现尾recursion方法。

一个例子:

 def u(n:Int):TailRec[Int] = { if (n==0) done(1) else tailcall(v(n/2)) } def v(n:Int):TailRec[Int] = { if (n==0) done(5) else tailcall(u(n-1)) } val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result) println(l) 

大小写类自动混入产品特征,为字段提供无types的索引访问权限,而无需任何reflection:

 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) 

此function还提供了一种简化的方法来更改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)) 

它并不完全隐藏,但肯定是一个广告的function: 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存在的implicit def augmentString的应用程序。

scalac -Xprint:<phase>将在某个编译器阶段后打印语法树。 要查看可用阶段,请使用scalac -Xshow阶段

这是了解幕后情况的好方法。

尝试

case class X(a:Int,b:String)

使用typer阶段,真正感受它是多么有用。

你可以定义你自己的控制结构。 它实际上只是函数和对象,还有一些语法糖,但它们看起来和行为都像真实的东西。

例如,下面的代码定义了dont {...} unless (cond)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) 

Scala 2.8中的@switch注释:

要应用于匹配expression式的注释。 如果存在,编译器将validation匹配是否已经编译到一个tableswitch或lookupswitch,如果它编译成一系列条件expression式,则会发出一个错误。

例:

 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 { 

不知道,如果这是真的隐藏,但我觉得很不错。

带有2个types参数的Typeconstructor可以用中缀表示法来书写

 object Main { class FooBar[A, B] def main(args: Array[String]): Unit = { var x: FooBar[Int, BigInt] = null var y: Int FooBar BigInt = null } } 

Scala 2.8引入了默认参数和命名参数,这就增加了Scala添加到案例类中的新的“复制”方法。 如果你定义这个:

 case class Foo(a: Int, b: Int, c: Int, ... z:Int) 

而你想创build一个新的Foo,就像现有的Foo,只有一个不同的“n”值,那么你可以说:

 foo.copy(n = 3) 

在Scala 2.8中,你可以添加@specialized到你的generics类/方法。 这将为原始types(扩展AnyVal)创build类的特殊版本,并节省不必要的装箱/拆箱成本: class Foo[@specialized T]...

你可以select一个AnyVals的子集: class Foo[@specialized(Int,Boolean) T]...

扩展语言。 我一直想在Java中这样做(不能)。 但在斯卡拉我可以有:

  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) 

你可以指定一个名称参数(EDITED:这是不同的,然后一个懒惰的参数!)到一个函数,它不会被评估,直到函数使用(编辑:实际上,它将被重新评估每次用过的)。 查看这个常见问题的细节

 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 */ 

您可以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 

我们实例化一个匿名的内部类,在with AbstractT2子句之前初始化块中的value字段。 这可以确保在执行AbstractT2的主体之前初始化value ,如运行脚本时所示。

您可以使用“with”关键字组合结构types

 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 ::= '_' 

expression式(语法类别Expr )可以在标识符合法的地方包含embedded的下划线符号_ 。 这样的expression式表示一个匿名函数,其中下划线的后续出现表示连续的参数。

从Scala语言变化 :

 _ + 1 x => x + 1 _ * _ (x1, x2) => x1 * x2 (_: Int) * 2 (x: Int) => x * 2 if (_) x else yz => 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)) 

隐式定义,特别是转换。

例如,假定一个函数将格式化inputstring以适合尺寸,用“…”replace它的中间部分:

 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声明而自动被传递。 此外,“s”被视为 sizeBoundedString中的一个String,因为存在从它到String的隐式转换。

这种types的隐含更好地定义为非常见的types,以避免意外的转换。 你也可以明确地传递一个转换,它仍然会在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 } 

这是用完全相同的方式。

Implicits可以有任何价值。 例如,它们可以用来隐藏图书馆信息。 以下面的例子为例:

 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的实例发送到守护进程X守护进程。 但是在一个X的实例上调用g会将日志发送给显式给定的DefaultDaemon。

虽然这个简单的例子可以用重载和私有状态重写,implicits不需要私有状态,并且可以与导入相关联。

也许不是太隐藏,但我认为这是有用的:

 @scala.reflect.BeanProperty var firstName:String = _ 

这将自动为匹配bean约定的字段生成一个getter和setter。

在developerworks进一步的描述

隐式参数在闭包中。

函数参数可以像方法一样标记为隐式。 在函数体的范围内,隐式参数是可见的,并且适用于隐式parsing:

 trait Foo { def bar } trait Base { def callBar(implicit foo: Foo) = foo.bar } object Test extends Base { val f: Foo => Unit = { implicit foo => callBar } def test = f(new Foo { def bar = println("Hello") }) } 

用Scala的Stream构build无限的数据结构: http : //www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient

结果types依赖于隐式parsing。 这可以给你一个多派遣的forms:

 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 

Scala相当于Java的双重大括号初始化器。

Scala允许你创build一个匿名的子类,该类的主体(构造函数)包含用于初始化该类的实例的语句。

这种模式在构build基于组件的用户界面(例如Swing,Vaadin)时非常有用,因为它允许创buildUI组件并更简洁地声明它们的属性。

有关更多信息,请参阅~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf

这是一个创buildVaadinbutton的例子:

 val button = new Button("Click me"){ setWidth("20px") setDescription("Click on this") setIcon(new ThemeResource("icons/ok.png")) } 

不包括import报表中的成员

假设你想使用一个包含printlnprinterr方法的Logger ,但是你只想用错误信息来保存标准输出的老版本的Predef.println 。 你可以这样做:

 val logger = new Logger(...) import logger.printerr 

但是如果logger还包含另外12种您想要导入和使用的方法,则列出它们将变得不方便。 你可以尝试:

 import logger.{println => donotuseprintlnt, _} 

但这仍然“污染”了import成员的名单。 input超强的通配符:

 import logger.{println => _, _} 

这将做正确的事情 ™。

require方法(在Predef定义),允许您定义在运行时检查的附加函数约束。 想象一下,你开发另一个Twitter客户端,你需要限制长达140个符号的微博长度。 此外,你不能发布空的鸣叫。

 def post(tweet: String) = { require(tweet.length < 140 && tweet.length > 0) println(tweet) } 

现在用不合适的长度参数调用post会导致exception:

 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 

具有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> aa("a lotta as") Called a with s=a lotta as Executed a in 0 ms res4: String = sa attol a 

虽然我的例子实际上不仅仅是一个可怜的AOP,但是我使用这些Stackable Traits使我更喜欢使用预定义的导入,自定义绑定和classpattes构buildScala解释器实例。 Stackable Traits可以new InterpreterFactory with JsonLibs with LuceneLibs创buildnew InterpreterFactory with JsonLibs with LuceneLibs然后为用户脚本提供有用的导入和范围new InterpreterFactory with JsonLibs with LuceneLibs