Clojure作业笔记
为了学分苟活的学渣我也就修修这种课来赚学分了。
Clojure是什么玩意:
Clojure is a young Lispish functional programming language on the JVM platform, suitable for small and large programs. Because it runs on the JVM, all Clojure programs can use all the standard and third-party Java libraries freely.
user=> (+ 1 2)
3
user=>
以上是一个标准的Clojure程序代码和输出,它是一种解释性语言。
在这门课的material里推荐使用light table作为编译器,大约也是因为可以很好的即时执行Clojure代码的缘故。
一个Clojure的function实现:
(defn ; Start a function definition:
hello ; name
"Gives out personalized greetings." ; a optional docstring
[who] ; parameters inside brackets
(str "Hello, " who "!")) ; body
;这个符号在clojure里起到注释的作用。
让我们来看看在Java下实现同样的语句效果时的代码是怎么样的:
/*
* Gives out personalized greetings.
*/
String hello(String who) {
return "Hello, " + who + "!";
}
没错,你可以发现,在Clojure里没有return关键字。
Note that in Clojure, there is no return keyword. The return value of a function is always the value of the last expression in the function body.
(defn sign [x]
(if (< x 0)
"-"
"+"))
(sign 2) ;=> "+"
(sign -2) ;=> "-"
(sign 0) ;=> "+"
以上可以看出来如下说明:
In functional programming, and specifically in Clojure, everything is an expression. This is a way of saying that everything has a usable value.
(let [name1 value1
name2 value2
...]
(expression1)
(expression2)
...)
user=> (let [x 42]
(+ x x))
;=> 84
user=> x
CompilerException java.lang.RuntimeException:Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH:0)
当需要在一个function里声明一个function或者value时,可以使用let关键字。但是此时let声明出的变量或者函数不能在此函数外部使用,说明这是一个local name。
What kind of values Clojure supports:
Type | Examples | Description |
---|---|---|
Numbers | 42, 3/2, 2.1 | Numbers include integers, fractions, and floats. |
Strings | "foo" | Text values. |
Characters | \x, \y, \√ | A single characer is written with a preceding \. |
Keywords | :foo, :? | Values often used as map keys. |
Booleans | true, false | Boolean values. |
Clojure还支持混合的数据结构:
A vector is a collection that can be indexed with integers, like an array in other languages. It can contain values of different types.
[1 2 3] ;=> [1 2 3]
[:foo 42 "bar" (+ 2 3)] ;=> [:foo 42 "bar" 5]
向量需要用符号[]来表示。不仅是空格,也可以用逗号来分割vector里的数据。以下是怎么获取向量里数据的几个例子:
(get ["a" "b" "c"] 1) ;=> "b"
(get ["a" "b" "c"] 15) ;=> nil
(get ["x"] 0) ;=> "x"
Vectors are immutable即一个已经声明好的向量是不可改变的,但是可以随时利用它来创造新的向量,例如:(conj和assoc是一个函数)
(conj [1 2 3] 4) ;=> [1 2 3 4]
(assoc [1 2 3 4] 2 "foo") ;=> [1 2 "foo" 4]
Vectors: A Postmodern Deconstruction
这个我不知道怎么翻译好,似乎是一种提取向量里的值的方法,说是by destructuring,如下:
(let [[x y z] [1 2 3 4 5 6]]
(str x y z))
;=> "123"
可以看到x,y,z分别抽取出了向量里1,2,3的值。
(defn sum-pairs [[x1 y1] [x2 y2]]
[(+ x1 x2) (+ y1 y2)])
可以看到我们声明了一个函数,这两个向量的第一个值第二个值分别相加,造出了一个新的向量。我认为这就是一种破坏再生的过程,即destructuring的含义所在。后边的练习大概就是关于利用这个特性各种各样拆分向量来计算,答案如下:
(defn point [x y]
[x y])
(defn rectangle [bottom-left top-right]
[bottom-left top-right])
(defn width [rectangle]
(let [[[x1 y1] [x2 y2]] rectangle]
(- x2 x1)))
(defn height [rectangle]
(let [[[x1 y1] [x2 y2]] rectangle]
(- y2 y1)))
(defn square? [rectangle]
(= (width rectangle) (height rectangle)))
(defn area [rectangle]
(* (width rectangle) (height rectangle)))
(defn contains-point? [rectangle point]
(let [[[x1 y1] [x2 y2]] rectangle
[x y] point]
(and (<= x1 x x2) (<= y1 y y2))))
(defn contains-rectangle? [outer inner]
(let [[k1 k2] inner]
(and (contains-point? outer k1) (contains-point? outer k2)))
今天就先这样吧,明天继续努力!
上周的目标没有实现,深感惭愧。忙着做各种事务,本周想着还是先完成clojure的作业比较好,因为本月内要完成所有的作业才能算完成。然后今天要写的大约是关于Clojure的maps概念。——4月13日
Maps
Where a vector associates integers to values, a map is not restricted to integer keys. You can use any kind of value as a key. A map is written with curly brackets, {}
.
{"foo" 42, "bar" 666}
{"mehmeh" (+ 2 5)
"rupatipor" "ropopo"}
A map is indexed with the get function:
(let [ages {"Juhana" 3
"Ilmari" 42
"King of All Cosmos" -6}]
(get ages "King of All Cosmos"))
;=> -6
在一个标准的clojure程序中,一个map的keys大多数都是keywords。 Keywords are a convenient way of naming keys for values in associative collections such as maps. They are written with a preceding :
.
(def book {:title "The City and the City"
:authors [{:name "China Miéville", :birth-year 1972}]})
(get book :title) ;=> "The City and the City"
使用Keywords会比这样调用get更方便。They work as functions that access collections:
(:title book) ;=> "The City and the City"
当像使用function这样使用时,一个关键字会去collection里寻找它自身有什么并且返回它拥有的
count可以用来查找collection中有多少elements。
(count [1 2 3]) ;=> 3
(count {:name "China Miéville", :birth-year 1972}) => 2
(count ":)") => 2
Adding Values to a Map
(assoc a-map a-key a-value)
sets the value of a-key
in a-map
to be a-value
.
(assoc {:a 1} :b 2) ;=> {:b 2, :a 1}
(assoc {:a 1} :a 2) ;=> {:a 2}
Vectors are an associative data structure, so assoc
also works with them.
;index: 0 1 2 0 1 2
(assoc [3 2 1] 1 "~o~") ;=> [3 "~o~" 1]
Assoc
并不会改变最初的数据结构, 只是会返回一个新的version。
(let [original [1 2 3 4]
new (assoc original 2 "foo")]
original)
;=> [1 2 3 4]
The keys and values of a map can be of any data type, and one map can contain any number of different data types as both keys and values.
一个map的keys和values可以是任何数据类型,而且它能包括任意数量的关于keys和values的不同的数据类型。
4月14日:昨天学习一点点Clojure的东西,今天继续学习。
对了,发现了teamtreehouse试用账户如果第一个月暂时不想续费是不可以的,必须续费了才能暂停在上边的学习!要不然第一个月暂时不想继续学的话,只能销毁账户。这一招也是挺犀利的,为了能赚点小钱不容易啊。
Sequences
Many of Clojure’s functions that operate on vectors and other collections actually operate on sequences. The (seq collection)
function returns a sequence constructed from a collection, such as a vector or a map.
Sequences have the following operations:
-
(first sequence)
返回sequence的第一个元素 -
(rest sequence)
返回sequence除了第一个元素的其他元素 -
(cons item sequence)
returns a new sequence where item is the first element and sequence is the rest.返回item是第一个元素、sequence是其余元素的新的sequence
你可以轻易看出,sequence的元素都在括号()
里。
(seq [1 2 3]) ;=> (1 2 3)
(seq {:a 42 :b "foo" :c ["ur" "dad"]})
;=> ([:a 42] [:c ["ur" "dad"]] [:b "foo"])
(first (seq [1 2 3])) ;=> 1
(rest (seq [1 2 3])) ;=> (2 3)
(cons 0 (seq [1 2 3])) ;=> (0 1 2 3)
在使用以上first,rest,cons时,也可以把seq省略,如:
(first [1 2 3]) ;=> 1
(rest [1 2 3]) ;=> (2 3)
(cons 0 [1 2 3]) ;=> (0 1 2 3)
The map function
(map function collection)
map里有两个参数,一个function和一个sequenceable collection即有序集,或者我们可以说是一个sequence。map会让那个function调用sequence里的每个元素,并返回一个新的sequence,它是由原来的sequence里的每个元素被那个function调用过后返回的结果组成的。
(defn munge [x]
(+ x 42))
(map munge [1 2 3 4])
;=> ((munge 1) (munge 2) (munge 3) (munge 4)) ; [note below]
;=> ( 43 44 45 46)
当你有一个关于map的sequence, :keywords
也可以做map里的function使用,举例:
(:name {:name "MEEEE", :secret "Awesome"}) ;=> "MEEEE"
(let [people [{:name "Juhana", :age 3}
{:name "Ilmari", :age 42}
{:name "Jani", :age 72}
{:name "King of All Cosmos" :age -6}]]
(map :age people))
;=> (3 42 72 -6)
4月15日:今天继续学习Clojure,感觉进度有点慢了。大约是因为我又分心跑去teamtreehouse看关于wordpress的教学视频。我为什么会突然不觉得Clojure很重要了呢,大约是因为我看到了暑期还是可以继续修这门课的,作业毕竟是网络提交,很方便。而teamtreehouse是花了钱的,不在上边学总感觉很浪费金钱,突然发觉这也是一种强迫我学习的好方式啊!
补充昨日关于sequence相关的一些东西:
(concat ["China Miéville"] ["Octavia E. Butler"]) ;=> ("China Miéville" "Octavia E. Butler")
concat可以整合向量vector或者sequence,然后生成新的sequence.但是如果是以下这个形式就不能做到,因为只有一个sequence。
(concat (("China Miéville") ("Octavia E. Butler")))
;=> (("China Miéville") ("Octavia E. Butler"))
(apply function a-seq)
applies function to the arguments in a-seq
. Here’s an example:
(apply + [1 2 3])
;=> (+ 1 2 3)
;=> 6
(apply concat [["China Miéville"] ["Octavia E. Butler"]])
;=> (concat ["China Miéville"] ["Octavia E. Butler"])
;=> ("China Miéville" "Octavia E. Butler")
通常来说,apply是这样使用的:
(apply function [arg1 arg2 arg3 ...]) => (function arg1 arg2 arg3 ...)
The function (repeat n x)
returns a sequence with n x s:
(repeat 5 "*") ;=> ("*" "*" "*" "*" "*")
(repeat 3 "~o~") ;=> ("~o~" "~o~" "~o~")
Set
还有一个主要的data structure是Set. 英文解释是It is an unordered collection of items without duplicates. 个人理解即为使用它后,会返回一个无序的不重复的集合。
(set ["^^" "^^" "^__*__^"]) ;=> #{"^__*__^" "^^"}
(set [1 2 3 1 1 1 3 3 2 1]) ;=> #{1 2 3}
The textual form of a set is #{an-elem another-elem ...}
and you can convert another collection into a set with the function set
.
可以使用contains?
查找set中有没有一个元素:
(def games #{"Portal", "Planescape: Torment",
"Machinarium", "Alpha Protocol"})
(contains? games "Portal") ;=> true
(contains? games "RAGE") ;=> false
(contains? games 42) ;=> false
(conj set elem)
adds elem to set if it does not already have elem;
(disj set elem)
removes elem from set if it contains elem:
(conj #{:a :b :c} :EEEEE) ;=> #{:a :c :b :EEEEE}
//如果有了那元素则不加入
(conj #{:a :b :c} :a) ;=> #{:a :c :b}
//也可以加入多个元素
(conj #{:a :b :c} :d :e) ;=> #{:a :c :b :d :e}
//删除元素
(disj #{:a :b :c} :c) ;=> #{:a :b}
(disj #{:a :b :c} :EEEEE) ;=> #{:a :c :b}
(disj #{:a :b :c} :c :a) ;=> #{:b}
如果想知道一个set的大小,可以用count
.
(count #{1 2 3}) ;=> 3
(count (set [1 2])) ;=> 2
给段例程,all-author-names
是关于怎么查找出books里所有书籍的作者名字并列出:
(def china {:name "China Miéville", :birth-year 1972})
(def octavia {:name "Octavia E. Butler"
:birth-year 1947
:death-year 2006})
(def friedman {:name "Daniel Friedman" :birth-year 1944})
(def felleisen {:name "Matthias Felleisen"})
(def cities {:title "The City and the City" :authors [china]})
(def wild-seed {:title "Wild Seed", :authors [octavia]})
(def embassytown {:title "Embassytown", :authors [china]})
(def little-schemer {:title "The Little Schemer"
:authors [friedman, felleisen]})
(def books [cities, wild-seed, embassytown, little-schemer])
(defn all-author-names [books]
(let [author-names
(fn [book] (map :name (:authors book)))]
(set (apply concat (map author-names books)))))
关于all-author-names的注释:
-
fn
to introduce a helper function, - keywords to index the books,
- 第一个map to get all authors from a single book
-
let
to give a name to our helper function, - 第二个map to apply the helper function to all the given books, and
- construct a
set
with the set function to get rid of duplicates.
结果如下:
(all-author-names books)
;=> #{"Matthias Felleisen" "China Miéville"
; "Octavia E. Butler" "Daniel Friedman"}
网友评论