声明:本文翻译自这里

递归101

我们都知道什么是递归吧?一个自己调用自己的函数,或者函数A调用函数B,函数B又调用函数A,或者是A调用B,B调用C, C调用A。但是我们大多数情况下所说的递归函数,是指一个调用自身的函数。
在Java世界中,递归函数的曝光率很低,说起来有不少原因。
第一,递归不直观,难以理解。对于一段循环代码(for, while), 你可以很直白地看到这段逻辑的全景,即便你是一个初学者,而对于一段递归代码,就不是那么容易了,你只能看到递归逻辑的一次调用,而不得不想象当递归调用发生时,这些多次调用是如何组合在一起的。
第二,比起递归,循环在java中更加容易实现,比如for ,for-each,while, do-while, 数组, iterator, ResultSet,这些结构都是用来实现循环的。
第三,Java中的递归有自己的Achille’s heel: call stack。

总的来说,当调用一个函数时,一个新的call stack frame会被放到call stack的顶部,用以保存局部变量,当前函数的caller,等等。但是,call stack的大小是有限的,当递归的深度不是很深时,调用递归函数是没有什么问题的,但是如果递归调用的深度无法预计,那么很有可能会导致stack overflow. 而循环却不会有这种问题(因为循环不会产生新的call stack frame),因此,使用循环更加安全。

Scala中的递归

Scala,作为一新兴的functional language,更偏爱递归胜过循环,那么在scala中,是如何解决call stack大小限制的问题的呢?我们来看一个例子:

  def listLength1(list: List[_]): Int = {
    if (list == Nil) 0
    else 1 + listLength1(list.tail)
  }

  var list1 = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  var list2 = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  1 to 15 foreach( x => list2 = list2 ++ list2 )

  println( listLength1( list1 ) )
  println( listLength1( list2 ) )

函数listLenght1递归地计算list中item的数量,这个函数在计算item数量较少的list时工作地很好,然而在item数量很大时,就会得到stack overflow错误. 递归是函数式语言的谋生手段,但是在Scala中,递归仍然倒在了stack overflow的脚下。

先别急着放弃,Scala有一个很重要的优化递归的方案,只要你用了正确的递归类型。

首递归和尾递归

根据递归调用的方式,递归可以分为首递归和尾递归。在首递归函数中,函数调用自身后,再进行其他运算(可能会把自用自身后的结果做为这些运算的输入)。在尾递归函数中,所有的计算都在函数调用自身前完成,调用自身是尾递归函数中做的最后一件事情。

这两种递归的区别的重要性目前看起来还不是那么明显,然而,它确实很重要!想象一下一个尾递归函数的执行过程,首先完成所有的计算,在最后一步,马上进行对自身递归的调用,一般情况下,这个时候就该使用call stack frame记录方法调用状态了,然而,这里却不需要:我们不需要记录局部变量,因为所有的计算都已经完成。我们也不需要知道目前在哪个函数中,因为我们始终在同一个函数中。基于以上前提,scala不会创建一个新的call stack frame,而是重用当前的call stack frame ,无论调用次数有多少,call stack也不会增长。这就是scala中尾递归函数的特殊性。在其他语言中,语言设计者通过把尾递归转换成循环的方式进行了优化。

而在首递归函数中,递归调用是不一样的,这是为什么呢?想象一下一个首递归函数的执行过程:先执行一些计算,在递归调用自身,然后在执行另一些计算。在调用自身前,需要记住当前的局部变量,以便在从递归调用返回后继续进行后面的运算,这样,就必须创建一个新的call stack frame来记录当前状态。因此首递归函数还是会有stack overflow的风险。并且无法被优化。

在这里问你一个问题,上面的listLenght1是一个尾递归还是首递归?让我们来看着这个函数做了哪些事情。
A) 检查参数是否为Nil。
B) 如果为空,返回零,因为Nil的长度是零。
C) 如果不为空,则返回1加上递归调用的结果。
递归调用逻辑在这个函数的最后一行,应该是尾递归函数吧?错!在尾递归调用结束后,然后对递归调用结果加一,然后返回最终结果。这实际上首递归(或者可以叫做中递归)因为递归调用并不是所有运算的最后一步。

尾递归例子

当你用scala写一个递归函数时,你的目标是写成尾递归以便编译器对尾递归进行优化。现在让我们把上面的那个函数重写为尾递归函数。

def listLength2(list: List[_]): Int = {
  def listLength2Helper(list: List[_], len: Int): Int = {
    if (list == Nil) len
    else listLength2Helper(list.tail, len + 1)
  }
  listLength2Helper(list, 0)
}

println( listLength2( list1 ) )
println( listLength2( list2 ) )

我写成两个函数(listLength2 和一个内部的helper函数)以便和上面的例子中的函数接口保持一致。如果你能给listLength2Helper的参数给个默认值,我们就能只提供一个函数,但是我不知道怎么做。长话短说:listLength2只调用了做了实际工作的listLength2Helper,而且listLength2Helper也是个递归函数。

