Aug 7 2010

Scala: 方法名约定

Scala中没有操作符但是支持运算符重载,方法的最后一个字符也对优先级有影响,它决定了方法调用的目标。

Scala的这个约定在熟悉之后你会发现它提高了流畅性,例如,如果想要将一个值添加到一list中,可以写作:value::list,尽管可以读为:“value被附加到list上”,这个方法的目标实际上list而参数是value,也就是说list.::(value)。

如果方法名以冒号结尾,那调用目标是操作符后面的实例,在下一个例子中,^() 是一个定义在类Cow中的方法,而 ^:() 是一个定义在Moon中的方法:

class Cow {
	def ^(moon: Moon) = println("Cow jumped over the moon")
}
class Moon {
	def ^:(cow: Cow) = println("This cow jumped over the moon too")
}

调用如下:

val cow = new Cow
val moon = new Moon
cow ^ moon
cow ^: moon

Continue reading


May 18 2010

用户访问行为,齐普夫分布及其 Java 实现

Zipf定律是文献计量学的重要定律之一,它和罗特卡定律、布拉德福定律一起被并称为文献计量学的三大定律。

对于CDN的内容管理,也近似符合Zipf 定律,就是大家常说对于内容的访问遵循80/20原则,也就是20%的内容,会占有80%的访问量。

zipf law

zipf law

这里 r 表示一个单词的出现频率的排名,P(r)表示排名为r的单词的出现频率.

(单词频率分布中 C约等于0.1, a约等于1)

后人将这个分布称为zipf distribution,中文名称为齐普夫分布或Zeta 分布。这是一个离散事件分布,广泛应用于语言学,保险学,网络模拟,以及对稀疏事件的建模中。

它表明在英语单词中,只有极少数的词被经常使用,而绝大多数词很少被使用。实际上,包括汉语在内的许多国家的语言都有这种特点。这个定律后来在很多领域得到了同样的验证,包括网站的访问者数量、城镇的大小和每个国家公司的数量。。这个定理也在很多分布里面得到了验证,比如人们的收入,互联网的网站数量和访问比例,互联网内容和访问比例(其他分布两个常数有所不同,a越大,分布越密集,对于VOD来说某些时候符合双zipf分布)。

比起枯燥的公式,图表更具有说服力,下面是用三百个严格符合zipf 分布的数据点描绘成的图,其中横轴表示排名,纵轴表示访问的频率,分别使用线性坐标和对数坐标表示:

zipf distribution

zipf distribution

可以看到对数坐标下是一条完美的直线。

Continue reading


May 5 2010

Netbeans 性能调优及加速

NetBeans  安装目录下的 netbeans.conf  文件中的 netbeans_default_options  能够对 NetBeans 的启动,运行以及编译的速度产生很大的影响,恰当地设值可以让 NetBeans 的性能有大幅提升,当然如果设置的值过高也能导致 NetBeans 无法启动。

我的内存是 2GB 的,netbeans_default-options 设置为以下的参数,运行速度很快,在这里做个备份供以后参考。

netbeans_default_options="-J-Dfile.encoding=UTF-8 -J-XX:PermSize=128m -J-XX:MaxPermSize=256m -J-Xmx1024m  -J-Duser.language=en -J-client -J-Xverify:none -J-Xss2m -J-Xms512m -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true -J-XX:+UseConcMarkSweepGC -J-XX:+CMSClassUnloadingEnabled -J-XX:+CMSPermGenSweepingEnabled"

Apr 30 2010

Swing : 将 System.out 重定向到 JTextArea 和 JTextPane

最近在用Swing 来编写图形用户界面,其中一个需求就是将System.out.println 的输出重定向到JTextArea组件中,刚开始使用的是《Java 标准输出重定向到GUI 》里的代码,但是在后台需要大量计算的时候,界面出现无响应的情况。后来又经过一番搜索,在 blogspot 找到了一篇文章: 《 Redirecting System.out and System.err to JTextPane or JTextArea》 ( 被墙了),可以实现实时输出的功能,不会出现缓慢的情况。

