Clojure进阶:使用Clojure编写文字冒险游戏

本文翻译自:Casting SPELs in Clojure

1 准备

任何学过Lisp的人都会说List和其它语言有很大的不同.它有很多不可思议的地

方.本文将告诉你它有哪些独特之处!

本文适用于Clojure,它是一个运行在JVM上的Lisp方言.Clojure的API和语法和

Common Lisp很类似,但是还是有足够多的区别,需要单独为其写个教程.

在大部分情况下,我们会说Lisp而不是Clojure,因为大部分的概念在Lisp中是通

用的.我们会指出Clojure特有的内容.

Clojure是运行在JVM之上的,所以你需要先安装JVM.如果你是MAC机,那么Java已

经被安装过了.如果是Linux或者Windows系统,你需要到Oracle Java官网下载对

应版本的Java.而Clojure,你可以从它的官网获得最新版本.

下载完成后,你只需要解压缩,打开命令行,切换到解压缩目录,输入:

java -jar clojure.jar

如果没有问题,那么你将会看到Clojure输出提示

Clojure 1.6.0
user=>

教程中有很多Clojure代码片段,类似下面的样子:

‘(these kinds of boxes)

你只需要将这些代码片段拷贝到Clojure REPL中运行就可以了!当你学习完此教

程,你将会有一个你自己的文字冒险游戏了!

2 语法和语义

每一个编程语言是由语法和语义组成的.语法是组成你的程序的骨架,你必须要

遵循它们,这样编译器才能知道你的程序里什么是什么,比如说什么是函数,什么

是变量,等等!而语义是个比较”随便”的东西,例如你的程序里有哪些不同的命令,

或者在程序的哪个部分能访问到哪些变量!这里Lisp比较特别的地方就是,Lisp的

语法笔其它任何语言都要简单.

首先,Lisp语法规定,所有传递给Lisp编译器的文本需要是个list,当然这个list

可以无限嵌套.每个list都必须使用括号包裹.

另外,Lisp编译器使用两种模式来读取你的代码:代码模式和数据模式.当你在数

据模式下,你可以将任何东西塞到你的list中.但是在代码模式下,你的list需要

是叫做form的特殊类型.

form也是个list,不过它的第一个符号被lisp编译器特殊对待了—一般被当做函

数的名字.在这种情况下,编译器会将list中的其它元素作为函数参数传递给这个

函数.默认情况下,编译器运行在代码模式下,除非你特意告诉它进入数据模式.

3 为我们的游戏世界定义数据

为了进一步的学习form,让我们来创建一些form,来定义我们游戏世界里的数据.首

先,我们的游戏有一些对象,玩家可以使用他们–让我们来定义吧:

(def objects ‘(whiskey-bottle bucket frog chain))

让我们来看看这行代码是什么意思:Lisp编译器总是使用代码模式来读取内容,所

以第一个符号(这里是def),肯定是个命令.在这里,它的作用就是给某个变量设

值:这里变量就是objects,而值是一个包含四个对象的list.这个list是数据(我

们可不想编译器去调用一个叫做whiskey-bottle的函数),所以在读取这个list时

我们需要将其设值为数据模式.在list前面的哪个单引号就是干这个的:

def命令就是用来设值的(如果你学过Common Lisp,你应该会知道它和Common

Lisp中的setf命令等价,但是Clojure中没有setf命令)

现在我们在游戏里定义了一些对象,现在让我们来定义一下游戏地图.下面是我们

的游戏世界:

在这个简单的游戏里,只有三个地点:一个房子,它包含起居室,阁楼和花园.让我

们来定义一个新变量,叫做game-map来描述这个游戏地图:

(def game-map (hash-map
   ‘living-room ‘((you are in the living room
                   of a wizards house - there is a wizard
                   snoring loudly on the couch -)
                  (west door garden)
                  (upstairs stairway attic))
   ‘garden ‘((you are in a beautiful garden -
              there is a well in front of you -)
             (east door living-room))
   ‘attic ‘((you are in the attic of the
             wizards house - there is a giant
             welding torch in the corner -)
            (downstairs stairway living-room))))

