2024 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面,获取更多关于此网站如何工作的信息。

+39
Clojure
编辑

我经常写: (some #(when (pred %) %) ...),并且经常错误地写成: (some #(pred %) ...)。我认为将 (some #(when (pred %) %) ... 作为 clojure.core 中的一个内置函数会很不错。

建议的名称:first-by。欢迎提出其他名称,我会在这里列出。

(first-by #(= (:id %) 2) [{:id 1} {:id 2} {:id 3}]) ;;=> {:id 2}

统计信息,在我的本地 .m2 目录中,我找到了 198 个 (some #(when ...) ..) 形式和 1503 个 (some #(foo ...) ...) 形式,其中 foo 不是 when。

some 和 fn 的总使用次数为 1701,其中 11% 是 of the some + when 形式。

找到这些用法的程序

(ns grasp
  (:require
   [clojure.spec.alpha :as s]
   [grasp.api :as g]))

(s/def ::some+when
  (s/cat :some #{'some}
         :fn (s/spec (s/cat :fn #{'fn 'fn*}
                            :args vector?
                            :when (s/spec (s/cat :when #{'when} :whatever (s/* any?)))))
         :coll any?))

(defn keep-fn [{:keys [spec expr uri]}]
  (let [conformed (s/conform spec expr)]
    (when-not (s/invalid? conformed)
      {:expr expr
       :uri uri})))

(defn -main [& args]
  (let [classpath (first args)
        matches (g/grasp classpath ::some+when {:keep-fn keep-fn})]
    (prn (count matches))))

{:deps {io.github.borkdude/grasp {:mvn/version "0.0.3"}}}

在 grep.app 上,我发现约 8% 的 some 使用了 some+when 形式。

7 答案

+7

我更喜欢像在 评论 中提到的 CLJ-2056 中提到的 (first xform coll) 一样。这个想法在那里被提及了,但是我认为当时并没有充分讨论。

这比 somefirst-by 更加通用:(some <pred> <coll>)(first (keep <pred>) <coll>) 相同,而 (first-by <pred> <coll>)(first (filter <pred> <coll>)) 相同。

此外,它可能比 somefirst-by 更高效。如果您想与其他序列函数结合使用,可以将它们合并到变换器中,例如

(first (comp (map #(* % %))
             (filter #(> % 100)))
       (range))

而不是

(first-by #(> % 100) (map #(* % %) (range)))
注意,我不想基于 filter 实现first-by,因为分块 (

https://twitter.com/borkdude/status/1567525617549152257
(first xform coll) 不会受到影响,因为使用了 filter 返回的变换器阶数。

差异巨大

(first (filter <pred> <coll>))

(first (filter <pred> <coll>))

编辑
是的,如果我们这样实现它

(defn first' [xf coll]
  (transduce xf (completing (fn [_ x] (reduced x))) nil coll))

它不会引起任何分块。事实上,它甚至没有意识到序列。
哦,(完成简化),天才!比我根据xforms提出的建议更简洁。
抱歉,我是指`(completing (fn [_ x] (reduced x)))`。已修复。
+2

顺便说一下,之前已经有过类似的提议被拒绝:[https://clojure.atlassian.net/browse/CLJ-2056](https://clojure.atlassian.net/browse/CLJ-2056)

Alex的回复很有趣,我确实看到了他的观点。

话虽如此,尽管我同意线性搜索在处理大量数据集时效率低下,但是我在处理短序列时最常用的操作就是如此,例如在处理文本行时。为了单次查找而构建索引结构仍然需要对数据进行线性处理,而且对于短序列来说效率更低(与ArrayMap相比)。

如果不同的结构始终是正确的方式,那么我们就不会有一打人立刻投这个项目的票,我们中的一些人还评论说我们经常会这样做。
+2

我认为允许甚至支持线性搜索可能会导致人们一开始就使用错误的数据结构。

然而,经常的情况是,对小型集合进行简单的线性搜索正好是正确的事情,而且在Clojure中做这件事总是有点尴尬。我认为这是一种不好的尴尬,它会导致不清晰和错误。

当我需要它时,我通常会实现Common Lisp中的find-if函数,在一个工具命名空间中,因为它使代码非常清晰

(find-if odd? xs)

+1投票

仅作为参考,我更倾向于在核心中使用when-valid而不是first-by

(defn when-valid [pred]
  #(when (pred %) %))

这可以与some组合使用。

(some (when-valid pos?) [-1 0 2]) => 2

它也可以与some-fn一起使用。

((some-fn (when-valid string?) (when-valid pos?)) "foo") => "foo"
((some-fn (when-valid string?) (when-valid pos?)) -5) => nil

编辑了
我此类函数的最新版本是`select`

```
(defn select
  "如果`(pred x)`为逻辑真,则返回`x`,否则返回`nil`。
   如果为单参数,则返回函数#(select % pred)。
  ([pred]
   #(select % pred))
  ([x pred]
   (when (pred x) x)))

(some-> x (select number?) inc)

(keep (select pos?) xs)
```
+1投票

编辑

使用 cgrand/xforms,我通常通过以下方式解决这个问题:x/some (x/some (filter <pred>) coll),只要返回的项目不是 nil。

基于 xforms 代码的潜在实现可能看起来像这样

(defn rf-first
  "Reducing function that returns the first value."
  ([] nil)
  ([x] x)
  ([_ x] (reduced x))) 

(defn xf-first
  "Process coll through the specified xform and returns the first value."
  [xform coll]
  (transduce xform rf-first nil coll))

(xf-first (filter <pred>) coll)
这个与分块(chunking)的兼容性如何?some + when 不支持分块
您在哪里看到分块(chunking)的实现?
现在我觉得这个内容有道理。
0

我每天都做同样的事情!已点赞。

我过去个人将这个函数称为“find”,并这样定义它。我在以往的编程语言和函数库中也看到了这样的命名。我认为“first”是解决方案的一部分,但不是名字的直观选择,因为它结合了filter和first,两个都是相同重要的部分,所以我感觉名字应该描述这种结合。
我选择了"ffilter",因为我之前的方法是将"filter"和"first"一起使用
0

编辑

正如另一篇答案中提到的,之前已经考虑过并拒绝了这一点。我认为从那时起并没有什么变化。

关于这个问题的趣闻轶事并不是特别有用,但数据是有用的。如果你可以使用 grep.app 或 grasp 或其他工具收集使用数据,将会很有帮助。另外,列出现有常用库中类似的函数及其使用方式(如果有的话,它们的实现区别在哪里)也将是有用的。

另外,从这个角度更好地考虑用法语料库,看看是否存在更深或更有趣的共性或问题,可能还有其他替代解决方案。

我现在正在处理一些数据。
已在原始帖子中添加数据。
...