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

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

+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。

总的使用: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 + when形式的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算子。

巨大的差异

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

(first (filter <pred> <coll>))
九月8日,2022年
编辑
是的,如果我们这样实现它

(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`。
   如果为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或其他工具收集使用数据,将会很有帮助。另外,有一个现有类似功能的通用库及其使用情况列表(以及如果有的话,它们的实现差异)也将很有用。

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

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