对Clojure声明的解释?

时间:2015-02-13 23:28:19

标签: clojure functional-programming higher-order-functions

我需要一些关于高阶函数定义或解释的帮助。

有些人写这样的声明:

(map inc [9 5 4 8]) ; statement

在此之后,对此声明作出解释:

 [(inc 9) (inc 5) (inc 4) (inc 8)] ; explanation.

或(this link)第一封邮件(reduce + (list 1 2 3 4 5)) “翻译为”:

(+ (+ (+ (+ 1 2) 3) 4) 5)

是否有一个函数可以解释Clojure中的语句?

3 个答案:

答案 0 :(得分:1)

理解高阶函数的技巧不是过度思考它们 - 它们实际上非常简单。高阶函数实际上只是一个函数,它将另一个函数作为其参数之一,并依次将该函数应用于每个参数,并对应用函数生成的结果执行某些操作。

您甚至可以将更高阶的功能视为迷你程序。它需要一个参数(一个函数)来说明如何处理数据输入(参数)。每次传入的函数应用于参数时,它都会生成一些新值(可能为零)。高阶函数接受该结果并对其执行某些操作。在map的情况下,它将它添加到一个新序列中,这个新序列将作为整体结果返回。

考虑更高阶的排序功能,让我们称之为排序'。它的第一个参数是用于比较元素的函数,以确定哪个元素在排序顺序中排在第一位,剩下的参数是要排序的元素列表。

实际的排序功能实际上只是脚手架或基本的排序引擎,它确保表示数据的列表在被排序之前被处理。它可以实现冒泡排序,快速排序或其他排序算法的基本机制。因为它是一个更高阶的函数,它不关心甚至不知道如何比较元素以确定正确的顺序。它依赖于作为第一个参数传递的函数来做到这一点。所有它想要的是该函数告诉它一个值是否比另一个更高,更低或相同。

传递给sort函数的函数是比较函数,它确定您的排序顺序。实际的排序功能不知道如何对数据进行排序。它将继续处理数据,直到满足某些标准,例如数据项的一次迭代,其中没有顺序发生变化。它期望比较函数采用两个参数并返回1,0或-1,具体取决于传递给它的第一个参数是大于,等于还是小于第二个参数。如果函数返回1或0则不执行任何操作,但如果值为-1,则交换A和B,然后使用新的B值和数据列表中的下一个值再次调用该函数。在第一次迭代输入后,它将有一个新的项目列表(相同的项目,但不同的顺序)。它可以遍历此过程,直到没有交换任何元素,然后返回最终列表,该列表将根据比较函数中指定的排序条件进行排序。

这个高阶函数的优点是你现在可以通过简单地定义一个新的比较函数来根据不同的排序标准对数据进行排序 - 它只需要取两个参数并返回1,0或-1。我们不必重写所有低级排序引擎。

Clojure提供了许多基本的脚手架功能,它们是更高阶的功能。其中最基本的是地图。 map函数非常简单 - 它需要一个函数和一个或多个序列。它遍历序列,从每个序列中取出一个元素并将它们传递给作为第一个参数提供的函数。然后它将每次调用的结果放入一个新的序列中,该序列作为最终结果返回。这有点简化,因为map函数可以使用多个集合。当它这样做时,它需要来自每个集合的一个元素,并期望作为第一个参数传递的函数将接受与集合一样多的参数 - 但这只是同一个主体的泛化,所以我们暂时忽略它

由于地图功能的执行没有改变,我们在尝试了解正在发生的事情时,不需要详细查看它。我们需要做的就是查看作为第一个参数传入的函数。我们看看这个函数,看看它根据传入的参数做了什么,并且知道调用map的结果将是一个新的序列,包括从提供的函数应用到输入数据返回的所有值,即传入的集合来图。

如果我们查看您提供的示例

(map inc [9 5 4 8])

我们知道地图的作用。它将传入函数(inc)应用于所提供集合中的每个元素([9 5 4 8])。我们知道它将返回一个新的集合。要知道它将做什么,我们需要查看传入的函数。公司的文件说

  

clojure.core / inc([x])返回大于num的数字。是否   不自动提升多头,会抛出溢出。另见:inc'

我实际上认为这是一个措辞严厉的文档。它不是说"返回大于num的数字,它可能应该说,返回一个大于x的数字,或者它应该将参数名称更改为([num]),但是yu得到了想法 - 它简单地将其参数增加1.

因此,map将依次将inc作为第二个参数传入集合中的每个项目,并以新的顺序收集结果。现在我们可以将其表示为[(inc 9)(inc 5)(inc 4)(iinc 8)],它是可以评估的clojure形式(表达式)的向量。 (inc 9)=> 10,(inc 5)=> 6等,这将导致[10 6 5 9]。它被表达为clojure形式的向量的原因是强调该映射返回一个惰性序列,即在它们被实现之前值不存在的序列。