listLength2Helper是个尾递归函数吗?递归调用是所有运算的最后一步,允许scala进行优化?就像listLenght1一样,listLength2首先检查参数是否为Nil,如果不是,就进行递归调用,但是仍然会有一个加一的操作 —— len + 1。难道这个就不是尾递归吗?不,len + 1 运算会在递归调用前运算。只有所有的参数运算完了以后,才会进行递归调用,这个函数确实是个尾递归函数。

什么是vagrant?

看<a =href=”http://vagrantup.com/docs/getting-started/index.html”>这里</a>

为什么要用vagrant?

看<a =href=”http://vagrantup.com/docs/getting-started/why.html”>这里</a>

怎样开始使用vagrant?

  1. 安装VirtualBox
  2. 安装基础虚拟机
$ vagrant box add base http://files.vagrantup.com/lucid32.box

这个基础虚拟机是一个运行着ubuntu,没有安装额外软件的虚拟机,以后的所有更改都是在这个基础虚拟机上进行的。

由于国内网络环境不好,推荐先把这个基础虚拟机镜像下载到本地,然后通过如下脚本安装:

$ vagrant box add base ./lucid32.box

然后运行如下命令初始化,启动虚拟机:

$ vagrant init base
$ vagrant up

至此,一个vagrant的基础虚拟机环境就创建成功了,想知道如何访问这个虚拟机?通过如下命令就可以登陆上虚拟机了。

$ vagrant ssh

怎么配置虚拟机?

#创建一个目录用于存放配置虚拟机的文件
$ mkdir mybox 
#初始化这个新虚拟机
$ cd mybox 
$ vagrant box add mybox http://files.vagrantup.com/lucid32.box
$ vagrant init

经过以上步骤,在mybox目录下会多出一个文件Vagrantfile,这个就是配置虚拟机的文件, 在这个文件里,vagrant支持通过chefpuppet对虚拟机进行配置。

如何使用这个虚拟机?

# 启动虚拟机
$ vagrant up
# 停止虚拟机
$ vagrant halt
# 登陆到虚拟机
$ vagrant ssh
# 删除虚拟机
$ vagrant destroy
# 把当前虚拟机打包成一个本地镜像,便于再次分发
$ vagrant package

OOCamp Day1

  • Tasking 做tasking的标准是,这个task具备可测试性
  • 封装 对一个类的属性的读取,修改都在该类的上下文,那么这个类就没有封装泄露。 tell, don’t ask

OOCamp Day2

使用自定义对象而不是java原生对象,更加容易适应需求变化

OOCamp Day3

  • 什么是重构? 不改变功能的情况下改变代码的架构,重构的时候可以随时停止
  • 如何识别bad smell? 如果有一个子类,没有比父类更多的状态,那么这其实一个strategy模式
  • 如何证明代码被改进了? 把一个badsmell经过重构变成了一个pattern,或者bad smell消失了,那么代码就是被改进了。
  • 什么是Pattern? Pattern是一类问题及该类问题的解决方案。使用Pattern很容易,关键是正确地识别出问题, 而问题一般都表现为一个bad smell。例如下面几个problem以及解决这些bad smell所使用pattern:
    class explosion Decorator
    object creation is dependent on outside Factory

OOCamp Day4

重构

  • 重构技巧:copy & paste => extract method & inline method
  • 代码中,什么是其组成的最小单元? 功能
  • 要有足够的bad smell支撑你来做重构

OOCamp Day5

一个很难发现的bad smell: 上下文分散, Pattern:Strategy,把业务逻辑放到更加集中的类里

OOCamp Day6

架构:

  • 什么是架构?
    软件架构回答了以下两个问题:
    1. 系统中有哪些组件
    2. 组件之间如何交互
  • 架构只有有了应用场景,才是有意义的

  • 五种常用的架构:
    单体: Singleton,最简单的架构
    黑板:模块之间的地位平等, 广播消息
    分层:上层把下层当做抽象机器, 拒绝跨层. 大多数问题都可以通过引入一个分层来解决, 但这通常都会带来新的问题.
    数据流:过滤器
    微核:分成平台和核. 带来的问题是隐性知识增多,一个典型的例子就是spring.

DRY:
DRY的代价是耦合, 绝大部分是值得的, 有些时候,需要故意引入重复来消除耦合.
DRY隐含了知识管理成本
note: 没有任何好的软件实践是不需要代价的.

Mock:
当我们提起Mock时,实际上在不同的上下文中,有着不同的含义。
如果你想测试的代码依赖一个尚未实现的外部接口,你需要“mock”这个外部接口,让其按照约定的行为工作, 然后测试你的程序,这时候,“mock”是一种技术。如果你写了了一个实现了这个外部接口的‘mock’ class,在这个class中返回假的数据以支撑你的测试,这时候,“mock”是一种“mock framework”。

当然,在实际开发过程中,你一般不需要自行实现一个“mock framework”,有太多的mock framework供你选择, easymock,jmock, mockito等等。

几种常见的mock手段:
fake, stub, dummy, mock

Q:什么时候决定了当前在用mock?
A:call verify, 在此之前,都是在stub.

