ClojureDocs

导航

命名空间

partial

clojure.core

自 1.0 起可用 (源代码)
  • (partial f)
  • (partial f arg1)
  • (partial f arg1 arg2)
  • (partial f arg1 arg2 arg3)
  • (partial f arg1 arg2 arg3 & more)
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
15 Examples
user=> (def to-english (partial clojure.pprint/cl-format nil "~@(~@[~R~]~^ ~A.~)"))
#'user/to-english

user=> (to-english 1234567890)
"One billion, two hundred thirty-four million, five hundred sixty-seven thousand, eight hundred ninety"
user=> (def hundred-times (partial * 100))
#'user/hundred-times

user=> (hundred-times 5)
500

user=> (hundred-times 4 5 6)
12000

user=> (def add-hundred (partial + 100))
#'user/add-hundred

user=> (add-hundred 5)
105
(def subtract-from-hundred (partial - 100))

user=> (subtract-from-hundred 10)      ; same as (- 100 10)
90

user=> (subtract-from-hundred 10 20)   ; same as (- 100 10 20)
70
; Maps exponent to coefficient
; x^3 + 2x + 1
(def poly (fn [n]
  (cond
    (= 0 n) 1
    (= 1 n) 2
    (= 3 n) 1
    :else 0)))

; Differentiates input by returning a polynomial that is curried
; 3x^2 + 2
(defn diff [p]
  (partial (fn [p n] (* (+ 1 n) (p (+ 1 n)))) p))

(poly 3)
;=> 1
((diff poly) 3)
;=> 0
((diff poly) 2)
;=> 3
user=> (defn fun-full [x y] (+ x y))
;=> #<function user$fun_full(x,y){ ...

user=> (fun-full 2 3)
;=> 5

user=> (def fun-half (partial fun-full 2))
;=> #<function (x,y,z,var_args){ ...

user=> (fun-half 3)
;=> 5
;;Takes a function f and the normal full arguments is allowed

user=> (defn add [x y] (+ x y))
#'user/add
user=> (partial add 1 1 )
#object[clojure.core$partial$fn__4529 0x5eb8fe04 "clojure.core$partial$fn__4529@5eb8fe04"]
user=> (apply (partial add 1 1 ) nil)
2
user=> ((partial add 1 1 ))
2
user=> ((partial add 1 1 1))
ArityException Wrong number of args (3) passed to: user/add  clojure.lang.AFn.throwArity (AFn.java:429)

user=>
user=> (def add1 (partial + 1))
#'user/add1
user=> (add1)
;=> 1
user=> (add1 2)
;=> 3
user=> (add1 2 3 4)
;=> 10
user=> (= (add1 2 3 4) (+ 1 2 3 4))
;=> true
(def times (partial *))

(times 1) ; -> 1

(times 1 2 3) ; -> 6

(* 1 2 3) ; -> 6


(def add-hundred (partial + 100))

(add-hundred 1) ; -> 101

(add-hundred 1 2 3) ; -> 106

(+ 100 1 2 3) ; -> 106
;; Check if a character is vowel

(def vowel? #(some (partial = %) "aiueo"))

(vowel? \e)
;;=> true

(vowel? \c)
;;=> nil
;; apply feeds sequence items as variable args to the conj function
;; variable args gets converted to list in the function arg and hence conj 
;; adds them as a list
(apply #(conj [0 1] %&) [2 3 4 5])
;;=> [0 1 (2 3 4 5)]

;; Partial offers are mechanism to feed the variable args as is to the conj 
;; function effectively  like (conj [] 2 3 4 5)
(apply (partial conj [0 1]) [2 3 4 5])
;;=> [0 1 2 3 4 5]
;;practical example

(def add-domain
  (partial #(str % "@clojure.com")))

(add-domain "info")
;;"info@clojure.com"
(defn email-struct
  [username domain]
  (str username "@" domain))

(def build-email
  #(partial email-struct %))

((build-email "info") "example.com")
;;"info@example.com"
;; partial does not gel well with pure java methods

;; Wrap Java method in Clojure fn
(defn letter? [ch]
  (Character/isLetter ch))

;; Idiomatic
(filter letter? "hello, world!")
;; => (\h \e \l \l \o \w \o \r \l \d)

;; This works
(filter (partial letter?) "hello, world!")
;; => (\h \e \l \l \o \w \o \r \l \d)

;; This also works
(filter #(Character/isLetter %) "hello, world!")
;; => (\h \e \l \l \o \w \o \r \l \d)

;; This doesn't
(filter (partial Character/isLetter) "hello, world!")
;; => Unable to find static field: isLetter in class java.lang.Character
;; Beware that partial "bakes in" the original function and does not look it up
;; again at call time, which can be confusing when mocking.

(defn foo [] "hit foo")
;; => #'user/foo
(defn mock [] "hit the mock")
;; => #'user/mock
(def par (partial foo))
;; => #'user/par
(with-redefs-fn {#'foo mock} #(foo))
;; => "hit the mock"
(with-redefs-fn {#'foo mock} #(par))
;; => "hit foo"
;; Partial application by calling partial significantly differs from partial
;; application by wrapping a function call in an anonymous function.

;; The code representing the to-be-applied function and to-be-applied-to arguments
;; - with the 'call to partial' approach
;;   - is evaluated exactly once, when the call to partial is evaluated.
;;   - is not evaluated when a call to the resulting function is evaluated.
;; - with the 'wrapped in an anonymous function' approach
;;   - is not evaluated when the anonymous function is evaluated.
;;   - is evaluated anew each time a call to the anonymous function is evaluated.

;; If there are side effects involved in obtaining the to-be-applied function or
;; to-be-applied-to arguments (because of with-redefs, this includes referring to a
;; var by providing a symbol), carefully consider which approach to take.

(defn fetch-processing-fn! [] (prn "fetching...") (fn [entity] entity))

(defn load-entity! [id] (prn "loading...") :entity)


(def top-level-fn-1 (partial (fetch-processing-fn!) (load-entity! 123)))
;; "fetching..." is printed.
;; "loading..." is printed.

(top-level-fn-1)
;; Nothing is printed.

(top-level-fn-1)
;; Nothing is printed.


(def top-level-fn-2 #((fetch-processing-fn!) (load-entity! 123)))
;; Nothing is printed.

(top-level-fn-2)
;; "fetching..." is printed.
;; "loading..." is printed.

(top-level-fn-2)
;; "fetching..." is printed.
;; "loading..." is printed.
See Also

Takes a set of functions and returns a fn that is the composition of those fns. The returned fn t...

Added by Ljos

Takes a set of functions and returns a fn that is the juxtaposition of those fns. The returned fn...

Added by Ljos

Applies fn f to the argument list formed by prepending intervening arguments to args.

Added by berczeck
1 Note
    By , created 11.4 years ago

    This function implements the concept of “currying�.