这里要理解的关键点是,地图只是遍历您提供的序列,并将您提供的功能应用于序列中的每个项目,并将结果收集到新序列中。真正的工作是作为map的第一个参数传入的函数完成的。要了解实际发生的情况,您只需要查看正在应用的功能图。由于这只是一个正常的功能,你甚至可以单独运行它并提供一个测试值,即

(inc 9)

当函数比inc更复杂时,这可能很有用。

我们感觉很冷,因为地图几乎可以做我们需要的一切。但是,有一些常见的处理模式经常出现,我们可能希望将它们抽象为自己的函数,例如reduce和filter。我们可以在map方面实现这个功能,但是由于必须跟踪状态或效率较低可能会很复杂,因此它们被抽象到自己的函数中。但是,整体模式几乎相同。 Filter就像map一样,除了它生成的新序列只包含输入集合中的元素,它们满足作为第一个参数传入的谓词函数。 Reduce遵循相同的基本模式,传入的函数应用于集合中的元素以生成新的序列。最大的区别在于它减少了'序列以某种方式 - 通过将其减少为新值或新表示(例如hashmap或set或其他任何方式)。

例如,

(reduce + (list 1 2 3 4 5))

遵循将作为第一个参数提供的函数应用于作为第二个参数提供的集合中的每个项目的相同基本模式。 Reduce略有不同,因为提供的函数必须带两个参数,并且在第一个参数之后的每个调用中,传递给函数的第一个参数表示从上一次调用返回的值。上面的例子可以写成

(reduce + 0 (list 1 2 3 4 5))

并以

执行
(+ 0 1) => 1
(+ 1 2) => 3
(+ 3 3) => 6
(+ 6 4) => 10
(+ 10 5) => 15

所以返回值将是15.然而,实际上,减少实际上比那个小例子更明显。文档说明

  

clojure.core / reduce([f coll] [f val coll])在1.0 f中添加应该是   2个参数的函数。如果未提供val,则返回   将f应用于coll中的前2项,然后应用f   到那个结果和第3项等。如果coll不包含任何项目,f   必须也不接受任何参数,并且reduce返回结果   不带参数调用f。如果coll只有1个项目,那就是   返回并且未调用f。如果提供了val,则返回
  将f应用于val和coll中的第一项的结果,然后是   将f应用于该结果和第二项等。如果coll不包含
  items,返回val和f不会被调用。

如果仔细阅读,您会看到reduce可用于累积每次应用函数时结果。例如,您可以使用reduce生成包含集合中奇数和偶数之和的映射,例如

(defn odd-and-even [m y]
  (if (odd? y)
    {:odd (+ (:odd m) y)
     :even (:even m)}
    {:odd (:odd m)
     :even (+ (:even m) y)}))

现在我们可以像这样使用reduce

(reduce odd-and-even {:odd 0 :even 0} [1 2 3 4 5 6 7 8 9 10])

我们得到了结果

{:odd 25, :even 30}

执行就像这样

(odd-and-even {:odd 0 :even o} 1) -> {:odd 1 :even 0}
(odd-and-even {:odd 1 :even 0} 2) => {:odd 1 :even 2}
(odd-and-even {:odd 1 :even 2} 3) => {:odd 4 :even 2}
(odd-and-even {:odd 4 :even 2) 4) => {:odd 4 :even 6}
....

答案 1 :(得分:0)

map将函数(在本例中为inc)应用于列表并返回结果。所以它返回一个新的列表,每个值增加一个。

Clojure documentation可能会有所帮助。

答案 2 :(得分:0)

虽然有一些工具可能有所帮助,但没有任何功能可以在所有情况下打印中间值。

  • tools.trace帮助打印中间值,例如函数调用: => (deftrace fubar [x v] (+ x v)) ;; To trace a function call and its return value => (fubar 2 3) TRACE t1107: (fubar 2 3) TRACE t1107: => 5 5
  • macroexpand-1将显示由宏生成的代码,例如 - >并且在学习如何编写宏方面至关重要: => (macroexpand-1 '(-> 42 inc inc dec inc)) => (inc (dec (inc (inc 42))))

第二个链接是在讨论reduce而不是map,因此解释并不适用。 Map接受一个序列并通过循环遍历它来构建一个新序列,调用您传递给元素的函数,然后将结果添加到它构建的列表中。它在列表中向下迭代,直到每个元素都被转换并包含在内 该链接所指的reduce采用初始值并通过使用它调用函数和序列中的第一个值来重复更改该值,然后循环并使用更新的值和第二个项调用函数列表,然后是第三个,依此类推,直到列表中的每个项目都用于更改值,然后返回该值。