State based testing:
内容实现不稳定时,采用基于状态的测试, 用stub即可。
Interaction based testing:
在明确系统的行为边界时,需采用基于交互的测试,需要使用mock来验证代码的行为。

Git save snapshot in each commit rather than store the delta

Local operations:

Three states:

modified

modified but not adding to git local database
Working directory

staged

modified and ready to add to git local database
Staging area

commited

data safely stored in local database
Git repository

Git config

you can put git config file in the following places:

  • /etc/gitconfig
  • ~/.gitconfig
  • /.git/config

you can show your git configs by

  • git config –list

meta programing ruby读书笔记

Open Class

给予了你为class增加方法的能力,但是也如同打开了潘多拉魔盒,一旦通过Open Class增加了class方法导致重名问题,将很难定位。

Instance variable

和Java不同,Ruby中的instance_variable 与class之间没有直接联系,你可以从一个class创建不同实例,并且各自拥有不同的instance variable. (What’s the purpose?)

Method

Ruby中,method保存在class中

Object

一个object是一组instance variable以及这个object所属的class的引用.

Class

Ruby中,所有的class都是类Class的instance object. class是一个Class的实例、一组instance method、一个到super class的引用.

Differences between Class and Module

Module is meant to be included (or mixined). Module can be used as namespace. Class is meant to be initlized or inherinted.

Dynamic Methods

ruby中,可以在class定义时通过define_methods动态地定义方法, 如下代码动态地为class Computer添加了三个方法 mouse, cpu, keyboard:

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def self.define_component(name) 
    define_method(name) {
      info = @data_source.send "get_#{name}_info", @id
      price = @data_source.send "get_#{name}_price", @id 
      "#{name.to_s.capitalize}: #{info} ($#{price})" 
    }
  end

  define_component :mouse
  define_component :cpu
  define_component :keyboard
end

Method Missing

ruby中,当尝试对receiver调用一个不存在的方法时,该receiver的method_missing方法会被调用,利用这一特性,可以通过重载 method_missing实现动态定义属性、方法效果, 通过method missing机制定义出来的方法被称为Ghost Method。 例如:

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name, *args)
    info = @data_source.send("get_#{name}_info", args[0])
    price = @data_source.send("get_#{name}_price", args[0])
    "#{name.to_s.capitalize}: #{info} ($#{price})"
  end

  #重写respond_to会使利用method_missing实现的动态方法与实际定义的方法无异
  def respond_to?(method)
    @data_source.respond_to?("get_#{method}_info") || super
  end
end

这些通过method_missing机制创建出来的methods被称为ghost method.

Flat Scope

ruby中,classmodule,def被称为scope gate,当程序遇到这几个关键字时,程序的scope就会发生变化,如果需要在scope之间共享变量,则可以使用Class.new代替classmodule, Module#define_method代替def,如:

my_var = 12
MyClass = Class.new do
  # my_var is visiable here
  ...
  define_method :my_method do
    # my_var is visiable here
  end
end

Object#instance_eval()

改变self的值为指定实例,同时改变“current class”为当前实例的eigenclass, 在此实例上下文中执行一个block

Module#class_eval() or Module#module_eval()

改变self的值为指定实例,同时改变“current class”为当前方法的receiver,在此实例上下文中执行一个block

Deferred Evaluation

定义一个block,proc,lambda,然后在其他地方调用此block,proc,lambda,这就叫做Deferred Evaluation

Singleton Method

单独给一个object instance定义的method,称之为singleton method class methods可以认为是一个class对象的singleton method

Class Macro

class macro看起来像是关键字,但是实际上就是普通的方法,但是这些方法常在声明定义class中使用。

Eignclass

对象除了有一个其被创建时声明的那个class外,还会有它自己的一个不可见的,特殊的class,这个class被叫做eigenclass,也被称为singleton class。一个对象的singleton method就存在于这个对象的eignclass上,eignclass的superclass是这个对象的对外呈现的那个class(参考下面的示例代码)。

class Duck
  #code omited
end
#Duck就是a_duck对外呈现的class
a_duck = Duck.new

通过下面的语法,你可以进入到一个对象的eignclass内部

class << obj
  # you are in the eigen class scope.
end

或者可以通过下面的语法获取对eignclass的引用

metaclass = class << matz; self; end

Scope changing

class Person
  #self is the class object `Person` 
  class << self
    #self is the eignclass of class object `Person`
    def species
          "Homo Sapien"
    end
  end
end

Person.instance_eval do
  #method will be defined on eignclass	of class object `Person`
  #but self is the class object `Person`
  def species
    "Homo Sapien"
  end
  self.name #=> "Person"
end

给对象的eignclass增加singleton方法:

#给对象增加singleton方法
matz = Object.new
def matz.speak
  "Place your burden to machine's shoulders"
end

#给类增加singleton方法(class methods)
class Person
end

def Person.speak 
  "Place your burden to machine's shoulders"
end

Class Extension Mixin

module M
  def included(clazz)
clazz.extend ClassMethods
  end

  #定义在这个内部module里的方法会变成inclusor的class method
  module ClassMethods
def a_class_method
    end
  end
end