我正在读一本关于 Clojure 的书,上面写着:
您可以使用映射做的另一件有趣的事情是向它传递函数集合。如果您想对不同的数字集合执行一组计算,则可以使用它,如下所示:
Run Code Online (Sandbox Code Playgroud)(def sum #(reduce + %)) (def avg #(/ (sum %) (count %))) (defn stats [numbers] (map #(% numbers) [sum count avg])) (stats [3 4 10]) ; => (17 3 17/3) (stats [80 1 44 13 6]) ; => (144 5 144/5)在此示例中,stats 函数迭代函数向量,将每个函数应用于数字。
我觉得这很令人困惑,本书没有给出更多解释。
我知道%代表匿名函数中的参数,但我无法弄清楚它们在这个示例中代表什么值。是什么%?
还有如何stats迭代countifcount嵌套在其中avg?
它有助于不要考虑“正在执行的代码”,而是考虑“正在减少的表达式树”。表达式树被重写,直到结果出现。当“实时函数”出现在列表的第一个位置时,符号将被替换为“它们代表什么”,并且函数将应用于其参数;如(some-function a b c). 这是从表达式树的顶部到叶子以自上而下的方式完成的,当quote遇到符号时停止。
在下面的示例中,遗憾的是,我们无法标记已减少的内容和未减少的内容,因为不支持着色。请注意,缩减顺序不一定与 Clojure 编译器发出的编译代码实际执行的操作相对应。
\n\n从...开始:
\n\n(defn stats\n [numbers]\n (map #(% numbers) [sum count avg]))\nRun Code Online (Sandbox Code Playgroud)\n\n...我们会打电话stats。
第一个困难是stats可以将集合作为单个事物来调用:
(stats [a0 a1 a2 ... an])\nRun Code Online (Sandbox Code Playgroud)\n\n或者可以使用一系列值来调用它:
\n\n(stats a0 a1 a2 ... an)\nRun Code Online (Sandbox Code Playgroud)\n\n是哪一个?不幸的是,预期的调用风格只能通过查看函数定义来找到。在这种情况下,定义说
\n\n(defn stats [numbers] ...\nRun Code Online (Sandbox Code Playgroud)\n\n这意味着stats需要一个名为numbers. 因此我们这样称呼它:
(stats [3 4 10])\nRun Code Online (Sandbox Code Playgroud)\n\n现在减价开始了!作为参数的数字向量被归约到其自身,因为向量的每个元素都被归约并且数字被归约到其自身。该符号stats被简化为之前声明的函数。的定义stats实际上是:
(fn [numbers] (map #(% numbers) [sum count avg]))\nRun Code Online (Sandbox Code Playgroud)\n\n...这有点被隐藏了defn简写隐藏了。因此
(stats [3 4 10])\nRun Code Online (Sandbox Code Playgroud)\n\n变成
\n\n((fn [numbers] (map #(% numbers) [sum count avg])) [3 4 10])\nRun Code Online (Sandbox Code Playgroud)\n\n接下来,减少fn表达式会产生一个参数的实时函数。让我们用 \xe2\x98\x85 标记实时函数,并使用数学箭头表示法:
(\xe2\x98\x85(numbers \xe2\x9e\x9c (map #(% numbers) [sum count avg])) [3 4 10])\nRun Code Online (Sandbox Code Playgroud)\n\n实时函数位于列表的第一个位置,因此接下来将进行函数调用。函数调用包括numbers用参数替换出现的[3 4 10],并去掉整个表达式的外括号:
(map #(% [3 4 10]) [sum count avg])\nRun Code Online (Sandbox Code Playgroud)\n\n符号map, sum, count,avg解析为已知的已定义函数,其中map和count来自 Clojure 核心库,其余部分已在之前定义。再次,我们将它们标记为活动状态:
(\xe2\x98\x85map #(% [3 4 10]) [\xe2\x98\x85sum \xe2\x98\x85count \xe2\x98\x85avg]))\nRun Code Online (Sandbox Code Playgroud)\n\n同样,该# %符号是函数接受一个参数并将其插入到%位置的简写,让我们明确这一点:
(\xe2\x98\x85map (fn [x] (x [3 4 10])) [\xe2\x98\x85sum \xe2\x98\x85count \xe2\x98\x85avg]))\nRun Code Online (Sandbox Code Playgroud)\n\n减少fn表达式会产生一个参数的实时函数。再次,用 \xe2\x98\x85 进行标记,并使用数学箭头表示法:
(\xe2\x98\x85map \xe2\x98\x85(x \xe2\x9e\x9c (x [3 4 10])) [\xe2\x98\x85sum \xe2\x98\x85count \xe2\x98\x85avg]))\nRun Code Online (Sandbox Code Playgroud)\n\n实时函数\xe2\x98\x85map位于头部位置,因此整个表达式根据以下规范进行简化map:将第一个参数(函数)应用于第二个参数(集合)的每个元素。我们可以假设首先创建集合,然后进一步评估集合成员,因此:
[(\xe2\x98\x85(x \xe2\x9e\x9c (x [3 4 10])) \xe2\x98\x85sum)\n (\xe2\x98\x85(x \xe2\x9e\x9c (x [3 4 10])) \xe2\x98\x85count)\n (\xe2\x98\x85(x \xe2\x9e\x9c (x [3 4 10])) \xe2\x98\x85avg)]\nRun Code Online (Sandbox Code Playgroud)\n\n集合中的每个元素都可以进一步减少,因为每个元素都有一个包含 1 个位于头部位置的参数和一个可用参数的实时函数。因此,在每种情况下,x都被适当地替换为:
[(\xe2\x98\x85sum [3 4 10])\n (\xe2\x98\x85count [3 4 10])\n (\xe2\x98\x85avg [3 4 10])]\nRun Code Online (Sandbox Code Playgroud)\n\n集合中的每个元素都可以进一步减少,因为每个元素在头位置都有一个包含 1 个参数的实时函数。练习继续:
\n\n[ ((fn [x] (reduce + x)) [3 4 10])\n (\xe2\x98\x85count [3 4 10])\n ((fn [x] (/ (sum x) (count x))) [3 4 10])]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ (\xe2\x98\x85(x \xe2\x9e\x9c (reduce + x)) [3 4 10])\n 3\n (\xe2\x98\x85(x \xe2\x9e\x9c (/ (sum x) (count x))) [3 4 10])]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ (reduce + [3 4 10])\n 3\n (/ ((fn [x] (reduce + x)) [3 4 10]) (count [3 4 10]))]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ (\xe2\x98\x85reduce \xe2\x98\x85+ [3 4 10])\n 3\n (/ (*(x \xe2\x9e\x9c (reduce + x)) [3 4 10]) (count [3 4 10]))]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ (\xe2\x98\x85+ (\xe2\x98\x85+ 3 4) 10)\n 3\n (/ (reduce + [3 4 10]) (count [3 4 10]))]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ (\xe2\x98\x85+ 7 10)\n 3\n (\xe2\x98\x85/ (\xe2\x98\x85reduce \xe2\x98\x85+ [3 4 10]) (\xe2\x98\x85count [3 4 10]))]\nRun Code Online (Sandbox Code Playgroud)\n\n然后
\n\n[ 17\n 3\n (\xe2\x98\x85/ 17 3)]\nRun Code Online (Sandbox Code Playgroud)\n\n最后
\n\n[ 17\n 3\n 17/3]\nRun Code Online (Sandbox Code Playgroud)\n\n您还可以使用函数juxt。尝试(doc juxt)REPL:
\n\n\nRun Code Online (Sandbox Code Playgroud)\nclojure.core/juxt\n([f] [f g] [f g h] [f g h & fs])\n Takes a set of functions and returns a fn that is the juxtaposition\n of those fns. The returned fn takes a variable number of args, and\n returns a vector containing the result of applying each fn to the\n args (left-to-right).\n ((juxt a b c) x) => [(a x) (b x) (c x)]\n
让我们试试吧!
\n\n(def sum #(reduce + %))\n(def avg #(/ (sum %) (count %)))\n((juxt sum count avg) [3 4 10])\n;=> [17 3 17/3]\n((juxt sum count avg) [80 1 44 13 6])\n;=> [144 5 144/5]\nRun Code Online (Sandbox Code Playgroud)\n\n因此我们可以定义stats为
(defn stats [numbers] ((juxt sum count avg) numbers))\n(stats [3 4 10])\n;=> [17 3 17/3]\n(stats [80 1 44 13 6])\n;=> [144 5 144/5]\nRun Code Online (Sandbox Code Playgroud)\n\n聚苯乙烯
\n\n有时 Clojure 代码很难阅读,因为您不知道正在处理什么“东西”。标量、集合或函数没有特殊的语法标记,实际上集合可以显示为函数,或者标量可以是集合。与 Perl 相比,Perl 有表示法$scalar, @collection, %hashmap,function但也$reference-to-stuff有 and$$scalarly-dereferenced-stuff和@$collectionly-dereferenced-stuffand %$hashmapply-dereferenced-stuff)。
| 归档时间: |
|
| 查看次数: |
745 次 |
| 最近记录: |