在上篇讨论里我们提到了become/unbecome。由于它们本质上是堆栈操作,所以只能在较少的状态切换下才能保证堆栈操作的协调及维持程序的清晰逻辑。对于比较复杂的程序流程,Akka提供了FSM:一种通过状态变化进行功能切换的Actor。FSM模式的状态转变特别适合对应现实情况中的程序流程,我们可以用每一种状态来代表一个程序流程。FSM是个trait,定义如下:

trait FSM[S, D] extends Actor with Listeners with ActorLogging {...}

我们看到:FSM就是一个特殊的Actor。带着两个类型参数:S代表状态类型,D代表状态数据类型。实际上S和D结合起来就是FSM的内部状态,即:SomeState+DataX 和 SomeState+DataY分别代表不同的Actor内部状态,这点从State定义可以得到信息:

/**
* This captures all of the managed state of the [[akka.actor.FSM]]: the state
* name, the state data, possibly custom timeout, stop reason and replies
* accumulated while processing the last message.
*/
case class State[S, D](stateName: S, stateData: D, timeout: Option[FiniteDuration] = None, stopReason: Option[Reason] = None, replies: List[Any] = Nil) {...}

我们可以用下面的表达式来代表FSM功能:

State(SA) x Event(E) -> Actions (A), State(SB)

意思是:假如在状态SA发生了事件E,那么FSM应该实施操作A并把状态转换到SB。这里面操作Action代表某项功能,事件Event是个新的类型,定义如下:

/**
* All messages sent to the [[akka.actor.FSM]] will be wrapped inside an
* `Event`, which allows pattern matching to extract both state and data.
*/
final case class Event[D](event: Any, stateData: D) extends NoSerializationVerificationNeeded

Event[D]是个包嵌消息和数据的类型。理论上FSM是通过接收Event来确定运行功能和下一个状态转换,就像普通的Actor接收Message原理一样。我们用上一篇里的FillSeasons作为这次FSM示范的例子。首先定义State,Data:

trait Seasons   //States
case object Spring extends Seasons
case object Summer extends Seasons
case object Fall extends Seasons
case object Winter extends Seasons class SeasonInfo(talks: Int, month: Int) //Data
case object BeginSeason extends SeasonInfo(,)

四个状态分别是:春夏秋冬。SeasonInfo代表数据类型,含被问候次数talks,季中月份month两个参数,每季含1,2,3三个月份。目前我们只支持两种功能消息:

object FillSeasons {
sealed trait Messages //功能消息
case object HowYouFeel extends Messages
case object NextMonth extends Messages
}

