Clojure 中声明的解释?

An explanation of a statement in Clojure?

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

有些人写了这样的声明:

(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)) "translates to":

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

Clojure 中是否有解释语句的函数?

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

Clojure documentation 可能会有帮助。

尽管有几个工具可能会有所帮助,但没有任何函数可以在所有情况下打印中间值。

  • 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))))

那第二个 link 是在谈论 reduce 而不是 map 所以解释不适用。 Map 获取一个序列并通过实质上循环遍历它来构建一个新序列,调用您传递给元素的函数,然后将结果添加到它正在构建的列表中。它遍历列表,直到每个元素都被转换并包含在内。
link 所指的 reduce 取一个初始值,并通过使用它和序列中的第一个值调用函数来重复更改该值,然后循环并使用更新后的值调用函数,列表中的第二项,然后是第三项,依此类推,直到列表中的每一项都已用于更改值,然后它 returns。

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

你甚至可以把高阶函数想象成一个小程序。它需要一个参数(一个函数)来说明如何处理数据输入(参数)。每次传入的函数应用于参数时,它都会生成一些新值(可能为 nil)。高阶函数获取该结果并对其进行处理。在 map 的情况下,它将它添加到一个新序列中,这个新序列将被 returned 作为整体结果。

考虑一个高阶排序函数,我们称它为'sort'。它的第一个参数是用于比较元素以确定哪个在排序顺序中排在第一位的函数,其余参数是要排序的事物的列表。

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

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

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

Clojure 提供了许多基本的脚手架函数,它们是高阶函数。其中最基本的是地图。 map 函数非常简单——它接受一个函数和一个或多个序列。它遍历序列,从每个序列中取出一个元素并将它们传递给作为其第一个参数提供的函数。然后它将每次调用此参数的结果放入一个新序列中,该序列被 returned 作为最终结果。这有点简化,因为 map 函数可以接受多个集合。当它这样做时,它从每个集合中获取一个元素,并期望作为第一个参数传递的函数将接受与集合一样多的参数——但这只是相同原则的概括,所以让我们暂时忽略它.

由于 map 函数的执行没有改变,我们在试图了解发生了什么时不需要查看任何细节。我们需要做的就是查看作为第一个参数传入的函数。我们查看此函数并根据传入的参数查看它的作用,并知道调用 map 的结果将是一个新序列,其中包含将提供的函数应用于输入的所有值 returned数据,即传递给地图的集合。

如果我们看一下您提供的示例

(map inc [9 5 4 8])

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

clojure.core/inc ([x]) Returns a number one greater than num. Does not auto-promote longs, will throw on overflow. See also: inc'

我实际上认为这是一份措辞糟糕的文档。而不是说“return 比 num 大的数字,它可能应该说,Return 比 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 return 是一个惰性序列,即值在实现之前不存在的序列。

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

(inc 9)

当函数比 inc 复杂一点时,这会很有用。

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

例如,

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

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

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

并作为

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

所以 return 值将是 15。然而,reduce 实际上比那个小例子中显而易见的更强大。文档说明

clojure.core/reduce ([f coll] [f val coll]) Added in 1.0 f should be a function of 2 arguments. If val is not supplied, returns the result of applying f to the first 2 items in coll, then applying f to that result and the 3rd item, etc. If coll contains no items, f must accept no arguments as well, and reduce returns the result of calling f with no arguments. If coll has only 1 item, it is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called.

如果你仔细阅读,你会发现 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}
....