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%是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)))
by
注意,我不想基于 filter 实现first-by,因为分块

https://twitter.com/borkdude/status/1567525617549152257
by
(first xform coll) 由于会使用 filter 返回的 transducer 阶数,所以不会受到分块的影响。

差异巨大

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

(first (filter <pred> <coll>))
by
edited by
是的,如果我们这样实现

(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

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)
这和分块操作如何协同工作? some + when 并不进行分块操作
你在这里看到分块操作在哪里?
现在我看不出任何分块操作。
0

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

个人将此函数称为“find”,并如此定义。我过去在其他语言和函数库中看到过这样的叫法。我认为“first”是解决方案的一部分,但并非构成名称,因为它是filter+first,它们都是相等组成部分,因此我认为名称应描述组合这两种功能。
选择了`ffilter`,因为之前的方法是将`filter`与`first`包装在一起
0

编辑

正如另一个答案中提到的,这已考虑过并已被拒绝。我认为自那以后没有变化。

关于此的故事并不特别有用,但数据是有用的。如果您可以使用grep.app或grasp或类似工具收集使用数据,那就太有帮助了。还可以列出通用工具库中的现有类似函数及其使用情况(以及如果有的话,它们的不同实现方式)。

此外,从更好地考虑语料库的角度来处理这个问题可能很有用,看看是否存在更深奥或更有趣的共同点或问题,这可能有其他替代解决方案。

...