Clojure的split-with
函数非常方便,但必须遍历seq的前导部分两次,因为它实际上是作为[(take-while pred coll) (drop-while pred coll)]
实现的。尽管如此,编写一个只遍历前导部分一次(将前导部分放入累积向量中,等等)的(尾部递归)版本还是相当容易的
然而,我想提取列表中满足谓词的第一个元素,并返回该元素和剩余列表(即(concat (take-while pred coll) (next (drop-while pred coll)))
)——希望在一次遍历中完成。如果我使用命令式语言,我只需遍历列表,抓住最后一个单元格,一旦我弹出元素,摆弄上一个单元格的"下一个指针"来重建修改后的列表,但这在函数式语言中似乎是不可能的。
那么,在Clojure中有没有一种方法可以有效地做到这一点呢?
对于split-with
(以及希望从一个输入产生两个输出的类似任务),您可以使用任意两个
- 懒惰
- 不可变性
- 完美的效率
例如,如果您不希望(第一个"丢弃"部分的)懒惰,您可以按照建议通过实现尾部递归版本来获得另外两个。
所有这些都不适用于您当前的问题,因为您只想要一个输出序列,我推荐kotarak的解决方案(或其他类似的解决方案)。但是,我想您可能想要解释一下为什么Clojure的内置split-with
遍历输入序列两次。
对于特殊要求,您可以随时下拉到lazy-seq
。
(defn splice-tail
([pred coll] (splice-tail pred 1 coll))
([pred n coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first s)]
(if (pred fst)
(cons fst (splice-tail pred n (rest s)))
(nthnext s n)))))))