这种普通Actor的消息类型对应到FSM的Event类型中的event:Any。也就是说FSM在收到功能消息后需要构建一个Event类型实例并把消息包嵌在里面。因为FSM继承了Actor,所以它必须实现receive函数。下面是FSM.receive的源代码:

 /*
* *******************************************
* Main actor receive() method
* *******************************************
*/
override def receive: Receive = {
case TimeoutMarker(gen) ⇒
if (generation == gen) {
processMsg(StateTimeout, "state timeout")
}
case t @ Timer(name, msg, repeat, gen) ⇒
if ((timers contains name) && (timers(name).generation == gen)) {
if (timeoutFuture.isDefined) {
timeoutFuture.get.cancel()
timeoutFuture = None
}
generation +=
if (!repeat) {
timers -= name
}
processMsg(msg, t)
}
case SubscribeTransitionCallBack(actorRef) ⇒
// TODO Use context.watch(actor) and receive Terminated(actor) to clean up list
listeners.add(actorRef)
// send current state back as reference point
actorRef ! CurrentState(self, currentState.stateName)
case Listen(actorRef) ⇒
// TODO Use context.watch(actor) and receive Terminated(actor) to clean up list
listeners.add(actorRef)
// send current state back as reference point
actorRef ! CurrentState(self, currentState.stateName)
case UnsubscribeTransitionCallBack(actorRef) ⇒
listeners.remove(actorRef)
case Deafen(actorRef) ⇒
listeners.remove(actorRef)
case value ⇒ {
if (timeoutFuture.isDefined) {
timeoutFuture.get.cancel()
timeoutFuture = None
}
generation +=
processMsg(value, sender())
}

除timer,subscription等特殊功能外,case value => ... 就是处理自定义消息的地方了。我们看到FSM是用processMsg(value, sender())来处理消息的。processMsg又调用了processEvent:

  private def processMsg(value: Any, source: AnyRef): Unit = {
val event = Event(value, currentState.stateData)
processEvent(event, source)
} private[akka] def processEvent(event: Event, source: AnyRef): Unit = {
val stateFunc = stateFunctions(currentState.stateName)
val nextState = if (stateFunc isDefinedAt event) {
stateFunc(event)
} else {
// handleEventDefault ensures that this is always defined
handleEvent(event)
}
applyState(nextState)
}

在processEvent里的stateFunction是个Map,以stateName为主键存放StateFunction:

 /*
* State definitions
*/
private val stateFunctions = mutable.Map[S, StateFunction]()

而StateFuction是:

 type StateFunction = scala.PartialFunction[Event, State]

FSM的receive函数在收到消息后把消息包嵌入新构建的Event然后在processEvent里通过stateName取出相应的StateFunction后传入Event产生新的状态State。用户提供的StateFunction是通过FSM的when函数压进stateFunction Map里的:

  /**
* Insert a new StateFunction at the end of the processing chain for the
* given state. If the stateTimeout parameter is set, entering this state
* without a differing explicit timeout setting will trigger a StateTimeout
* event; the same is true when using #stay.
*
* @param stateName designator for the state
* @param stateTimeout default state timeout for this state
* @param stateFunction partial function describing response to input
*/
final def when(stateName: S, stateTimeout: FiniteDuration = null)(stateFunction: StateFunction): Unit =
register(stateName, stateFunction, Option(stateTimeout)) private def register(name: S, function: StateFunction, timeout: Timeout): Unit = {
if (stateFunctions contains name) {
stateFunctions(name) = stateFunctions(name) orElse function
stateTimeouts(name) = timeout orElse stateTimeouts(name)
} else {
stateFunctions(name) = function
stateTimeouts(name) = timeout
}
}

我们看到when调用了register在stateFunction Map中按stateName放置StateFunction。FSM的这个stateFunction Map解决了become/unbecome产生的堆栈问题。FSM有个比较规范的结构,拿上面例子的FeelingSeasons结构做个示范:

class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) => ...
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
}
onTransition {
case Spring -> Summer => log.info("Season changed from Spring to Summer month 1")
case Summer -> Fall => log.info("Season changed from Summer to Fall month 1")
case Fall -> Winter => log.info("Season changed from Fall to Winter month 1")
case Winter -> Spring => log.info("Season changed from Winter to Spring month 1")
} initialize() //设定起始状态
}

基本上是按照各状态定义事件处理函数StateFunction的。也可以包括状态转换处理函数TransitionHandler:

  type TransitionHandler = PartialFunction[(S, S), Unit]

最后,initialize()确定起始状态是否安排正确:

  /**
* Verify existence of initial state and setup timers. This should be the
* last call within the constructor, or [[akka.actor.Actor#preStart]] and
* [[akka.actor.Actor#postRestart]]
*
* An initial `currentState -> currentState` notification will be triggered by calling this method.
*
* @see [[#startWith]]
*/
final def initialize(): Unit =
if (currentState != null) makeTransition(currentState)
else throw new IllegalStateException("You must call `startWith` before calling `initialize`")

完整的FSM FeelingSeasons定义如下:

class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) =>
val numtalks = seasonInfo.talks +
log.info(s"It's ${stateName.toString}, feel so gooood! You've asked me ${numtalks}times.")
stay using seasonInfo.copy(talks = numtalks)
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
val numtalks = stateData.talks +
log.info(s"It's ${stateName.toString}, it's so hot! You've asked me ${numtalks}times")
stay().using(stateData.copy(talks = numtalks))
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's no so bad. You've asked me ${numtalks}times.")
stay using SeasonInfo(numtalks,mnth)
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's freezing cold! You've asked me ${numtalks}times.")
stay using si.copy(talks = numtalks)
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
val mth = seasonInfo.month
if (mth <= ) {
log.info(s"It's month ${mth+1} of ${stateName.toString}")
stay using seasonInfo.copy(month = mth + )
}
else {
goto(nextSeason(stateName)) using SeasonInfo(,)
}
}
onTransition {
case Spring -> Summer => log.info(s"Season changed from Spring to Summer month ${nextStateData.month}")
case Summer -> Fall => log.info(s"Season changed from Summer to Fall month ${nextStateData.month}")
case Fall -> Winter => log.info(s"Season changed from Fall to Winter month ${nextStateData.month}")
case Winter -> Spring => log.info(s"Season changed from Winter to Spring month ${nextStateData.month}")
} initialize() //设定起始状态
log.info(s"It's month 1 of ${stateName.toString}") //季节转换顺序
def nextSeason(season: Seasons): Seasons =
season match {
case Spring => Summer
case Summer => Fall
case Fall => Winter
case Winter => Spring
}
}

首先注意StateFunction中SeasonInfo的各种意思同等的表达方式及nextStateData。FSM状态数据用不可变对象(immutable object)最安全,所以在更新时必须用case class 的copy或直接构建新的SeasonInfo实例。

我们再来看看processEvent的作业流程:

 private[akka] def processEvent(event: Event, source: AnyRef): Unit = {
val stateFunc = stateFunctions(currentState.stateName)
val nextState = if (stateFunc isDefinedAt event) {
stateFunc(event)
} else {
// handleEventDefault ensures that this is always defined
handleEvent(event)
}
applyState(nextState)
}

先运算用户定义的StateFunction处理事件Event获取新的状态State。然后调用applyState运算makeTransition处理状态转换(currentState = nextState):

  private[akka] def applyState(nextState: State): Unit = {
nextState.stopReason match {
case None ⇒ makeTransition(nextState)
case _ ⇒
nextState.replies.reverse foreach { r ⇒ sender() ! r }
terminate(nextState)
context.stop(self)
}
} private[akka] def makeTransition(nextState: State): Unit = {
if (!stateFunctions.contains(nextState.stateName)) {
terminate(stay withStopReason Failure("Next state %s does not exist".format(nextState.stateName)))
} else {
nextState.replies.reverse foreach { r ⇒ sender() ! r }
if (currentState.stateName != nextState.stateName || nextState.notifies) {
this.nextState = nextState
handleTransition(currentState.stateName, nextState.stateName)
gossip(Transition(self, currentState.stateName, nextState.stateName))
this.nextState = null
}
currentState = nextState def scheduleTimeout(d: FiniteDuration): Some[Cancellable] = {
import context.dispatcher
Some(context.system.scheduler.scheduleOnce(d, self, TimeoutMarker(generation)))
} currentState.timeout match {
case SomeMaxFiniteDuration ⇒ // effectively disable stateTimeout
case Some(d: FiniteDuration) if d.length >= ⇒ timeoutFuture = scheduleTimeout(d)
case _ ⇒
val timeout = stateTimeouts(currentState.stateName)
if (timeout.isDefined) timeoutFuture = scheduleTimeout(timeout.get)
}
}
}

我们用FSM DSL的stay, goto,using来取得新的FSM状态和数据:

 /**
* Produce transition to other state.
* Return this from a state function in order to effect the transition.
*
* This method always triggers transition events, even for `A -> A` transitions.
* If you want to stay in the same state without triggering an state transition event use [[#stay]] instead.
*
* @param nextStateName state designator for the next state
* @return state transition descriptor
*/
final def goto(nextStateName: S): State = FSM.State(nextStateName, currentState.stateData) /**
* Produce "empty" transition descriptor.
* Return this from a state function when no state change is to be effected.
*
* No transition event will be triggered by [[#stay]].
* If you want to trigger an event like `S -> S` for `onTransition` to handle use `goto` instead.
*
* @return descriptor for staying in current state
*/
final def stay(): State = goto(currentState.stateName).withNotification(false) // cannot directly use currentState because of the timeout field

