好久没有写博客了,上一次更新竟然是一月份。

说工作忙都是借口,咋有空看美剧呢。

这半年荒废掉博客说到底就是懒,惯性的懒惰。写博客这事儿,一丢掉就很久捡不起来。

闲话到此为止,下面进入正题。

Default parameter value,默认参数值。
这个很容易理解,给参数一个默认值,如果调用者不显式指明参数值,则使用默认值。如果显式指明了,那就用显式指明的值。

举个例子:

阅读全文 »

上次博客谈到了implicit function,但是漏掉了一些东西,今天补上。

由于上次已经讲过implicit function的实现细节,这次就不再重复了。今天只补充上次漏掉了的implicit function的一种很好的实践。

先看一段specs2的测试代码:

1
2
3
4
5
6
7
8
9
10
11
import org.specs2.mutable._

class HelloWorldSpec extends Specification {

"The 'Hello world' string" should {

"contain 11 characters" in {
"Hello world" must have size 11
}
}
}

我们试着理解这个测试代码在做什么的时候,无须多少思考,因为它和人类语言一样的亲近和自然。但是如果我们想知道specs2如何做到这一点时,就有点费解了。

我们知道xObject yMethod zParameter的写法是一个语法糖,它和xObject.yMethod(zParameter)是一样的。也就是说should和in都是方法名。于是,问题来了,should和in前面是个String啊,String上哪有这两个方法的定义?

阅读全文 »

Structural types,中文怎么翻译不确定。我们可以用它来实现类似于鸭子类型的效果。为什么说是“类似”鸭子类型呢?稍后会说到它和鸭子类型的区别。

举一个例子,看看它都可以做什么:

1
def makeNoise(quacker: {def quack(): String}) = quacker.quack

声明一个方法,叫做makeNoise,接受什么类型的参数呢?不做严格限制,我们只声明说参数必须有一个叫做quack的方法,该quack方法返回值类型为String。然后在makeNoise方法内调用quack方法。请注意我们并没有声明一个含有quack方法签名的接口或者类,我们仅仅是在声明参数的同时声明我们期待参数含有什么样的成员。

然后我们声明一个Duck类:

1
2
3
class Duck {
def quack() = "real quack"
}
阅读全文 »

Implicit function,中文或许应该叫做隐式函数吧。主要用来作隐式类型转换。例子:

1
2
3
4
5
6
7
8
9
10
11
class Duck {
def makeDuckNoise() = "gua gua"
}

class Chicken {
def makeChickenNoise() = "ge ge"
}

class Ducken(chicken: Chicken) extends Duck {
override def makeDuckNoise() = chicken.makeChickenNoise()
}

三个类,鸭子,鸡,还有伪装成鸭子的鸡。如果有这么一个函数:

1
def giveMeADuck(duck: Duck) = duck.makeDuckNoise()

该函数要求我们给它提供一只鸭子,我们可以这么调用它:

1
giveMeADuck(new Duck)
阅读全文 »

Function composition,顾名思义,就是函数的组合。直接举例:

1
2
3
def sayHi(name: String) = "Hi, " + name

def sayBye(str: String) = str + ", bye"

两个方法,一个说你好,一个说再见。然后我们创建很多个人名:

1
val names = List("world", "tom", "xiao ming")

我们希望对List中的每个人都说你好然后说再见:

1
names.map(sayHi).map(sayBye)
阅读全文 »

Pattern matching是Scala中很好用的一个语言特性。先举一个最简单的例子:

1
2
3
4
5
6
7
val number = 1

number match {
case 1 => doSomething()
case 2 => doSomethingElse()
case _ => doDefault()
}

这个代码和我们熟悉的switch case看起来很像,其实,这段代码反编译之后和Java的switch case确实就是一样的:

1
2
3
4
5
6
7
8
9
10
11
int number = 1;

int i = number; switch (i)
{
default:
doDefault(); break;
case 2:
doSomethingElse(); break;
case 1:
doSomething();
}

但是和Java的switch case不一样的是,Scala的pattern matching作为一个expression是可以evaluate一个值出来的,我们把上面的代码改一下,让doSomething,doSomethingElse和doDefault都返回点东西:

1
2
3
4
5
6
7
val number = 1

val result = number match {
case 1 => doSomething()
case 2 => doSomethingElse()
case _ => doDefault()
}
阅读全文 »

apply method是一个很简单的语言特性。如果一个class或者是object有一个主要的方法,那么与其每次显式的调用这个主要的方法,还不如隐式调用。举个例子:

1
2
3
4
5
6
class Kettle {
def boil(water: Water) = {
water.isWarm = true
water
}
}

一个水壶的主要作用就是烧开水,于是我们每次都要调用boil方法来烧开水:

1
2
val kettle: Kettle = new Kettle()
kettle.boil(new Water())

如果要把它改写成apply method的方式,只需要给boil改个名字就好了:

