天天看點

Practical Clojure - Functional Programming Techniques

此處主要描述這些fp技術特性, 在clojure中的實作 

function作為fp中最基本的元素, 是構成所有其他的基石, 是以是一類公民...

 什麼是first-class? it can be created on demand.   it can be stored in a data structure.  it can be passed as an argument to a function.  it can be returned as the value of a function.

there are two aspects to using first-class functions: 

consuming, as arguments and calling them 

functions that take other functions as arguments are extremely common. these are known as higher order functions.

producing, creating and returning them 

not only can functions take other functions as arguments, but they can construct them and return them as values. 

此特性相當強大, 後面描述的curry, comp, closure都是produce function的方法 

this is one of the main reasons lisp has historically been associated with artificial intelligence. 

it was thought that functions creating other functions would allow a machine to evolve and define its own behavior. 

although self-modifying programs never quite lived up to expectations, the ability to define functions on-the-fly is nevertheless extremely powerful and useful for many everyday programming 

tasks.

代碼即資料, 可以運作時修改代碼, 這也成為lisp主要被用于ai的主要原因, 滿足程式不停自我進化的需要. 

(defn rangechecker

  "returns a function that determines if a number is in a provided range."

  [min max]

  (fn [num]

    (and (<= num max)

         (<= min num))))

user=> (def myrange (rangechecker 5 10))

user=> (myrange 7)

true

user=> (myrange 11)

false

closure和clojure讀音一樣, 是以在clojure中很重要?

為什麼叫閉包? 

簡單的一句話, 儲存context的函數. context一般都是在function之外的資料 

因為引用的value, 其實在調用的時候, 本不應該存在的. 而實際不但存在且僅存在于function内部, 并且這個值還會伴随function的整個生命周期. 是不是很像這個value被close over在function内部... 

as might be gathered from its very name, closures are a central feature in clojure. but what, exactly, is a closure? and why do they matter so much? 

briefly stated, closures are first-class functions that contain values as well as code. 

these values arethose in scope at function declaration, preserved along with the function. 

whenever a function is declared, the values locally bound to symbols it references are stored along with it. 

they are “closed over” (hence the name) and maintained along with the function itself. this means that they are then available for the function’s entire lifespan and the function can be referred to as a closure.

the value of a closed-over value can’t change after the function is created, so it becomes in essence a constant for that function.

closures和oo的關系... 

one interesting property of closures is that due to their dual nature—both behavior and data—they can fulfill some roles that are assumed by objects in object-oriented languages. 

just as anonymous classes with one method are used to simulate first-class functions in java, closures can be viewed as an object with a single method.

例子,

(defn times-n [n]

  (let [x n]

    (fn [y] (* y x))))

(def times-four (times-n 4))

(times-four 10)

;=> 40

in clojure, any function can be curried using the partial function. 

partial takes a function as its first argument and any number of additional arguments. 

it returns a function that is similar to the provided function, but with fewer arguments; it uses the additional arguments to partial instead.

partial這個名字比較形象, 部分啥? 部分參數. 第一個參數是function, 後面的參數是需要fix的那部分參數

user=> (def times-pi (partial * 3.14159)) ;*本身需要2個參數, 用partial指定一個而生成新的pi函數

不用partial你直接這樣定義也是一樣的, 不過用partial更簡潔

(defn times-pi

  "multiplies a number by pi”

  [n]

  (* 3.14159 n))

comp takes any number of parameters: each parameter is a function. 

it returns a function that is the result of calling all of its argument functions, from right to left. starting with the rightmost, it calls the function and passes the result as the argument to the next function and so on.

user=> (def my-fn (comp – *))

user=> (my-fn 5 3) ;–(5 * 3)

(defn my-fn ;同樣comp隻是一種簡潔的表達方式, 也可以直接寫

  "returns –(x * y)”

  [x y]

  (- (* x y)))

comp看上去和curry沒啥關系, 但是comp的每個function都隻能有一個參數, 是以經常使用currying技術來減少參數 

because the functions passed to comp are required to take a single argument, it makes them particularly good candidates for using currying with partial.

user=> (def my-fn (comp (partial * 10) - *))

user=> (my-fn 5 3)

-150

before wrapping up this chapter, we’re going to take time to talk about a style of programming not necessarily prevalent in clojure, but moreso in the functional tradition: continuation-passing style. 

continuation-passing style (cps) is a hybrid between recursion and mutual recursion, but with its own set of idioms.

這在fp基礎裡面也講到, 但其實在clojure裡面并沒有被廣泛使用. 因為可能不太需要, 因為在haskell這種pure fp裡面, cps可以用于保證執行順序 

其實可以認為recursion是一種特殊的cps, 每次都continue到自身 

後面給的例子沒太明白...

本文章摘自部落格園,原文釋出日期:2013-02-07