stay,goto返回结果都是State[S,D]类型。using是State类型的一个方法:

   /**
* Modify state transition descriptor with new state data. The data will be
* set when transitioning to the new state.
*/
def using(@deprecatedName('nextStateDate) nextStateData: D): State[S, D] = {
copy(stateData = nextStateData)
}

我们看到using的主要作用是把当前状态数据替换成新状态的数据。

Akka的FSM是一个功能强大的Actor类型,所以配备了一套完整的DSL来方便FSM编程。FSM的DSL语句包括:

  final def startWith(stateName: S, stateData: D, timeout: Timeout = None): Unit =
currentState = FSM.State(stateName, stateData, timeout) final def goto(nextStateName: S): State = FSM.State(nextStateName, currentState.stateData) final def stay(): State = goto(currentState.stateName).withNotification(false) final def stop(): State = stop(Normal) final def transform(func: StateFunction): TransformHelper = new TransformHelper(func) ...

State[S,D]也提供了一些比较实用的方法函数:

case class State[S, D](stateName: S, stateData: D, timeout: Option[FiniteDuration] = None, stopReason: Option[Reason] = None, replies: List[Any] = Nil) {
... // defined here to be able to override it in SilentState
def copy(stateName: S = stateName, stateData: D = stateData, timeout: Option[FiniteDuration] = timeout, stopReason: Option[Reason] = stopReason, replies: List[Any] = replies): State[S, D] = {
new State(stateName, stateData, timeout, stopReason, replies)
} /**
* Modify state transition descriptor to include a state timeout for the
* next state. This timeout overrides any default timeout set for the next
* state.
*
* Use Duration.Inf to deactivate an existing timeout.
*/
def forMax(timeout: Duration): State[S, D] = timeout match {
case f: FiniteDuration ⇒ copy(timeout = Some(f))
case Duration.Inf ⇒ copy(timeout = SomeMaxFiniteDuration) // we map the Infinite duration to a special marker,
case _ ⇒ copy(timeout = None) // that means "cancel stateTimeout". This marker is needed
} // so we do not have to break source/binary compat.
// TODO: Can be removed once we can break State#timeout signature to `Option[Duration]` /**
* Send reply to sender of the current message, if available.
*
* @return this state transition descriptor
*/
def replying(replyValue: Any): State[S, D] = {
copy(replies = replyValue :: replies)
} /**
* Modify state transition descriptor with new state data. The data will be
* set when transitioning to the new state.
*/
def using(@deprecatedName('nextStateDate) nextStateData: D): State[S, D] = {
copy(stateData = nextStateData)
} ...
}

FSM DSL中的transform是这样定义的:

  final class TransformHelper(func: StateFunction) {
def using(andThen: PartialFunction[State, State]): StateFunction =
func andThen (andThen orElse { case x ⇒ x })
} final def transform(func: StateFunction): TransformHelper = new TransformHelper(func)

我们看到TransformHelper用using对入参func:StateFunction施用用户提供的andThen: PartialFunction[State,State]后返回新的状态State。这个using与State.using是不同的。下面是一个transform用法例子:

when(Running) {
transform {
case Event(m, Target(Actor.noSender)) =>
goto(Uninitialised) using NoConfig case Event(m, Target(ref)) =>
ref ! m
stay
} using targetTransformer
} def targetTransformer: PartialFunction[State, State] = {
case s @ State(stateName, Target(ref), _, _, _) if ref.path.name.startsWith("testActor") =>
log.debug("Setting target to dead letters")
s.using(Target(Actor.noSender))
}

transform{...}产生的State传给了targetTransformer然后经过模式匹配拆分出properties后用s.using更新stateData。

与become/unbecome相同,我们也可以在FSM里使用stashing。下面是一个用例:

when(Uninitialised) {
case Event(Config(ref), _) =>
goto(Running) using Target(ref) case Event(_, _) =>
stash
stay
} when(Running) {
case Event(m, Target(ref)) =>
ref ! m
stay
} onTransition {
case Uninitialised -> Running => unstashAll()
}

当然,还有如stop,setTimer,replying,forMax,onTermination等方法和函数,这里就不一一详述了,有兴趣可以直接查询Akka/actor/FSM.scala。

下面是本次讨论的示范源码:

import akka.actor._

sealed trait Seasons   //States
case object Spring extends Seasons
case object Summer extends Seasons
case object Fall extends Seasons
case object Winter extends Seasons //sealed trait SeasonData //Data
case class SeasonInfo(talks: Int, month: Int) object FillSeasons {
sealed trait Messages //功能消息
case object HowYouFeel extends Messages
case object NextMonth extends Messages def props = Props(new FillSeasons)
} class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) =>
val numtalks = seasonInfo.talks +
log.info(s"It's ${stateName.toString}, feel so gooood! You've asked me ${numtalks} times.")
stay using seasonInfo.copy(talks = numtalks)
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
val numtalks = stateData.talks +
log.info(s"It's ${stateName.toString}, it's so hot! You've asked me ${numtalks} times")
stay().using(stateData.copy(talks = numtalks))
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's no so bad. You've asked me ${numtalks} times.")
stay using SeasonInfo(numtalks,mnth)
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's freezing cold! You've asked me ${numtalks} times.")
stay using si.copy(talks = numtalks)
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
val mth = seasonInfo.month
if (mth <= ) {
log.info(s"It's month ${mth+1} of ${stateName.toString}")
stay using seasonInfo.copy(month = mth + )
}
else {
goto(nextSeason(stateName)) using SeasonInfo(,)
}
}
onTransition {
case Spring -> Summer => log.info(s"Season changed from Spring to Summer month ${nextStateData.month}")
case Summer -> Fall => log.info(s"Season changed from Summer to Fall month ${nextStateData.month}")
case Fall -> Winter => log.info(s"Season changed from Fall to Winter month ${nextStateData.month}")
case Winter -> Spring => log.info(s"Season changed from Winter to Spring month ${nextStateData.month}")
} initialize() //设定起始状态
log.info(s"It's month 1 of ${stateName.toString}") //季节转换顺序
def nextSeason(season: Seasons): Seasons =
season match {
case Spring => Summer
case Summer => Fall
case Fall => Winter
case Winter => Spring
}
} object FSMDemo extends App {
import scala.util.Random
val fsmSystem = ActorSystem("fsmSystem")
val fsmActor = fsmSystem.actorOf(FillSeasons.props,"fsmActor") ( to ).foreach { _ =>
( to Random.nextInt()).foreach{ _ =>
fsmActor ! FillSeasons.HowYouFeel
}
fsmActor ! FillSeasons.NextMonth
} scala.io.StdIn.readLine()
fsmSystem.terminate()
}