1
2
3
4
5
6
class Kettle {
def apply(water: Water) = {
water.isWarm = true
water
}
}
阅读全文 »

这篇博客介绍一下Scala中的partial application,局部应用,或者叫做柯里化。

所谓柯里化就是指把一个接受多个参数的函数的一部分参数写死,剩下的一部分由调用者提供。

用Java代码来表述,大概可以写成这样:

1
2
3
4
5
6
7
8
9
10
11
public String greet(String greeting, String name) {
return greeting + " " + name;
}

public String sayHello(String name) {
return greet("Hello", name);
}

public String greetXiaoMing(String greeting) {
return greet(greeting, "Xiao Ming");
}

greet用来给某个不确定的人打个不确定的招呼。

sayHello用来给某个不确定的人说一句固定的Hello。

阅读全文 »

Scala中的lazy关键字是实现延迟加载的好帮手。

在Java中想要做到延迟加载,常规的做法是大抵是这样的:

1
2
3
4
5
6
7
8
private String str = null;

public String getStr() {
if (str == null) {
str = getStrFromWebService();
}
return str;
}

以这种方式来保证web service不会被无谓的重复请求。

C#中则可以使用Lazy of T来实现类似的事:

1
2
3
4
5
6
7
8
9
private Lazy<String> str = new Lazy<string> (() => GetStrFromWebService ());

public String Str
{
get
{
return str.Value;
}
}
阅读全文 »

Scala中有一个type关键字,用来给类型或者是操作起别名,用起来很是方便。

比如这样:

1
type People = List[Person]

这样就是给List[Person](方括号是Scala的类型参数的写法)声明了一个别名,叫做People。

接下来就可以这样使用它:

1
2
3
def teenagers(people: People): People = {
people.filter(person => person.age < 20)
}
阅读全文 »

我在Coursera上跟了一门叫做Functional Programming Principles in Scala的课程,是由Scala的作者Martin Odersky讲授的。其中第三周的作业中使用到了Scala的trait这个语言特性。

我以前熟知的语言都没有类似的特性(Ruby的mixin和Scala的trait很像,但是Ruby我不熟),所以这周的博客就分析一下这个语言特性是如何实现的。

trait

在讲trait的实现机制之前,先看一个使用trait的例子。
假设我们有以下几个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
abstract class Plant {
def photosynthesis = println("Oh, the sunlight!")
}

class Rose extends Plant {
def smell = println("Good!")

def makePeopleHappy = println("People like me")
}

class Ruderal extends Plant {
def grow = println("I take up all the space!")
}

abstract class Animal {
def move = println("I can move!")
}

class Dog extends Animal {
def bark = println("Woof!")

def makePeopleHappy = println("People like me")
}

class Snake extends Animal {
def bite = println("I am poisonous!")
}

植物家族有玫瑰和杂草。

阅读全文 »

一个谜团

如果你用过类似guava这种“伪函数式编程”风格的library的话,那下面这种风格的代码对你来说应该不陌生:

1
2
3
4
5
6
7
8
9
public void tryUsingGuava() {
final int expectedLength = 4;
Iterables.filter(Lists.newArrayList("123", "1234"), new Predicate<String>() {
@Override
public boolean apply(String str) {
return str.length() == expectedLength;
}
});
}

这段代码对一个字符串的list进行过滤,从中找出长度为4的字符串。看起来很是平常,没什么特别的。

但是,声明expectedLength时用的那个final看起来有点扎眼,把它去掉试试:

error: local variable expectedLength is accessed from within inner class; needs to be declared final

阅读全文 »

上篇博文的末尾留了三个问题,现在自问自答一下。

在Scala中被声明为val的v4为什么在反编译的Java中不是final的呢?

在方法中声明局部变量时,如果用Scala的val关键字(或者是Java中的final)来修饰变量,则代表着此变量在赋过初始值之后不可以再被重新赋值。这个val或者final只是给编译器用的,编译器如果发现你给此变量重新赋值会抛出错误。

而bytecode不具备表达一个局部变量是immutable的能力,也就是说对于JVM来说,不存在不可变的局部变量这个概念。所以v4在反编译之后,就和普通的局部变量无异了。

在Scala中被声明为val的v2为什么在反编译的C#中不是readonly的呢?

这是个挺tricky的问题,我试着解释一下。Scala .NET是基于IKVM实现的,IKVM可以把Java bytecode翻译为CIL。
所以Scala编译为CIL的过程实际是这样的:

阅读全文 »

Scala可以编译为Java bytecode和CIL,从而在JVM和CLI之上运行。Scala有很多在Java和C#的世界中显得陌生的语言特性,本文将分析这些语言特性是如何实现的。

object

Scala中可以像这样创建object:

1
2
3
4
5
object HowIsObjectImplementedInScala {
def printSomething() {
println("printSomething")
}
}

