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

欢迎!请参阅 关于 页面,了解更多此处的信息。

+39

我经常编写: (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% 是 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 上,我发现一些+when形状的大约 8% 的 some 使用。

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的返回转换器arity。

巨大差异

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

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

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

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

它不会引起任何分块。实际上,它甚至没有意识到序列。
作者:
哦,(completing reduced),太棒了!比我基于xforms提出的要简洁得多。
作者:
抱歉,我是指`(completing (fn [_ x] (reduced x)))`。已经修复。
+2
作者:

顺便说一句,之前有一个类似的提议被拒绝了: 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`。
   如果为1元函数,则返回函数#(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)
这对分块有多好的表现?some + when不支持分块
你在这里看到分块在哪里?
现在对我来说很有意义。
0

我每天都在做同样的事情!给予点赞。

我个人把这个函数叫做"find",并这样定义。这也是我过去在其他语言和函数库中看到的方式。我认为"first"是解决方案的一部分,但并不是我的首选名字,因为它既是filter,也是first,两者都是相同的基本部分,所以我感觉名字应该描述两者的组合。
我选择了`ffilter`,因为我在另一种做法中是通过将`filter`与`first`包装在一起来了。
0

编辑了

如另一回答中所提,这之前已经考虑并否定了。我认为从那时起没有发生任何变化。

关于这个的轶事并不特别有用,但数据是有用的。如果你可以使用grep.app或grasp之类的工具收集使用数据,这将是有帮助的。还有一个好处就是有一个现有类似函数的列表,以及它们在常用库中的使用情况(以及如果它们不同,它们的实现方式)。

另外,考虑一个好的使用语料库可能会有所帮助,看看是否有更深入或有更多有趣共同点的共同问题,这可能存在其他替代解决方案。

...