Akka(7): FSM:通过状态变化来转换运算行为的更多相关文章

  1. robotframework中的用evaluate关键字进行运算(随机数+转换+运算)

    当我们在写rf测试用例时,可能需要随机产生一些数据,可能需要将已有的数据进行转换,做简单的运算等:此时我们可以用万能的evaluate来实现 ,后面一般均适用python表达式来进行实现. 接下来详细 ...

  2. Akka FSM 源代码分析

    Akka FSM 源代码分析 萧猛 <simonxiao@qq.com> 啰嗦几句 有限状态机本身不是啥新奇东西,在GoF的设计模式一书中就有状态模式, 也给出了实现的建议.各种语言对状态 ...

  3. Akka(6): become/unbecome:运算行为切换

    通过一段时间的学习了解,加深了一些对Akka的认识,特别是对于Akka在实际编程中的用途方面.我的想法,或者我希望利用Akka来达到的目的是这样的:作为传统方式编程的老兵,我们已经习惯了直线流程方式一 ...

  4. Akka源码分析-FSM

    akka还有一个不常使用.但我觉得比较方便的一个模块,那就是FSM(有限状态机).我们知道了akka中Actor模型的具体实现之后,就会发现,Akka的actor可以非常方便的实现FSM.其实在akk ...

  5. Akka(16): 持久化模式:PersistentFSM-可以自动修复的状态机器

    前面我们讨论过FSM,一种专门为维护内部状态而设计的Actor,它的特点是一套特殊的DSL能很方便地进行状态转换.FSM的状态转换模式特别适合对应现实中的业务流程,因为它那套DSL可以更形象的描述业务 ...

  6. [翻译]AKKA笔记 - 有限状态机 -1

    原文地址:http://rerun.me/2016/05/21/akka-notes-finite-state-machines-1/ 我最近有个机会在工作上使用了Akka FSM,是个非常有趣的例子 ...

  7. Akka Typed 官方文档之随手记

    ️ 引言 近两年,一直在折腾用FP与OO共存的编程语言Scala,采取以函数式编程为主的方式,结合TDD和BDD的手段,采用Domain Driven Design的方法学,去构造DDDD应用(Dom ...

  8. SQL Server 进制转换函数

    一.背景 前段时间群里的朋友问了一个问题:“在查询时增加一个递增序列,如:0x00000001,即每一个都是36进位(0—9,A--Z),0x0000000Z后面将是0x00000010,生成一个像下 ...

  9. .NET LINQ 转换数据类型

    转换数据类型      转换方法更改输入对象的类型.      LINQ 查询中的转换运算可用于各种应用程序.下面是一些示例: Enumerable.AsEnumerable<TSource&g ...

随机推荐

  1. Neural Networks and Deep Learning(神经网络与深度学习) - 学习笔记

    catalogue . 引言 . 感知器及激活函数 . 代价函数(loss function) . 用梯度下降法来学习-Learning with gradient descent . 用反向传播调整 ...

  2. Android 正则表达式验证手机号、姓名(包含少数民族)、身份证号

    最近项目中新增的功能,需要对手机号.姓名.身份证号等一些信息进行验证,最好的方法是通过正则表达式来验证,网上查了一些资料,写了这几个工具方法. 1.验证手机号 规则:第一位只能是1,第二位为3-8中的 ...

  3. vector介绍

    vector(向量,也可称为容器): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的. 1.1  ...

  4. LESS的一点自己的理解(1)

    写前端的时间也不短了,Less也用过几次,都是现学现用,没有仔细的梳理过,今天就有条理的梳理下.参考文章是一篇不错的哦.下面会附上链接的,废话不多说,开始正题. 首先应该说下什么是Less,Less是 ...

  5. [.NET] 《Effective C#》快速笔记 - C# 高效编程要点补充

    <Effective C#>快速笔记 - C# 高效编程要点补充 目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 ...

  6. Linux常用快捷按键

    Linux常用快捷按键 为了提高工作效率 1 一定用快捷键 这里简单的说下几个常用的快捷按键. Ctrl + l    清屏,相当于clear命令. Ctrl + z    挂起,程序放到后台,程序没 ...

  7. 用Python设计一个经典小游戏

    这是关于Python的第9篇文章,介绍如何用Python设计一个经典小游戏:猜大小. 在这个游戏中,将用到前面我介绍过的所有内容:变量的使用.参数传递.函数设计.条件控制和循环等,做个整体的总结和复习 ...

  8. HDU4712 Hamming Distance (随机化)

    link:http://acm.hdu.edu.cn/showproblem.php?pid=4712 题意:给1e5个数字,输出这些数中,最小的海明码距离. 思路:距离的范围是0到20.而且每个数的 ...

  9. Python进阶之装饰器

    函数也是对象 要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用.既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一 ...

  10. CentOS7.2静默安装oracle11g

    http://www.centoscn.com/image-text/config/2015/0528/5552.html http://www.linuxidc.com/Linux/2016-04/ ...