The Joy of Clojure 笔记

The Joy of Clojure

跳转至: 导航、 搜索

目录

  • 1 基础
  • 2 Data types
  • 3 FP
  • 4 large-scale design
    • 4.1 Macros
    • 4.2 Combining data and code
    • 4.3 Java.next
    • 4.4 Mutation
  • 5 Tangential considerations

基础

  1. (for [x [:a :b], y (range 5) :when (odd? y)] [x y])
    1. for产生lazy seq
    2.  :when :while是guard's
  2. Simplicity, freedom to focus, empowerment, consistency, and clarity.
  3. LISP: atom, car, cdr, cond, cons, eq, quote; lambda, label
  4. SQL DSL???
    1. ~:unquote
  5. Code is data
  6. FP?:Haskell, ML, Factor, Unlambda, Ruby, or Qi.
  7. OOP:no clear distinction between state and identity(状态 vs. 身份)
    1. defprotocol(定义接口)、extend-type(扩展类型)
  8. 关键字::keyword
  9. 容器类型:
    1. Lists:(yankee hotel foxtrot)
      1. 第一个元素:A form is any Clojure object meant to be evaluated ...
    2. Vectors:[1 2 :a :b :c]
    3. Maps(这里的语法很诡异):{1 "one", 2 "two", 3 "three"}
    4. Sets:#{1 2 "three" :four 0x5}
  10. 函数调用
    1. (+ 1 2 3)
  11. 定义函数
    1. (fn mk-set [x y] #{x y})
      1. ((fn [x y] #{x y}) 1 2) --匿名函数?
        1. 允许arity重载:(fn ([x] #{x}) ([x y] #{x y}))
          1. 变长参数:((fn arity2+ [x y & z] [x y z]) 1 2 3 4) ;=> [1 2 (3 4)] &后的参数以list传递
  12. special form
    1. 匿名函数?:fn
    2. 命名函数:def defn
    3. 顺序执行:do
    4. 尾递归:recur
    5. 条件:if(when,and,or是宏)
    6. 循环:loop(作为recur的跳转目标)。。。恩,recur相当于C语言里的goto了
    7. let(局部变量与`locals`有什么区别?):immutable
      (def make-a-set (fn ([x] #{x}) ([x y] #{x y}))) 相当于JavaScript里的var f = function(){ ... }
  13. In-place functions with #() 这里的记号似乎有点混淆?
    (def make-a-list_ #(list %)) ;为什么不是defn?
    (def make-a-list3+ #(list %1 %2 %3 %&))
  14. Vars
    1. (def x 42) ;这是root binding(全局变量?)
    2. 测试:(.start (Thread. #(println "Answer: " x)))
  15. Locals, loops, and blocks
    1. (let [x 1 y 2] (println (+ x y)))
    2. ...
  16. Preventing things from happening: quoting
    1. (cons 1 [2 3]) ;立即评估
    2. => (quote (cons 1 [2 3])) ;推迟评估
    3. (cons 1 (quote (2 3))) ???(评估过程对于LISP表达式是递归的函数调用吗?难怪没法区分)=> (cons 1 '(2 3))
    4. Syntax-quote:`(1 2 3) ;用于动态构造函数代码?
      1. clojure.core/map
      2. `能够做自动名字空间解开:`(map even? [1 2 3]) ;重名的情况怎么办? ... "will use the current namespace"
    5. unquote ~
      1. `(+ 10 ~(* 3 2))
    6. unquote-splicing(FP里的评估、bound/unbound概念真是麻烦!)
      1. (let [x '(2 3)] `(1 ~@x))
    7. Auto unqualified symbol:`potion#
  17. Java Interop(Java互操作)
    1. 静态方法调用:(Math/sqrt 9)
    2. 类实例:(new java.util.HashMap {"foo" 42 "bar" 9 "baz" "quux"})
      或(Clojure习俗):(java.util.HashMap. {"foo" 42 "bar" 9 "baz" "quux"})
    3. (.x (java.awt.Point. 10 20)) ;这相当于把一个成员方法作用于一个对象实例(函数调用的风格)
    4. (let [origin (java.awt.Point. 0 0)] (set! (.x origin) 15) ) ;这里的set!是通过反射调用的吗
    5. 调用链风格:..(Clojure里更习惯用->、->>)
      (.. (java.util.Date.) toString (endsWith "2010"))
    6. doto宏
  18. macros
    1. reify、deftype:创建Java接口的实现
    2. ns:名字空间
      (ns joy.req (:require clojure.set)) ;暂时无法理解这么做的原因?
      (ns joy.use-ex (:use [clojure.string :only [capitalize]])) ;仅将capitalize 映射到当前名字空间。。。哦。。。
       :refer *
      (ns joy.java (:import [java.util HashMap] [java.util.concurrent.atomic AtomicLong]))
  19. 异常(暂略)
    1. 当发生异常时,(.printStackTrace *e)
  20. Truthiness
    1. []、()不同于nil
    2. 不要创建Boolean类型的对象!
    3. nil punning
    4. Destructuring(相当于Scala/Erlang里的模式匹配)
      1. e.g. (let [[f-name m-name l-name] guys-whole-name] (str l-name ", " f-name " " m-name))
      2. e.g. (let [[a b c & more] (range 10)] ... )
      3. 匹配Map:(let [{f-name :f-name, m-name :m-name, l-name :l-name} guys-name-map]
      4. associative destructuring???
        (let [{first-thing 0, last-thing 3} [1 2 3 4]] [first-thing last-thing])
  21. (for [x (range 2) y (range 2)] [x y]) ;这个2重循环的写法真怪异

Data types

  1. M后缀:任意精度。(let [imadeuapi 3.14159265358979323846264338327950288419716939937M] ...
  2. Promotion(自动的精度提升):如何检测‘溢出’的?(class (+ clueless 90000000000000000000))
  3. Rounding errors
  4. 表达式中有一个是double,结果就是double???(+ 0.1M 0.1M 0.1M 0.1 0.1M 0.1M 0.1M 0.1M 0.1M 0.1M)
  5. 使用有理数?
    1. BigDecimal用一个32位数表示小数点后的位数,。。。
    2. 浮点数不满足结合律于分配律(st!)
    3. (def b (rationalize -1.0e50))
  6. keywords vs. symbols
    1. (identical? 'goat 'goat) 与 (= 'goat 'goat)
      为什么不把相同的symbols存储到同一个'位置'?... metadata
      (let [x (with-meta 'goat {:ornery true}) y (with-meta 'goat {:ornery false})] ...
  7. Lisp-1:uses the same name resolution for function and value bindings,name-shadowing问题
    1. vs Lisp-2:依赖于context
    2. (defn best [f xs] (reduce #(if (f % %2) % %2) xs)) ~~~寒
  8. 正则表达式:#"an example pattern"
    1. 区别:(java.util.regex.Pattern/compile "\\d")等于#"\d"
    2. Regex flags:d i(不区分大小写) x m s u,例如:#"(?i)yo"
    3. (seq (.split #"," "one,two,three"))与(re-seq #"\w+" "one-two/three")
    4. !!!不要使用Matcher对象(re-matcher)
  9. Clojure’s sequence abstraction — all the persistent collections use it
  10. `persistent`:所有历史版本保留
    1. (def ds [:willie :barnabas :adam]) ==>修改:(def ds1 (replace {:barnabas :quentin} ds)) ;Scala风格
      变成可变的:(def ds ( into-array [:willie :barnabas :adam])) ==>修改:(aset ds 1 :quentin)
  11. 比较sequential, sequence, and seq:
    1. sequential:元素顺序不会reordering
    2. sequence:seq操作返回,可在上执行first、rest操作(这里的命名与Lisp不太一致)
    3. (= [1 2 3] '(1 2 3))返回true,因为=操作基于序列接口?
  12. Vectors:immutable and persistent(arrays可变)
    1. (let [my-vector [:a :b :c]] (into my-vector (range 10))) ;‘倒进’操作 -- 这些名字太难记了!
    2. 相对与Lists更有效率的地方:从右边添加/删除元素;按索引访问;反向遍历
    3. (def a-to-j (vec (map char (range 65 75)))):
      1. (nth a-to-j 4) ;vector空或index越界时返回nil
      2. (get a-to-j 4) ;index越界时抛出异常
      3. (a-to-j 4)  ;会抛出异常(这里用法类似于Pyhton/C++?)
    4. 多维索引访问:get-in assoc-in update-in
    5. Vectors as stacks:conj/pop(靠,这个命名。。。);clojure.lang.IPersistentStack
    6. 经典的Lisp总是要求list翻转操作,但idiomatic Clojure使用vector就不需要(-_-)
    7. Subvectors:subvec操作,参数意义:[start, end)
    8. 作为MapEntry使用(实际上就是C++ hashmap的pair<K,V>)
  13. Lists: Clojure’s code form data structure
    1. (cons 1 '(2 3))与(conj '(2 3) 1)
    2. Clojure has no “dotted pair.” If you don’t know what that is, don’t worry about it.(这话真操蛋)
    3. peek/pop粗糙地等于first/rest(靠,作者似乎对程序的操作语义极为关注。。。但是一般的使用很容易混淆。。。)
    4. contains? will always return false for a list. ;既然不支持set接口,干吗要定义这个操作?
  14. 队列
    1. not a workflow mechanism(java.util.concurrent.BlockingQueue)
    2. clojure.lang.PersistentQueue/EMPTY
    3. 内部实现:the front being a seq and the rear being a vector(类似于一个C++ rope?)
  15. Persistent sets
    1. (sorted-set :b :c :a)
    2. Sets are implemented as maps with the same element as the key and value,。。。
  16. Thinking in maps
    1. key,value不要求同一类型:(let [m {:a 1, 1 :b, [1 2 3] "4 5 6"}] ...
    2. (map,key)可以视为函数调用。。。
    3. (into {} [[:a 1] [:b 2]]) ;返回一个相同结构的map对象
    4. (apply hash-map [:a 1 :b 2]) ;=> {:a 1, :b 2}
    5. (sorted-map :thx 1138 :r2d 2) ;支持key的有序遍历?(干吗不定义tree数据结构呢)
    6. 按照插入顺序:(seq (array-map :a 1, :b 2, :c 3))
  17. It’s usually a bad idea to build your programs around concrete types, and always bad to build around undocumented behaviors.

FP

  1. Equality in the presence of mutability has no meaning.
    1. And if two objects aren’t equal forever, then they’re technically never equal (Baker 1993). 哈哈
  2. lazy-seq原则:
    1. 使用lazy-seq宏
    2. rest,而不是next(重启,不是迭代)//Using next causes a lazy seq to be one element less lazy(因为需要检查‘下一个’)
    3. 为产生rest,仅仅保存必须的状态(相当于保存了一个函数对象?),避免过早的realize
    4. ??(let [r (range 1e9)] [(first r) (last r)]) ;=> [0 999999999]
    5. map,reduce,filter
    6. delay,force宏:不要直接使用,容易出错。。。?
      1. (if-let [res :truthy-thing] (println res)) ;另外的when-let
    7. *A lazy, tail-recursive quicksort implementation //看起来可以用它来求max/min-N?
  3. (def fifth (comp first rest rest rest rest))
  4. (defn fnth [n]
    (apply comp
    (cons first
    (take (dec n) (repeat rest)))))
  5. partial isn't currying. ?
    1. Because Clojure allows functions of variable number of arguments, currying makes little sense
  6. Higher-order functions:以函数为参数、返回值
  7. pure functions:相同输入总是得到相同输出;没有副作用
    1. ... as constant, or referentially transparent:例,(defn manip-map [f ks m] (conj m (keys-apply f ks m)))
      keys-apply: (defn keys-apply [f ks m] (let [only (select-keys m ks)] (zipmap (keys only) (map f (vals only)))))
  8. 命名参数:(defn slope [& {:keys [p1 p2] :or {p1 [0 0] p2 [1 1]}}] ... ;(slope :p1 [4 15] :p2 [3 21])
  9.  :pre :post
  10. closure
    1. polymorphism
    2. compile-time VS run-time
      1. In current versions of Clojure, each function definition gets its own class.
  11. mundane recursion(显示的递归调用)
    1. 计数器+recur
    2. lazy-seq
    3. Scheme(Lambda Papers, Guy L. Steele and Gerald Sussman):actors被移除,仅保留functions => Generalized tail-call optimization
      任何A到B的尾调用将释放A下的locals ...
    4. JVM不提供lco机制,recur也只能处理自己调自己的特殊情形
      但至少Clojure为什么不在编译器层次把自己调自己的mundane recursion解析处理成recur呢?
  12. Tail position:
      1. fn, defn:(fn [args] expressions tail)
      2. loop:(loop [bindings] expressions tail)
    1. *...act as anonymous recursion points. Why recur indeed. (靠!)
  13. mutually recursive:trampoline??
  14. Continuation-passing style(CPS):1)必须保证bounded execution path;2)isn’t conducive to parallelization
    1. An accept function that decides when a computation should terminate
    2. A return continuation that’s used to wrap the return values
    3. A continuation function used to provide the next step in the computation
  15. e.g. A-* path-finding

large-scale design

Macros

  1. (-> 25 Math/sqrt int list):展开为(list (int (Math/sqrt 25)))
  2. (eval '(list 1 2))
  3. defmacro
    1. (defmacro resolution [] `x) => (macroexpand '(resolution)) ;Common LISP中需避免名字捕捉
  4. domain DSL structure ~ XML/JSON(都是树形结构)
  5. Anaphora(上下文的代词引用)
    1. Scala:Array(1, 2, 3, 4, 5).map(2 * _)
    2. Anaphora don’t nest, and as a result are generally not employed in Clojure.
    3. => if-let when-let ~'symbol
  6. selective name capturing
    1. proxy ~'this
    2. A hygienic macro is one that doesn’t cause name capturing at macro expansion time.(FUCK,学术气味太浓了吧?)
  7. 管理资源(try/catch/finally):with-open
    (defmacro with-resource [binding close-fn & body]
    `(let ~binding
    (try
    (do ~@body)
    (finally
    (~close-fn ~(binding 0))))))
  8. macros returning functions
  9. contract

Combining data and code

  1. ns:java.lang、clojure.core自动导入
  2. in-ns:不自动导入clojure.core
  3. create-ns
  4. ns-map
  5. Var mapping::exclude, :only, :as, :refer-clojure, :import, :use, :load, :require.
  6. Yegge’s UDP:beget, get, put, has?, forget
  7. Multimethods
    1. defmulti defmethod
    2. derive(继承关系?)
    3. prefer-method 解决多继承情况下的冲突?
    4. juxt
  8. Records
    1. (defrecord TreeNode [val l r])
  9. Protocols
    1. In fact, the first parameter to a protocol function corresponds to the target object ...
    2. Clojure-style mixins

Java.next

  1. proxy
    1. construct-proxy、get-proxy-class、init-proxy、update-proxy、
  2. gen-class
    1. (doto (StringBuilder. "abc") (.append (char-array [\x \y \z])))
    2. into-array ;type based on the first element of the sequence
    3. ... Instead, variadic methods expect an array as their final argument
  3. All Clojure functions implement...
    1. java.util.Comparator
    2. java.lang.Runnable
    3. java.util.concurrent.Callable
  4. Java里调用Clojure数据结构
    1. Clojure sequential collections conform to the immutable parts of the java.util.List
    2. java.util.List
    3. java.lang.Comparable
    4. java.util.RandomAccess
    5. java.util.Collection
    6. java.util.Set
  5. definterface
    1. (definterface ISliceable (slice [^int s ^int e]) (^int sliceCount []))
      (def dumb ( reify user.ISliceable (slice [_ s e] [:empty]) (sliceCount [_] 42)))
  6. defprotocol ?
    1. extend
  7. extend-type
  8. 为了调用checked Exception的Java方法,Clojure中的所有函数都throws Exception ... ?
  9. Runtime versus compile-time exceptions
    1. (defn explode [] (explode)) ==> (try (explode) (catch Exception e "Stack is blown"))
    2. In Java, catching exceptions at the level of Throwable is considered bad form
    3. The way to throw a compile-time exception is to make sure your throw doesn’t occur within a syntax-quoted form
  10. (defmacro -?> [& forms]
    `(try (-> ~@forms)
    (catch NullPointerException _# nil)))
  11. ... then bear in mind that it’s rare for Clojure core functions to throw exceptions

Mutation

  1. A tangled web of mutation means that any change to your code potentially occurs in the large.
  2. 4 major mutable references: Refs, Agents, Atoms, and Vars.
  3. parallelism support: futures, promises, and a trio of functions pmap, pvalues, and pcalls
  4. STM with MVCC & snapshot isolation
    1. Time, State, Identity
    2. Clojure has but one transaction per thread, ...
      1. when a restart occurs in the (conceptual) subtransaction clojure.b, it causes a restart of the larger transaction.
    3. orphaned locks (locks held by a thread that has died)
    4. Any I/O operation in the body of a transaction is highly discouraged. ?事务不带外部磁盘IO怎么做?logging?
      1. (io! (.println System/out "Haikeeba!")) ;警告
      2. get in and get out as quickly as possible.
  5. When to use Refs
    1. ideal use:CoordinatedRetriable
      1. 其他:Agent:Asynchronous;Atom:Retriable;Var:Thread-local
    2. Coordinated: reads and writes to multiple refs can be made in a way that guarantees no race conditions
    3. Value access via the @ reader feature or the deref function
    4. 可绑定set-validator
    5. [脚注] Except for ref-set on Refs, reset! on Atoms, and set! on Vars. ???
    6. alter
    7. (stress-ref (ref 0 :min-history 15 :max-history 30))
  6. When to use Agents
    1. Each Agent has a queue to hold actions that need to be performed on its value
    2. 串行IO?(def log-agent (agent 0)) ...
    3. the current state of an Agent can be observed cheaply
    4. send
    5. send-off
    6. 错误处理::continue and :fail
      1. (restart-agent log-agent 2500 :clear-actions true)
  7. When to use Atoms
    1. Once an Atom’s value is set, it’s set, and it doesn’t roll back when a transaction is retried
    2. ... holding a function’s memoization cache is idempotent on update
    3. memoize
  8. When to use locks
  9. When to use futures(`one way to perform parallel computation`)
    1. (time (let [x (future (do (Thread/sleep 5000) (+ 41 1)))] [@x @x]))
  10. When to use promises
    1. placeholders for values whose construction is fulfilled by another thread via the deliver function
    2. Promises are write-once; any further attempt to deliver will throw an exception
    3. *A macro for transforming a callback-based function to a blocking call
    4. ?Deterministic deadlocks
  11. Parallelism
    1. as-futures与with-promises
    2. pvalues:it returns a lazy sequence of the results of all the enclosed expressions,例如:(pvalues 1 2 (+ 1 2))
    3. pmap
    4. pcalls
  12. Vars and dynamic binding
    1. binding宏
    2. def defn defmacro defonce defmulti defdynamic*
    3. (resolve 'x)、(bound? #'x)、(thread-bound? #'x)
    4. *with-local-vars、deref、var-get
    5. Dynamic scope(全局变量?)
    6. (with-precision 4
      ( doall (map (fn [x] (/ x 3)) (range 1M 4M)))) #强制map解析
    7. bound-fn ?

Tangential considerations

  1. Performance:
    1. Type hints
    2. Transients
    3. Chunked sequences
    4. Memoization
    5. Understanding coercion 

你可能感兴趣的:(jvm,scala,并发编程,mvcc,clojure)