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。

总一些 + fn 使用:1701,其中 11% 是一些 + 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 的arity。

差别很大

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

(first (filter <pred> <coll>))
by
编辑 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
by

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

然而,在许多情况下,对于小集合进行简单的线性搜索正是正确的做法,而在Clojure中这样做总有些尴尬。我认为这种尴尬是一种不好的尴尬,那种导致不清晰和错误的原因。

当我需要它时,我通常在某个实用命名空间中实现Common Lisp的`find-if`,因为这会使代码非常清晰

(find-if odd? xs)

+1
by

只是提一下,我更愿意在核心中看到类似`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
by
编辑了 by
我最新版本的此类函数是`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 positive?) 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)
这在分块处理中表现如何? &=when 并不进行分块。
你在这里看到分块的地方在哪里?
现在我认为这很有意义。
0

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

我曾是个人把这个函数叫做 "find" 并定义为如此。这是我在过去在其他语言和函数库中看到它是如何被称呼的。我认为 "first" 是解决方案的一部分,但并不是我想用来命名的,因为它是 filter+first,两者都是相等的部分,所以我感觉名字应该描述两者的组合。
我选择了“ffilter”,因为另一种方法是使用“filter”和“first”包装。
0

编辑于

正如在其他答案中提到的,这在过去已经被考虑并拒绝了。我不认为那时有任何改变。

关于此事的轶事并不特别有用,但数据是有用的。如果您可以使用grep.app或grasp或其他工具收集使用数据,那将有所帮助。还有一个有用的点是列出公共实用库中现有的类似功能及其用法(以及如果有的话,它们的实现差异)。

另外,从更好考虑用例库的角度来处理这个问题可能是有用的,看看是否存在更深层次的或有更多有趣的共性或问题,这可能具有其他替代解决方案。

...