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"
如果你不习惯使用模式匹配和提取器,第二行看起来很混乱。 每当你定义一个val
或var
,关键字后面的内容不仅仅是一个标识符,而是一个模式。 这就是为什么这个工程:
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.unapplySeq
( unapplySeq
与unapplySeq
超出了这个答案的范围),它将匹配组抽取成一个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
报表中的成员
假设你想使用一个包含println
和printerr
方法的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
。