这个map包含了三个地点的所有重要信息:每个地点都有个独立的名字,一个简短

的描述,描述了我们能在这些地点看到什么,以及如何进入此处或从此处出去.请

注意这个包含了丰富信息的变量是如何定义的—Lisp程序员更喜欢用小巧的代

码片段而不是一大片代码,因为小代码更容易理解.

现在我们有了一个地图和一组对象,让我们来创建另一个变量,来描述这些对象在

地图的哪些地方.

(def object-locations (hash-map
                       ‘whiskey-bottle ‘living-room
                       ‘bucket ‘living-room
                       ‘chain ‘garden
                       ‘frog ‘garden))

这里我们将每个对象和地点进行了关联.Clojure提供了Map这个数据结构.Map使

用hash-map函数来创建,它需要一组参数类似(key1 value1 keys value2…).我

们的game-map变量也是个Map—三个key分别是living-room,garden和attic.

我们定义了游戏世界,以及游戏世界中的对象,现在就剩下一件事了,就是描述玩

家的地点!

(def location ‘living-room)

搞定,现在让我们来定义游戏操作吧!

4 环顾我们的游戏世界

我们想要的第一个命令能够告诉我们该地点的描述.那么我们该怎么定义这个函

数呢?它要知道我们想要描述的地点以及能够从map中查找地点的描述.如下:

(defn describe-location [location game-map]
  (first (location game-map)))

defn定义了一个函数.函数的名字叫做describe-location,它需要两个参数:地点

和游戏地图.这两个变量在函数定义的括号内,所以它们是局部变量,因此对于全

局的location和game-map没有关系.注意到了吗?Lisp中的函数与其它语言中的函

数定义相比,更像是数学中的函数:它不打印信息或者弹出消息框:它所作的就是

返回结果.我们假设现在我们在起居室里!

为了能找到起居室的描述,describe-locatin函数首先需要从地图中找到起居

室.(location game-map)就是进行从game-map中查找内容的,并返回起居室的描

述.然后first命令来处理返回值,取得返回的list的第一个元素,这个就是起居室

的描述了.

现在我们来测试一下

(describe-location ‘living-room game-map)
user=> (describe-location ‘living-room game-map)
(you are in the living-room of a wizard‘s house -
there is a wizard snoring loudly on the couch -)

很完美!这就是我们要的结果!请注意我们在living-room前添加了一个单引号,因

为这个符号是地点map的一个名称!但是,为什么我们没有在game-map前面添加单

引号呢?这是因为我们需要编译器去查询这个符号所指向的数据(就是那个map)

5 函数式编码风格

你可能已经发现了describe-location函数有几个让人不太舒服的地方.第一,为

什么要传递位置和map参数,而不是直接使用已经定义的全局变量?原因是Lisp程

序员喜欢写函数式风格的代码.函数式风格的代码,主要遵循下面三条规则:

  • 只读取函数传递的参数或在函数内创建的变量
  • 不改变已经被设值的变量的值
  • 除了返回值,不去影响函数外的任何内容

你也许会怀疑在这种限制下你还能写代码吗?答案是:可以!为什么很多人对这些

规则感到疑惑呢?一个很重要的原因是:遵循此种风格的代码更加的引用透明

(referential transparency):这意味着,对于给定的代码,你传入相同的参数,永

远返回相同的结果—这能减少程序的错误,也能提高程序的生产力!

当然了,你也会有一些非函数式风格的代码,因为不这么做,你无法和其它用户或

外部内容进行交互.教程后面会有这些函数,他们不遵循上面的规则.

describe-location函数的另一个问题是,它没告诉我们怎么进入一个位置或者怎

么从某个位置出来.让我们来编写这样的函数:

(defn describe-path [path]
  `(there is a ~(second path) going ~(first path) from here -))

这个函数看起来很明了:它看起来更像是数据而不是函数.我们先来尝试调用它,看

它做了些什么:

(describe-path ‘(west door garden))
user=> (describe-path ‘(west door garden))
(user/there user/is user/a door user/going west user/from user/here clojure.core/-)

这是什么?!结果看起来很乱,包含了很多的/和一些其它的文字!这是因为Clojure

会将命名空间的名字添加到表达式的前面.我们这里不深究细节,只给你提供消除

这些内容的函数:

(defn spel-print [list] (map (fn [x] (symbol (name x))) list))

修改调用方式

(spel-print (describe-path ‘(west door garden)))
user=> (spel-print (describe-path ‘(west door garden)))
(there is a door going west from here -)

现在结果很清晰了:这个函数接收一个描述路径的list然后将其解析到一个句子

里面.我们回过头来看这个函数,这个函数和它产生的数据非常的像:它就是拼接

第一个和第二个list的元素到语句中!它是怎么做到的?使用语法quote!

还记得我们使用quote来从代码模式切换到数据模式吗?语法quote的功能类似,但

是还不只这样.在语法quote里,我们还能使用’~‘再次从数据模式切换回代码模式.

语法quote是List的一个很强大的功能!它能使我们的代码看起来像它创建的数据.这

在函数式编码中很常见:创建这种样子的函数,使得我们的代码更易读也更稳健:

只要数据不变,函数就不需要修改.想象一下,你能否在VB或C中编写类似的代码?

你可能需要将文字切成小块,然后在一点点的组装-这和数据本身看起来差距很大,更

别说代码的稳健性了!

现在我们能描述一个路径,但是一个地点可能会有多个路径,所以让我们来创建一

个函数叫做describe-paths:

(defn describe-paths [location game-map]
  (apply concat (map describe-path (rest (get game-map location)))))

这个函数使用了另一个在函数式编程中很常用的技术:高阶函数.apply和map这两

个函数能将其它的函数作为参数.map函数将另一个函数分别作用到list中的每个

对象上,这里是调用describe-path函数.apply concat是为了减少多余的括号,没

有多少功能性操作!我们来试试新函数

(spel-print (describe-paths ‘living-room game-map))
user=> (spel-print (describe-paths ‘living-room game-map))
(there is a door going west from here -
there is a stairway going upstairs from here -)

漂亮!

最后,我们还剩下一件事要做:描述某个地点的某个对象!我们先写个帮助函数来

告诉我们在某个地方是否有某个对象!

(defn is-at? [obj loc obj-loc] (= (obj obj-loc) loc))

=也是个函数,它判断对象的地点是否和当前地点相同!

我们来尝试一下:

(is-at? ‘whiskey-bottle ‘living-room object-locations)
user=> (is-at? ‘whiskey-bottle ‘living-room object-locations)
true

返回结果是true,意味着whiskey-bottle在起居室.

现在让我们来使用这个函数描述地板:

(defn describe-floor [loc objs obj-loc]
  (apply concat (map (fn [x]
                       `(you see a ~x on the floor -))
                     (filter (fn [x]
                               (is-at? x loc obj-loc)) objs))))

这个函数包含了很多新事物:首先,它有匿名函数(fn定义的函数).第一个fn干的

事,和下面的函数做的事情是一样的:

