我正在玩并尝试创建自己的简化实现,到目前为止,我已经使用了这个测试数据:
((fn [func & args]
(reduce (fn [acc item]
(conj acc (func (last acc) item))
)[(first args)] (first (rest args)))) * 2 [3 4 5]
我不喜欢的是我如何分离args。
(first args)
是我所期望的,即2,但(rest args)
是([3 4 5])
,所以我得到了余下的(first (rest args))
,我不喜欢。
我是否遗漏了一些技巧,可以更容易地使用可变参数?
答案 0 :(得分:3)
Variadic参数只是在列表中获取未指定数量的参数,因此可以在此处应用所有列表/解构操作。
例如:
(let [[fst & rst] a-list]
; fst is the first element
; rst is the rest
)
这比以下内容更具可读性:
(let [fst (first a-list)
rst (rest a-list)]
; ...
)
您可以在一行中进一步获取列表的第一个和第二个元素(假设它有> 1个元素):
(let [fst snd & rst]
; ...
)
我最初误读了您的问题,并认为您正在尝试重新实现reduce
功能。以下是我为此答案编写的示例实现,它不使用first
或rest
:
(defn myreduce
;; here we accept the form with no initial value
;; like in (myreduce * [2 3 4 5]), which is equivalent
;; to (myreduce * 2 [3 4 5]). Notice how we use destructuring
;; to get the first/rest of the list passed as a second
;; argument
([op [fst & rst]] (myreduce op fst rst))
;; we take an operator (function), accumulator and list of elements
([op acc els]
;; no elements? give the accumulator back
(if (empty? els)
acc
;; all the function's logic is in here
;; we're destructuring els to get its first (el) and rest (els)
(let [[el & els] els]
;; then apply again the function on the same operator,
;; using (op acc el) as the new accumulator, and the
;; rest of the previous elements list as the new
;; elements list
(recur op (op acc el) els)))))
我希望它可以帮助您了解如何使用列表解构,这可能是您在函数中想要的。这是关于此主题的relevant blog post。
答案 1 :(得分:1)
整理你的功能。
如@bfontaine所述,您可以使用(second args)
代替(first (rest args))
:
(defn reductions [func & args]
(reduce
(fn [acc item] (conj acc (func (last acc) item)))
[(first args)]
(second args)))
这使用
func
(first args)
(second args)
...但忽略args
的其余部分。
因此我们可以使用解构来命名args
的第一和第二个元素 - init
和coll
似乎合适 - 给予
(defn reductions [func & [init coll & _]]
(reduce
(fn [acc item] (conj acc (func (last acc) item)))
[init]
coll))
...其中_
是被忽略参数的常规名称,在本例中是一个序列。
我们可以摆脱它,简化为
(defn reductions [func & [init coll]] ... )
...然后到
(defn reductions [func init coll] ... )
... - 三个参数的直接功能。
处理潜在问题。
您的功能有两个问题:
慢度
此功能中闪烁的红灯是在
中使用last
(fn [acc item] (conj acc (func (last acc) item)))
即使acc
是向量,每次调用时都会扫描整个acc
。因此,此reductions
需要的时间与coll
长度的平方成正比:对于长序列来说,它的速度很慢。
一个简单的解决方法是将(last acc)
替换为(acc (dec (count acc)))
,这需要有效的恒定时间。
缺乏懒惰
我们仍然不能懒惰地使用该功能产生的东西。例如,将这样的因子序列封装起来会很好:
(def factorials (reductions * 1N (next (range)))))
使用reductions
,此定义永不返回。
你必须完全重塑你的功能才能使它变得懒惰。让我们修改标准-lazy - reductions
来使用解构:
(defn reductions [f init coll]
(cons
init
(lazy-seq
(when-let [[x & xs] (seq coll)]
(reductions f (f init x) xs)))))
现在我们可以定义
(def factorials (reductions * 1N (next (range))))
然后,例如,
(take 10 factorials)
;(1N 1N 2N 6N 24N 120N 720N 5040N 40320N 362880N)
另一种方法是从自身推导出序列,就像铁路机车铺设它所经过的轨道一样:
(defn reductions [f init coll]
(let [answer (lazy-seq (reductions f init coll))]
(cons init (map f answer coll))))
但这包含一个隐藏的递归(至少对我隐藏):
(nth (reductions * 1N (next (range))) 10000)
;StackOverflowError ...