解宝明2017/04/06         
对于Scala编程,我们收集了这些常见代码编写中的陷阱。这些技巧来自于DanielSobral,一个曾参加过FreeBSD项目和Java软件开发工程的Scala狂热爱好者。
1、语法错误
认为“yield”像”return”一样。有人会这样写:
for(i <- 0 to 10) { if (i % 2 == 0) yield i else yield -i }
正确的表示应该是:
for(i <- 0 to 10) yield { if (i % 2 == 0) i else -i }2、误用和语法错误
val xml=<root/>这段代码真正的意思是:
val xml.$equal$less(root).$slash$greater这种情况的发生是由于操作符相当随意,而且scala采用这样一种事实:字母数字字符与非字母数字字符通过下划线可以结合成为一个有效的标识符。这也使得“x+y”这样的表达式不会被当成一个标识符。而应该注意“x_+”是一个有效的标识符。所以,赋值标识符的写法应该是:
val xml = <root/>3、用法错误
object MyScalaApp extends Application { // ... body ... }示例部分的问题在于,body部分在单元对象初始化时执行。首先,单元初始化的执行是异步的,因此你的整个程序不能与其它线程交互;其次,即时编译器(JIT)不会优化它,因此你的程序速度慢下来,这是没有必要的。
val r = """(\d+)""".r val s = "--> 5 <---" s match { case r(n) => println("This won't match") case _ => println("This will") }此处的问题在于,当模式模式匹配时,Scala的正则表达式表现为如同开始于”^”,结束于”$”。使之工作的正确写法是:
val r = """(\d+)""".r val s = "--> 5 <---" r findFirstIn s match { case Some(n) => println("Matches 5 to "+n) case _ => println("Won't match") }或者确保模式能匹配任意前缀和后缀:
val r = """.*(\d+).*""".r val s = "--> 5 <---" s match { case r(n) => println("This will match the first group of r, "+n+", to 5") case _ => println("Won't match") }5、用法错误
class User(val name: String, initialPassword: String) { private lazy var encryptedPassword = encrypt(initialPassword, salt) private lazy var salt = scala.util.Random.nextInt private def encrypt(plainText: String, salt: Int): String = { ... } private def decrypt(encryptedText: String, salt: Int): String = { ... } def password = decrypt(encryptedPassword, salt) def password_=(newPassword: String) = encrypt(newPassword, salt) }单例模式(Singleton)
class User(initialName: String, initialPassword: String) { private object fields { var name: String = initialName; var password: String = initialPassword; } def name = fields.name def name_=(newName: String) = fields.name = newName def password = fields.password def password_=(newPassword: String) = fields.password = newPassword }或者,对于一个类来说,可以为相等关系或hashCode自动定义可被重用的方法:
class User(name0: String, password0: String) { private case class Fields(var name: String, var password0: String) private object fields extends Fields(name0, password0) def name = fields.name def name_=(newName: String) = fields.name = newName def password = fields.password def password_=(newPassword: String) = fields.password = newPassword }子类化
case class Customer(name: String) class ValidatingCustomer(name0: String) extends Customer(name0) { require(name0.length < 5) def name_=(newName : String) = if (newName.length < 5) error("too short") else super.name_=(newName) } val cust = new ValidatingCustomer("xyz123")6、用法错误
def checkList[A](l: List[A]) = l match { case _ : List[Int] => println("List of Ints") case _ : List[String] => println("List of Strings") case _ => println("Something else") }在运行时,被传递的List没有类型参数。而List[Int]和List[String]都将会变成List[_].因此只有第一种情况会被调用。
def checkList[A](l: List[A])(implicit m: scala.reflect.Manifest[A]) = m.toString match { case "int" => println("List of Ints") case "java.lang.String" => println("List of Strings") case _ => println("Something else") }7、设计错误
implicit def string2Int(s: String): Int = s.toInt这是一个不好的做法,因为有人可能错误的使用了一个字符串来代替Int。对于上面的这种情况,更好的方法是使用一个类。
case class Age(n: Int) implicit def string2Age(s: String) = Age(s.toInt) implicit def int2Age(n: Int) = new Age(n) implicit def age2Int(a: Age) = a.n这将会使你很自由的将Age与String或者Int结合起来,而不是让String和Int结合。类似的,当使用隐式参数时,不要像这样做:
case class Person(name: String)(implicit age: Int)这不仅因为它容易在隐式参数间产生冲突,而且可能导致在毫无提示情况下传递一个隐式的age,而接收者需要的只是隐式的Int或者其它类型。同样,解决办法是使用一个特定的类。
◆使用这样的定义:
def equals(other: MyClass): Boolean而不是这样的:
override def equals(other: Any): Boolean忘记重载hashCode,以确保当a==b时a.hashCode==b.hashCode(反之不一定成立)。
ping `hostname`在这种情况下,fsc和scala都不会工作,而scalac则可以。这是因为fsc运行在背景模式下,通过TCP套接字监听连接来加速编译,而scala却用它来加快脚本的执行速度。
def matchingChars(string: String, characters: String) = { var m = "" for(i <- 0 until string.length) if ((characters contains string(i)) && !(m contains string(i))) m += string(i) m }而应该使用:
def matchingChars(string: String, characters: String) = { var m = "" for(c <- string) if ((characters contains c) && !(m contains c)) m += c m }如果有人需要返回一个索引,可以使用下面的形式来代替按索引迭代的方法。如果对性能有要求,它可以较好的应用在投影(projection)(Scala2.7)和视图(Scala2.8)中。
def indicesOf(s: String, c: Char) = for { (sc, index) <- s.zipWithIndex if c == sc } yield index
- 2017/04/09
回复
- 2017/04/09
回复
- 2017/04/09
回复