在 Swing 中,如果想将System.out和System.err 重定向到 JTextPane或者JTextArea,只需要覆盖OutputStream中的write() 方法来将文件附加到 text pane中。

JTextArea 版本:

  private void updateTextArea(final String text) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        textArea.append(text);
      }
    });
  }

  private void redirectSystemStreams() {
    OutputStream out = new OutputStream() {
      @Override
      public void write(int b) throws IOException {
        updateTextArea(String.valueOf((char) b));
      }

      @Override
      public void write(byte[] b, int off, int len) throws IOException {
        updateTextArea(new String(b, off, len));
      }

      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
    };

    System.setOut(new PrintStream(out, true));
    System.setErr(new PrintStream(out, true));
  }

Continue reading


Apr 26 2010

Scala : “运算符” 重载

运算符带了双引号,这是因为从技术上来说,Scala 并没有所谓的“运算符”,在这里是指重载符号,如+ ,+- 等等。
在 Scala 中,事实上这些是方法的名字。但是由于 Scala 并不需要点来分隔对象引用和方法名,因而看起来像是运算符。调用 ref1+ref2 其实与 ref1.+(ref2)是等同的,调用的其实是 ref1 的+()方法。

举例如下:

class Complex(val real: Int, val imaginary: Int) {
    def +(operand: Complex) : Complex = {
        new Complex(real + operand.real, imaginary + operand.imaginary)
    }
    override def toString() : String = {
        real + (if (imaginary < 0) "" else "+") + imaginary + "i"
    }
}
val c1 = new Complex(1, 2)
val c2 = new Complex(2, -3)
val sum = c1 + c2
println("(" + c1 + ") + (" + c2 + ") = " + sum)

运行结果:

(1+2i) + (2-3i) = 3-1i

再次强调,Scala 并不存在运算符,所以没有定义运算符优先级,但是同时又定义了方法的优先级。方法的第一个字符用来决定它们的优先级,如果两个同等优先级的字符在同个表达式中出现,那左边的优先。

Continue reading


Apr 26 2010

Scala : 交互式应用程序,函数组合

函数和交互式应用程序

回调(callback)在交互式应用程序中是很常见的。例如一个按钮被点击,并进行一个特定的动作。在Web 应用程序中创建回调是尤其困难的,除非你拥有像Scala这样强大的工具。下面创建一个方法,它生成一个随机的String,以作为一个全局唯一的标识符(GUID)。

scala> def randomName() = "I"+ scala.util.Random.nextLong.abs
randomName: ()java.lang.String

然后定义一个通用的JavaScript trait, 我们不需要刷新它,假设它包含可以在浏览器中运行的JavaScript 命令。

scala> trait JavaScript
defined trait JavaScript

下面用一个能生成 JavaScript的函数来创建一个Map 以关联GUID:

scala> var callbacks: Map[String, () => JavaScript] = Map()
callbacks: Map[String,() => JavaScript] = Map()
scala> def register(f: () => JavaScript) = {
| val name = randomName
| callbacks += name -> f
| <button onclick={"invokeServerCall("""+name+""")"}>ClickMe</button>
| }
register: (f: () => JavaScript)scala.xml.Elem

当用户点击按钮时,会用 GUID 创建一个Ajax HTTP 请求,Servlet 在map中查找GUID,如果GUID存在,这个函数就会被调用并将JavaScript则返回给浏览器,这个功能的代码类型于:

Scala> def handleAjax(guid: String): HttpResponse =
functionMap.get(guid).map(f => f()) match {
case Some(javaScript) => JavaScriptResponse(javaScript)
case _ => Http404Response()
}

上面的代码是 Lift 框架的简化版本,可以看到,这种写法可以让程序员更关注于业务逻辑。

Continue reading


Apr 25 2010

Scala : 作用域中的变量,容器

Functions Bound to Variables in Scope

函数被绑定到创建函数时所在的作用域内中的变量,这样就允许承载状态,例如首先声明一个名为 foo 的变量:

scala> val foo = "dog"
foo: java.lang.String = dog

下面创建一个引用了这个变量的函数:

scala> val whoTo = (s: String) => s+" "+foo
whoTo: (String) => java.lang.String = <function1>

调用这个函数:

scala> whoTo("I love my")
res0: java.lang.String = I love my dog

scala> whoTo("I walk my")
res1: java.lang.String = I walk my dog

函数可以绑定于vars 或 vals ,甚至可以修改vars,首先,创建一个var strs,它是一个 List[String]:

然后创建一个以String为参数并返回String的函数,并且它能修改strs:

scala> var strs: List[String] = Nil
strs: List[String] = List()

scala> val strF = (s: String) => {strs ::= s; s+" Registered"}
strF: (String) => java.lang.String = <function1>

调用这个函数:

scala>  strF("a")
res2: java.lang.String = a Registered

scala> strF("b")
res3: java.lang.String = b Registered

scala> strs
res4: List[String] = List(b, a)

把这个函数作为参数:

scala> List("p", "q", "r").map(strF)
res5: List[java.lang.String] = List(p Registered, q Registered, r Registered)

scala> strs
res6: List[String] = List(r, q, p, b, a)

可以看到每一步都正确地更新了 strs。

Continue reading


Apr 21 2010

Scala : Partial Functions 和 Type Parameters

方法和函数是不同的,在Scala中,除了方法以外,一切都是一个实例。方法被附加到实例之上,并且被实例所调用。Function是实现了 FunctionNN trait (NN是指参数的个数,在 Scala 2.8 的文档中可知,NN 的取值范围是 1~22)的实例。在运行时函数没有什么特别的地方,但在编译阶段,有一些方式可以让编写一个函数使用很少的代码。

Partial Functions

Partial function 是只有部份参数被 apply 的函数,在Scala中,可以从方法中来构建 partially applied function:

scala> def plus(a: Int, b: Int) = "Result is: "+(a + b)
plus: (a: Int,b: Int)java.lang.String

scala> val p = (b: Int) => plus(42, b)
p: (Int) => java.lang.String =

partial function 允许你基于已有的方法或函数来构建新的函数,参数可以用不同的括号来分组:

scala> def add(a: Int)(b: Int) = "Result is: "+(a + b)
add: (a: Int)(b: Int)java.lang.String

这样,就可以用下面的語法来调用:

scala> add(1)(2)
res1: java.lang.String = Result is: 3

通过这个語法,你可以传递一个独立于其它参数的代码快作为参数:

scala> add(1){
| val r = new java.util.Random
| r.nextInt(100)
| }
res2: java.lang.String = Result is: 15

同时,也允许很容易地将一个方法提升为一个partially applied function:

scala> def w42(f: Int => String) = f(42)
w42: (f: (Int) => String)String

scala> w42(add(1))
res5: String = Result is: 43

最后,也可以通过partially applying一个方法将它转换为一个函数:

scala> def f2 = add(1) _
f2: (Int) => java.lang.String

此时,这个函数也能被传递给另一个方法:

scala> w42(f2)
res6: String = Result is: 43

Type Parameters

方法可以拥有类型参数:类型参数定义了其它的类型或方法的返回值类型。但注意函数不能接收类型参数,一个的参数和返回值必须在函数定义时创建。

Continue reading


Apr 20 2010

Scala : Functions and Parameters

一个Function 是一个实例

在scala中,function是类的实例,所以在一个function上进行和类的实例一样的操作,下面创建了一个function并将它赋值给一个变量:

scala> val f:Int => String = x => "Dude: "+x
f: (Int) => String = <function1>

实际上它相当于:

scala> val f:(Int => String) = {  x=> "Dude :" +x }
f: (Int) => String = <function1>

可以在 f上调用方法:

scala> f.toString
res0: java.lang.String = <function1>

scala> f == f
res1: Boolean = true

scala> f == 4
res2: Boolean = false

用一个参数来apply function:

scala> f(44)
res3: String = Dude: 44

functions 作为参数传递

function能像任何其它参数那样被传递给方法。下面定义一个w42 方法,它接收一个输入为Int返回为String的function,在这里w42方法apply 42给函数,并返回結果。

scala> def w42(f: Int => String) = f(42)
w42: (f: (Int) => String)String

用函数变量来调用它:

scala> w42(f)
res11: String = Dude: 42

Continue reading


Apr 19 2010

Scala 集合框架:Tuple,Map[K,V],Option[T]

Tuple

Tuple 能让一个方法返回两个或三个值,例如下面的代码输入一个 List[Double],返回元素个数,元素的和及其平方和:

def sumSq(in: List[Double]): (Int, Double, Double) =
in.foldLeft((0, 0d, 0d))((t, v) => (t._1 + 1, t._2 + v, t._3 + v * v))

这里,编译器会将在括号中的集合元素看作是一个Tuple,上面的代码会翻译成:Tuple3[Int,Double,Double],这个函数接收两个参数:t和 v,t是一个Tuple3,而v是一个Double。
使用模式匹配可以让上面的代码更易理解:

def sumSq(in: List[Double]) : (Int, Double, Double) =
in.foldLeft((0, 0d, 0d)){
case ((cnt, sum, sq), v) => (cnt + 1, sum + v, sq + v * v)}

当然,也可以用其它方式来创建一个Tuple:

scala> Tuple2(1,2) == Pair(1,2)
scala> Pair(1,2) == (1,2)
scala> (1,2) == 1 -> 2

其中最后一种写法 1 -> 2 对于传递pairs 是很有用的一种方式。Pairs经常出现在代码中,包括用来创建 Maps 的 name/value 对。

对于Tuple元素的读取,应该使用下标 _n,例如 t._1, t._2 ,t._3 ,注意这里起始为1,并且对元素的引用不能越界。

Map[K,V]

Map 是一个 key/value 对的集合, key 是唯一的,但value 可以不唯一,Scala中的默认Map实现是不可变的,可以用于多线程环境,而且性能与Java中的HashMap相当。
创建一个 Map:

scala> var p = Map(1 -> "David", 9 -> "Elwood")
p: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,David), (9,Elwood))

添加一个元素:

scala> p += 8 -> "Archer"

scala>  p = p + (9 -> "Archer")
p: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> David, 8 -> Archer, 9 -> Archer)

如果想取出一个不存在的元素:

scala> p(88)
java.util.NoSuchElementException: key not found: 88

这里会抛出异常,虽然看起来并不方便,因为Java的Map能够通过返回null来处理这个情况,但这样做有两个缺点:首先,必须对每个Map的访问进行 null-test,其次,这代表着不能在一个Map中存储null值。对此Scala有一个更好的机制,即Map的get()方法将返回一个包含了結果的Option(Some 或none):

scala> p.get(88)
res10: Option[java.lang.String] = None

如果key没有找到,可以返回一个默认值:

scala> p.getOrElse(99, "Nobody")
res55: java.lang.String = Nobody
scala> p.getOrElse(1, "Nobody")
res56: java.lang.String = David

也可以使用flatMap来返回key在1-5之间的值:

scala> 1 to 5 flatMap(p.get)
res13: scala.collection.immutable.IndexedSeq[java.lang.String] = Vector(David)

注意,表面上来看这里p.get 只是一个方法,但是Scala正期待着一个拥有特定类型的参数,而你传递了一个接收一个参数的方法,这时Scala会将这个方法和它未写的参数提升为一个函数。

Continue reading