我正在研究this koan的解决方案,其中指出:
Write a function which replicates each element of a sequence a variable number of times.
要做到这一点,我想:
考虑到这一点,我写了以下方法:
(fn dupSeqX [aseq x]
(fn dupx [v x]
(if (= x 1) (list v) (concat v (dupx v (- x 1))))
)
(reverse (reduce #(concat %1 (dupx %2 x)) '() aseq)))
运行此代码时,出现以下错误:
java.security.PrivilegedActionException: java.lang.Exception: Unable to resolve symbol: dupx in this context (NO_SOURCE_FILE:0)
如何创建一个允许我完成此公案的本地方法?
有没有“clojure-esque”这样做的方式,我不知道?
答案 0 :(得分:8)
首先,我们不讨论方法:我们谈论功能。在clojure中你可以调用一个方法,但它与一个函数不同。如果你停止使用OO术语,你也会失去OO的思维方式。
你想做的事情是可能的。您基本上希望在dupx
函数中创建名为dupseqx
的新函数。你现在正在做的是创建一个函数,然后抛弃它(你没有对返回值做任何事情,只返回函数中的最后一个表单)。由于函数与任何其他值一样,您可以使用与任何其他值相同的机制:创建本地“变量”。这个机制是什么?它是本地绑定,它的工作原理如下(fn中的名称只能让你自己调用它;它不需要与let
绑定名称相同):
(let [dupx (fn dupx [v x]
(if (= x 1)
(list v)
(cons v (dupx v (dec x)))))]
(dupx 5 3))
请注意,我更正了其他一些事情。
这是一个较短的形式(修复双重名称丑陋):
(letfn [(dupx [v x] (if (= x 1)
(list v)
(cons v (dupx v (dec x)))))]
(dupx 5 3))
在“(让[...]”和匹配的“)”之间的所有内容都可以。我们现在有一个dupx
函数。
所以现在你的其余代码可以工作了:
(fn dupSeqX [aseq x]
(letfn [(dupx [v x] (if (= x 1) (list v) (cons v (dupx v (dec x)))))]
(reverse (reduce #(concat %1 (dupx %2 x)) '() aseq))))
这段代码可以更加惯用:
coll
,而不是aseq
我是怎么写这个的?
首先是基本的fn。 coll
是期望序列的命名函数的标准。
(fn [coll times] )
如果你读到这个“序列的每个元素”,你的大脑必须去MAP。
(fn [coll times]
(map (fn ....) coll))
“复制每个......”基本上是对你必须放入地图功能的描述。我们可以使用repeat
(你的dubx函数,但有一些额外的东西,比如它很懒)。
(fn [coll times]
(map (fn [val] (repeat times val)) coll))
还有一个问题(来自公案)。它想要一个seq,而不是每个元素的序列中的序列。这意味着我们必须将结果连在一起。
(fn [coll times]
(apply concat (map (fn [val] (repeat times val)) coll)))
您经常会看到(apply concat (map ....))
模式。在标准库中有一个更好的函数,称为mapcat
,我将内部函数转换为短语法。
(fn [coll times]
(mapcat #(repeat times %) coll))
希望有所帮助!
答案 1 :(得分:2)
Clojure有很多或非常好的功能可以帮助你进入“在seqs中思考”的习惯。当你发现自己写的东西在列表中迭代时会想到“我可以映射这个吗?”,当你发现自己在列表中做了其他事情时,请查看list of useful seq functions。
在这种情况下,内部函数恰好是内置函数,所以你很幸运:)通常你需要自己写它并将它存储在let
中,但是如果你使它成为一个合适的函数那么你可以在代码的其他地方找到它的用法。
这是一个开始的暗示
(flatten (map #(repeat 3 %) [1 2 3 4]))
(1 1 1 2 2 2 3 3 3 4 4 4)
ps:#(repeat 3 %)
是(fn [n] (repeat 3 n))