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。

总的使用数量: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形式的使用。

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
编辑 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 或 grasps 或其他工具收集使用数据将很有帮助。并且,有一个列表显示现有类似函数在常用库中的用法及其实现(如果它们有所不同)也将很有用。

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

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