(defn blabla [x] `(you see a ~x on the floor.))

然后将这个blabla函数传递给map函数.filter函数是过滤掉那些在当前位置没有

出现的物体.我们来试一下新函数:

(spel-print (describe-floor ‘living-room objects object-locations))
user=> (spel-print (describe-floor ‘living-room objects object-locations))
(you see a whiskey-bottle on the floor - you see a bucket on the floor -)

现在,让我们来将这些函数串联起来,定义一个叫look的函数,使用全局变量(这个

函数就不是函数式的了!)来描述所有的内容:

(defn look []
  (spel-print (concat (describe-location location game-map)
          (describe-paths location game-map)
          (describe-floor location objects object-locations))))

我们来试一下:

user=> (look)
(you are in the living room of a wizards house -
there is a wizard snoring loudly on the couch -
there is a door going west from here -
there is a stairway going upstairs from here -
you see a whiskey-bottle on the floor -
you see a bucket on the floor -)

很酷吧!

6 环游我们的游戏世界

好了,现在我们能看我们的世界了,让我们来写一些代码来环游我们的世

界.walk-direction包含了一些方向可以使我们走到那里:

(defn walk-direction [direction]
  (let [next (first (filter (fn [x] (= direction (first x)))
                            (rest (location game-map))))]
    (cond next (do (def location (nth next 2)) (look))
          :else ‘(you cannot go that way -))))

这里的let用来创建局部变量next,用来描述玩家的方向.rest返回一个list,包含

原list中除了第一个元素外的全部元素.如果用户输入了错误的方向,next会返回

().cond类似于if-then条件:每个cond都包含一个值,lisp检查该值是否为真,如

果为真则执行其后的动作.在这里,如果下一个位置不是nil,则会定义玩家的

location到新位置,然后告诉玩家该位置的描述!如果next是nil,则告诉玩家,无

法到达,请重试:

(walk-direction ‘west)
user=> (walk-direction ‘west)
(you are in a beautiful garden -
there is a well in front of you -
there is a door going east from here -
you see a frog on the floor -
you see a chain on the floor -)

现在,我们通过创建look函数来简化描述.walk-direction也是类似的功能.但是

它需要输入方向,而且还有个quote.我们能否告诉编译器west仅仅是个数据,而不是

代码呢?

7 构建SPELs

现在我们开始学习Lisp中一个很强大的功能:创建SPELs!

SPEL是”语义增强逻辑”的简称,它能够从语言级别,按照我们的需求定制,对我们的代码添加新的行为-这

是Lisp最为强大的一部分.为了开启SPELs,我们需要先激活Lisp编译器的SPEL

(defmacro defspel [& rest] `(defmacro [email protected]))

现在,我们来编写我们的SPEL,叫做walk:

(defspel walk [direction] `(walk-direction ‘~direction))

这段代码干了什么?它告诉编译器walk不是实际的名称,实际的名字叫

walk-direction,并且direction前面有个quote.SPEL的主要功能就是能在我们的

代码被编译器编译之前插入一些内容!

注意到了吗?这段代码和我们之前写的describe-path很类似:在Lisp中,不只是代

码和数据看起来很像,代码和特殊形式对于编译器来说也是一样的-高度的统一带

来简明的设计!我们来试试新代码:

(walk east)
user=> (walk east)
(you are in the living room of a wizards house -
there is a wizard snoring loudly on the couch -
there is a door going west from here -
there is a stairway going upstairs from here -
you see a whiskey-bottle on the floor -
you see a bucket on the floor -)

感觉好多了!

现在我们来创建一个命令来收集游戏里的物品

(defn pickup-object [object]
  (cond (is-at? object location object-locations)
        (do
          (def object-locations (assoc object-locations object ‘body))
          `(you are now carrying the ~object))
        :else ‘(you cannot get that.)))

这个函数检查物品是否在当前地点的地上-如果在,则将它放到list里面,并返回

成功提示!否则提示失败!

现在我们来创建另一个SPEL来简化这条命令:

(defspel pickup [object] `(spel-print (pickup-object ‘~object)))

调用

(pickup whiskey-bottle)
user=> (pickup whiskey-bottle)
(you are now carrying the whiskey-bottle)

现在我们来添加更多有用的命令-首先,一个能让我们查看我们捡到的物品的函

数:

(defn inventory []
  (filter (fn [x] (is-at? x ‘body object-locations)) objects))

以及一个检查我们是否有某个物品的函数:

(defn have? [object]
   (some #{object} (inventory)))

8 创建特殊操作

现在我们只剩下一件事情需要做了:添加一些特殊动作,使得玩家能够赢得游戏.第

一条命令是让玩家在阁楼里给水桶焊接链条.

(def chain-welded false)

(defn weld [subject object]
  (cond (and (= location ‘attic)
             (= subject ‘chain)
             (= object ‘bucket)
             (have? ‘chain)
             (have? ‘bucket)
             (not chain-welded))
        (do (def chain-welded true)
            ‘(the chain is now securely welded to the bucket -))
        :else ‘(you cannot weld like that -)))

首先我们创建了一个新的全局变量来进行判断,我们是否进行了此操作.然后我们

创建了一个weld函数,来确认此操作的条件是否完成,如果已完成则进行此操作.

来试一下:

(weld ‘chain ‘bucket)
user=> (weld ‘chain ‘bucket)
(you cannot weld like that -)

Oops…我们没有水桶,也没有链条,是吧?周围也没有焊接的机器!

现在,让我们创建一条命令来将链条和水桶放到井里:

(def bucket-filled false)

(defn dunk [subject object]
  (cond (and (= location ‘garden)
             (= subject ‘bucket)
             (= object ‘well)
             (have? ‘bucket)
             chain-welded)
        (do (def bucket-filled true)
            ‘(the bucket is now full of water))
        :else ‘(you cannot dunk like that -)))

注意到了吗?这个命令和weld命令看起来好像!两条命令都需要检查位置,物体和

对象!但是它们还是有不同,以至于我们不能将它们抽到一个函数里.太可惜了!

但是…这可是Lisp.我们不止能写函数,还能写SPEL!我们来创建了SPEL来处理:

(defspel game-action [command subj obj place & args]
  `(defspel ~command [subject# object#]
     `(spel-print (cond (and (= location ‘~‘~place)
                             (= ‘~subject# ‘~‘~subj)
                             (= ‘~object# ‘~‘~obj)
                             (have? ‘~‘~subj))
                        [email protected]‘~args
                        :else ‘(i cannot ~‘~command like that -)))))

非常复杂的SPEL!它有很多怪异的quote,语法quote,逗号以及很多怪异的符号!更

重要的是他是一个构建SPEL的SPEL!!即使是很有经验的Lisp程序员,也需要费下

脑细胞才能写出这么个玩样!!(这里我们不管)

这个SPEL的只是向你展示,你是否够聪明来理解这么复杂的SPEL.而且,即使这段

代码很丑陋,如果它只需要写依次,并且能生成几百个命令,那么也是可以接受的!

让我们使用这个新的SPEL来替换我们的weld命令:

(game-action weld chain bucket attic
   (cond (and (have? ‘bucket) (def chain-welded true))
              ‘(the chain is now securely welded to the bucket -)
         :else ‘(you do not have a bucket -)))

现在我们来看看这条命令变得多容易理解- game-action这个SPEL使得我们能编

写我们想要的核心代码,而不需要额外的信息.这就像我们创建了我们自己的专门

创建游戏命令的编程语言.使用SPEL创建伪语言称为领域特定语言编程(DSL),它

使得你的编码更加的快捷优美!

(weld chain bucket)
user=> (weld chain bucket)
(you do not have a chain -)

…我们还没有做好焊接前的准备工作,但是这条命令生效了!

下面我们重写dunk命令:

(game-action dunk bucket well garden
             (cond chain-welded
                   (do (def bucket-filled true)
                       ‘(the bucket is now full of water))
                   :else ‘(the water level is too low to reach -)))

注意weld命令需要检验我们是否有物体,但是dunk不需要.我们的game-action这

个SPEL使得这段代码易写易读.

最后,就是将水泼到巫师身上:

(game-action splash bucket wizard living-room
             (cond (not bucket-filled) ‘(the bucket has nothing in it -)
                   (have? ‘frog) ‘(the wizard awakens and sees that you stole
                                       his frog -
                                       he is so upset he banishes you to the
                                       netherworlds - you lose! the end -)
                   :else ‘(the wizard awakens from his slumber and greets you
                               warmly -
                               he hands you the magic low-carb donut - you win!
                               the end -)))

现在你已经编写完成了一个文字冒险游戏了!

点击这里是完整的游戏.

点击这里是代码.

为了使教程尽可能的简单,很多Lisp的执行细节被忽略了,所以最后,让我们来看

看这些细节!

9 附录

现在,我们来聊一聊被忽略的细节!

首先,Clojure有一套很成熟的定义变量以及改变变量值的系统.在此教程中,我们

只使用了def来设置和改变全局变量的值.而在真正的Clojure代码里,你不会这么

做.取而代之,你会使用Refs,Atoms和Agents,它们提供了更清晰,以及线程安全的

方式来管理数据.

另一个问题就是我们在代码中大量使用了符号(symbol)

‘(this is not how Lispers usually write text)
"Lispers write text using double quotes"

符号在Clojure有特殊含义,主要是用来持有函数,变量或其它内容的.所以,在

Lisp中将符号作为文本信息描述是很奇怪的事情!使用字符串来显示文本信息可

以避免这样的尴尬!不过,使用字符串的话,在教程里就没法讲很多关于符号的内

容了!

还有就是SPEL在Lisp里面更普遍的叫法是”宏”,使用defmacro来定义,但是这个名

字不易于教学,所以没有提及.你可以阅读此文,这是我为什么没有使用”宏”这个

名字的原因.最后,在编写类似game-action这样的SPEL的时候,很可能会发生命名

重复的问题.当你编写了足够多的lisp的时候,你会越来越能体会到这个问题了.

Q. 后面我该阅读哪些内容来扩充我的Lisp知识?

A. 在cliki网站有很多Lisp书籍可以下载.

如果你对于理论上的内容很感兴趣,那么我推荐Paul Graham的

On Lisp电子书,它是免费的.他网站上的一些短文也很精彩.

如果你对实际应用比较感兴趣,那么大多数Lisp程序员对Perter Seibel编写的”Practical Common

Lisp”这本书推崇有加,你可以从这里获得

10 为什么没有使用”宏”这个词

编写这个教程的一个意图是使用宏来解决真实的难题.而经常的,当我向没有Lisp

经验的人解释宏这个概念的时候,我得到的答复往往是,”哦!C++里也有宏”.当发

生这种事情的时候,我就很难去解释宏的概念了.的确,Lisp中的宏和C++中的宏的

确有几分相似,它们都是为了能通过编译器来改进代码的编写…

…所以,假设一下,如果John McCarthy使用了”add”而不是”cons”这个词来将元

素添加到list中:我就真的很难解释cons是如何工作的了!

所以,我决定在此文中使用一个新的词汇:SPEL,语义增强逻辑的简称,它更易理解

一些:

  • 它解释了Lisp宏的核心功能,能改变Lisp运行环境的行为
  • SPEL这个术语可以被用来很高雅的解释很多语言上观念.
  • 这个术语不会导致Lisp中的宏与其它的宏被混为一谈
  • SPEL这个词重复的可能性非常低.Google搜索”macro 或者 macros 程序 -lisp

    -scheme”返回大概1150000条结果.而搜索”spel 或者 spels 程序 -lisp

    -scheme”值返回28400条结果.

所以,我希望,作为一个Lisp程序员,你能接受这个术语-当然了,像这样的新词汇

会被接受的可能性非常低.

如果你有一个库或者是一个Lisp实现者,请先放下你手头上的工作,先在你的库里,添

加下面这行代码:

(defmacro defspel [& rest] `(defmacro [email protected]))

11 译者感想

  • 本人对Lisp的宏还是有些了解的,所以个人无法接受SPEL这个新词汇
  • 且SPEL使得代码不易阅读,就game-action这个SPEL来说,使用了两层,而使用宏

    只需要一层

  • 附录中是我使用Clojure的惯用法重新改写的代码,且文字翻译成了中文.以及

    使用了宏而不是SPEL.各位可比较,自行选择

源代码

Clojure进阶:使用Clojure编写文字冒险游戏

时间: 2024-08-01 15:32:34

Clojure进阶:使用Clojure编写文字冒险游戏的相关文章

有关css编写文字动态下划线

<div class="main_text">哈哈这就是我的小视频</div> 上面为html代码 接下来进行css的编写 .main_text{ position:relative; //给其一个相对定位 } .main_text::after{ //我们要对其使用伪类元素 content:""; width:100%; height:1px; position:absolute; left:0; bottom:0; background-

Clojure学习笔记(一)——介绍、安装和语法

什么是Clojure Clojure是一种动态的.强类型的.寄居在JVM上的语言. Clojure的特性: 函数式编程基础,包括一套性能可以和典型可变数据结构媲美的持久性数据结构 由JVM提供的成熟的.高效的运行时环境:所以Clojure可以使用Java类库,反之Clojure库也可以被Java使用 跟JVM/Java的互操作能力使得很多架构.运维方面的需求可以得到满足:Clojure代码可以像Java代码一样被打包,然后部署到任何Java应用可以部署的地方 一套提供并发.并行语义的机制:Clo

Clojure命名空间

版本 本文翻译自Clojure Namespaces and Vars 本文涵盖如下内容: + Clojure命名空间和var概述 + 如何定义命名空间 + 如何使用其它命名空间里的函数 + require,refer和use + 常见错误和典型错误,以及导致这些错误的原因 + 命名空间和代码管理 版权: This work is licensed under a Creative Commons Attribution 3.0 Unported License (including image

[转] Clojure 快速入门指南:1/3

[From] http://huangz.iteye.com/blog/1325228 导读 本文的目标是为熟悉 Ruby.Python或者其他类似语言.并对 Lisp 或者函数式编程有一定程度了解的程序员写的 Clojure 快速入门指南. 为了让文章尽可能地精炼且简单易懂,本文有以下三个特点: 一:不对读者的知识水平作任何假设,当遇上重要的知识点时,只给出 wikipedia 等网站的链接引用而不对知识点进行解释,有需要的读者可以沿着链接查看,没需要的直接略过就行了. 二:和第一条类似,没有

理解Clojure STM 软件事务性内存

翻译说明: 英文原文来自:http://java.ociweb.com/mark/stm/article.html 原文包含了一些非STM的知识,也包括STM底层实现的内容,这里只是翻译了STM抽象层的内容,自认为这部分比较重要. 翻译是基于自己能够理解的方式翻译的,并非逐句翻译,目的是理解STM,理解如何调优STM,有逐句翻译强迫症的同学请不要喷我! 本人是在学习<Clojure编程乐趣>的"压力之下的 Ref"章节,遇到无法理解minHistory和maxHistory

建立Clojure开发环境 - 使用IntelliJ Idea和Leiningen

起步Clojure编程. OS: Ubuntu 14.10 IDEA 14.0.3 Ultimate 安装Leiningen 按照http://leiningen.org/的指南安装好lein 安装La Clojure 安装Idea插件La Clojure.启动Idea,点左上角的File --> Settings  --> Plugins --> 搜"Clojure" , 然后找到La Clojure, 然后Install. 新建项目 在workspace下建立cl

Clojure操作mysql

在Eclipse中新建一个Clojure工程clj01 clojure 操作mysql需要依赖mysql-connector-java.clojure-contrib与java.jdbc三个jar包. project.clj配置信息如下:配置完:dependencies 保存,系统会自动下载所配置的jar包信息. (defproject clj01 "0.1.0-SNAPSHOT" :description "FIXME: write description" :u

Clojure新手入门

官方网站 clojure.org 环境安装 Java(JDK) Leiningen 编辑工具 Eclipse插件 -- Counterclockwise IntelliJ插件 -- Cursive Emacs -- Cider N分钟学会Clojure Learn X in Y minutes where X=Clojure 文档汇总 官方文档 -- Clojure API Documentation 社区文档其一 -- Clojure Doc 社区文档其二 -- Clojure Docs 社区

互联网计算机技术方面的入门书籍(转自知乎答主:专业主义)

互联网计算机技术方面的入门书籍有哪些推荐? 大三学生,在校有参与项目(网站目前在建中),计算机水平只有 VB 二级,将来想从事互联网产品方面的工作,现在想在技术上面多充电,应该多看些那些方面的书呢? 产品经理.用户体验方面的书都有看过,觉得想从事这方面工作还是要懂点技术,现求互联网.计算机技术方面偏入门一点的书. test 善用搜索.Github 上除了项目代码之外,还有很多编程相关的资源列表,比如下面这个: 免费的编程中文书籍索引 免费的编程中文书籍索引,欢迎投稿. 国外程序员在 stacko