jiacai2050 / ideas

Think more
https://github.com/jiacai2050/ideas/issues
29 stars 2 forks source link

Clojure cheatsheet #38

Open jiacai2050 opened 8 years ago

jiacai2050 commented 8 years ago
$ java -cp ~/bin/clojure-1.8.0.jar:. clojure.main <src>.clj

main.java

/**
 *   Copyright (c) Rich Hickey. All rights reserved.
 *   The use and distribution terms for this software are covered by the
 *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 *   which can be found in the file epl-v10.html at the root of this distribution.
 *   By using this software in any fashion, you are agreeing to be bound by
 *   the terms of this license.
 *   You must not remove this notice, or any other, from this software.
 **/

package clojure;

import clojure.lang.Symbol;
import clojure.lang.Var;
import clojure.lang.RT;

public class main{

final static private Symbol CLOJURE_MAIN = Symbol.intern("clojure.main");
final static private Var REQUIRE = RT.var("clojure.core", "require");
final static private Var LEGACY_REPL = RT.var("clojure.main", "legacy-repl");
final static private Var LEGACY_SCRIPT = RT.var("clojure.main", "legacy-script");
final static private Var MAIN = RT.var("clojure.main", "main");

public static void legacy_repl(String[] args) {
    REQUIRE.invoke(CLOJURE_MAIN);
    LEGACY_REPL.invoke(RT.seq(args));
}

public static void legacy_script(String[] args) {
    REQUIRE.invoke(CLOJURE_MAIN);
    LEGACY_SCRIPT.invoke(RT.seq(args));
}

public static void main(String[] args) {
    REQUIRE.invoke(CLOJURE_MAIN);
    MAIN.applyTo(RT.seq(args));
}
}
jiacai2050 commented 8 years ago
jiacai2050 commented 8 years ago

Why clojure is bad when scaling both in code lines and people

jiacai2050 commented 8 years ago

From a high-level perspective, namespaces can be likened to a two-level mapping, where the first level is a symbol to a namespace containing mappings of symbols to Vars.

ns 命名空间

ns 宏可以包含以下几种指令:

require

(ns megacorp.profitd.scheduling
  (:require [clojure.set  :as cs]
            [clojure.walk :as walk]))

(ns megacorp.profitd.scheduling
  (:require [clojure.set :refer [difference intersection]]))