然后在代码的其他地方调用printSomething,一个object究竟是什么东西呢?
我们将这段Scala编译为Java bytecode,然后反编译为Java,会发现编译器为HowIsObjectImplementedInScala这个object生成了两个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public final class HowIsObjectImplementedInScala
{
public static void printSomething()
{
HowIsObjectImplementedInScala..MODULE$.printSomething();
}
}

public final class HowIsObjectImplementedInScala$
{
public static final MODULE$;

static
{
new ();
}

public void printSomething()
{
Predef..MODULE$.println("printSomething");
}

private HowIsObjectImplementedInScala$()
{
MODULE$ = this;
}
}
阅读全文 »

本文讲什么?

本文用Scheme(Racket)代码为例,一步一步的推出Y Combinator的实现。

本文不讲什么?

Y Combinator是什么,干什么用的,它为什么能够work,它的数学含义以及实际应用场景,这些话题由于篇幅所限(咳咳,楼主的无知)不在本文论述范围之内。

如果有兴趣,请参考维基: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator

鸣谢

阅读全文 »

为什么要做这款插件?

在豆瓣上查看一本书的时候,页面的右侧会显示哪些网站可以购买该书以及各自的价格。
比如这本《乡关何处》,页面右侧显示了亚马逊,京东,当当等网站的购买链接。

乡关何处

但是豆瓣只会提供纸质书的购买链接,不提供电子书的。除非该书豆瓣自己有售。
所以我写了个Chrome的插件来解决这个问题。

这款插件怎么用?

这款插件会在每个图书页面上添加正版电子书的购买链接及其价格。您只需点击链接去购买就好了。

阅读全文 »

测试发布文章

测试修改

再次修改

好久没用了,还能更新吗?test

is it fixed 10?

本文翻译自 Jon Skeet 的系列博文”Edulinq”。

本篇原文地址:

http://msmvps.com/blogs/jon_skeet/archive/2010/12/29/reimplementing-linq-to-objects-part-12-defaultifempty.aspx

上次实现 First / Last的时候写了大量的代码,相比起来,今天要讲的 DefaultIfEmpty 就轻松多了。

DefaultIfEmpty是什么?

这个操作符虽然简单,但是还是有 两个重载

阅读全文 »

本文翻译自 Jon Skeet 的系列博文”Edulinq”。

本篇原文地址:

http://msmvps.com/blogs/jon_skeet/archive/2010/12/29/reimplementing-linq-to-objects-part-11-first-single-last-and-the-ordefault-versions.aspx

今天我实现了六个操作符,每个操作符都有两个重载。我一开始以为这些操作符的实现会很相似,但是最后发现每个都稍微有些不同…

今天实现了哪些操作符?

以下三个集合的排列 {First, Last, Single}, { 带有 / 不带有 OrDefault }, { 带有 / 不带有谓词
} ,其结果是十二个不同的方法签名:

阅读全文 »

本文翻译自 Jon Skeet 的系列博文”Edulinq”。

本篇原文地址:

http://msmvps.com/blogs/jon_skeet/archive/2010/12/27/reimplementing-linq-to-objects-part-9-selectmany.aspx

我们接下来要实现的这个操作符是LINQ 中最重要的操作符。大多数(或者是全部?)其他的返回一个序列的操作符都可以通过调用 SelectMany
来实现,这是后话按下不表。现在我们首先来实现它吧。

SelectMany 是什么?

SelectMany 有四个重载,看起来一个比一个吓人:

阅读全文 »

本文翻译自 Jon Skeet 的系列博文”Edulinq”。

本篇原文地址:

http://msmvps.com/blogs/jon_skeet/archive/2010/12/27/reimplementing-linq-to-objects-part-8-concat.aspx

上文讲的 Count 和 LongCount 返回的是数值类型,本文我们讲的 Concat 返回的是一个序列。

Concat 是什么? Concat

只有一种签名形式,这让它使用起来很简单:

阅读全文 »

本文翻译自 Jon Skeet 的系列博文”Edulinq”。

本篇原文地址:

http://msmvps.com/blogs/jon_skeet/archive/2010/12/26/reimplementing-linq-to-objects-part-7-count-and-longcount.aspx

今天的文章要介绍两个 LINQ 操作符,因为它们实在是太类似了,所以放到一起来讲。 Count 和 LongCount
的实现非常相像,不同的只是方法名,返回值类型和几个变量。

Count 和 LongCount 是什么呢? Count LongCount
各自有两个重载:一个重载接受谓词,另一个不接受。下面是这四个方法的签名:

1
2
3
4
5
6
7
public static int Count < TSource > (this IEnumerable < TSource > source)

public static int Count < TSource > (this IEnumerable < TSource > source, Func < TSource, bool > predicate)

public static long LongCount < TSource > (this IEnumerable < TSource > source)

public static long LongCount < TSource > (this IEnumerable < TSource > source, Func < TSource, bool > predicate)
阅读全文 »