;; Now it is possible to do:
;; (difference #{1 2 3} #{3 4 5})

import

(ns megacorp.profitd.scheduling
  (:import java.util.concurrent.Executors
           java.util.concurrent.TimeUnit
           java.util.Date))

(ns megacorp.profitd.scheduling
  (:import [java.util.concurrent Executors TimeUnit]
           java.util.Date))

refer-clojure

(ns megacorp.profitd.scheduling
  (:refer-clojure :exclude [find]))

use

(ns megacorp.profitd.scheduling-test
  (:use clojure.test :only [deftest testing is]))

这相当于 Clojure 1.4 之前的

(ns megacorp.profitd.scheduling-test
  (:require clojure.test :refer [deftest testing is]))

现在比较推荐refer的方式

参考

jiacai2050 commented 8 years ago

How are keywords different from symbols?

Keywords always refer to themselves. What this means is that the keyword :magma always has the value :magma, whereas the symbol ruins may refer to any legal Clojure value or reference.

In a nutshell, symbols are primarily used to provide a name for a given value. But in Clojure, symbols can also be referred to directly, by using the symbol or quote function or the ' special operator. Symbols tend to be discrete entities from one lexical contour to another, and often even within a single contour. Unlike keywords, symbols aren’t unique based solely on name alone, as you can see in the following:

(identical? 'goat 'goat)
;=> false

Symbols differ from strings in that you can test equality by comparing a pointer.

http://www.paulgraham.com/diff.html

jiacai2050 commented 8 years ago

vector

(def a-to-j (vec (map char (range 65 75))))
a-to-j
;=> [\A \B \C \D \E \F \G \H \I \J]

(nth a-to-j 4)
(get a-to-j 4)
(a-to-j 4)

All three of these do the same work and each returns \E: snip20160626_8

(assoc a-to-j 4 "no longer E")
;=> [\A \B \C \D "no longer E" \F \G \H \I \J]

(replace {2 :a, 4 :b} [1 2 3 2 3 4])
;=> [1 :a 3 :a 3 :b]

(def matrix
             [[1 2 3]
              [4 5 6]
              [7 8 9]])

(get-in matrix [1 2])
;=> 6
(assoc-in matrix [1 2] 'x)
;=> [[1 2 3] [4 5 x] [7 8 9]]

Vectors as stacks

(def my-stack [1 2 3])
(peek my-stack)
;=> 3
(pop my-stack)
;=> [1 2]
(conj my-stack 4)
;=> [1 2 3 4]
(+ (peek my-stack) (peek (pop my-stack)))
;=> 5

Using vectors instead of reverse

(defn strict-map1 [f coll]
  (loop [coll coll, acc nil]
    (if (empty? coll)
      (reverse acc)
      (recur (next coll) (cons (f (first coll)) acc)))))
(strict-map1 - (range 5))
;=> (0 -1 -2 -3 -4)

(defn strict-map2 [f coll]
  (loop [coll coll, acc []]
    (if (empty? coll)
      acc
      (recur (next coll) (conj acc (f (first coll)))))))
(strict-map2 - (range 5))
;=> [0 -1 -2 -3 -4]

What vectors aren’t

jiacai2050 commented 8 years ago

Lists: Clojure’s code form data structure

(cons 1 '(2 3))
;=> (1 2 3)
(conj '(2 3) 1)
;=> (1 2 3)
jiacai2050 commented 8 years ago

How to use persistent queues

(defmethod print-method clojure.lang.PersistentQueue
  [q, w]
  (print-method '<- w) (print-method (seq q) w) (print-method '-< w))

(def schedule
  (conj clojure.lang.PersistentQueue/EMPTY
        :wake-up :shower :brush-teeth))

<-(:wake-up :shower :brush-teeth)-<

snip20160626_9

jiacai2050 commented 8 years ago

Named arguments in Clojure functions

(defn slope
  [& {:keys [p1 p2] :or {p1 [0 0] p2 [1 1]}}]
  (float (/ (- (p2 1) (p1 1))
            (- (p2 0) (p1 0)))))

(slope :p1 [4 15] :p2 [3 21])
;=> -6.0

(slope :p2 [2 1])
;=> 0.5

(slope)
;=> 1.0
jiacai2050 commented 8 years ago

Clojure aphorism

Clojure is a design language where the conceptual model is also Clojure.

jiacai2050 commented 8 years ago

Macro Power

(ns learn-by-doing.contract)

(declare collect-bodies)
(defmacro contract
  [name & forms]
  ;; `(fn ~name (collect-bodies ~forms))
  (list* `fn name (collect-bodies forms))
)

(declare build-contract)

(defn collect-bodies [forms]
  (for [p (partition 3 forms)]
    (build-contract p)))

(defn build-contract [form]
  (let [args (first form)]
    (list
     (into ['f] args)
     (apply merge
            (for [con (rest form)]
              (cond
                (= (first con) :require) (assoc {} :pre (vec (rest con)))
                (= (first con) :ensure) (assoc {} :post (vec (rest con)))
                :else (throw (Exception. (str "Unknown tag" (first con)))))))
     (list* 'f args)
     ;; (lazy-seq (into ['f] args))
     )))

(def double-contract
  (contract doubler
            [x]
            (:require (pos? x))
            (:ensure (= (* x 2) %))))
;; (fn doubler
;;   ([f x]
;;    {:post [(= (* 2 x) %)],
;;     :pre [(pos? x)]}
;;    (f x)))

(def times2 (partial double-contract #(* 2 %)))
(times2 2)

;; (def times3 (partial double-contract #(* 3 %)))

;; (times3 10)

(def doubler-contract
  (contract doubler
            [x]
            (:require (pos? x))
            (:ensure (= (* x 2) %))
            [x y]
            (:require (pos? x) (pos? y))
            (:ensure (= (* 2 (+ x y)) %))))

(doubler-contract #(* % 2) 4)
(doubler-contract #(* 2 (+ %1 %2)) 4 3)