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上,我发现在some的使用中大约有8%是some+when形状的。

7 个答案

+7

我更愿意选择 (first xform coll),这是在 评论 中提到的,在 CLJ-2056。这个想法当时被提到了,但当时并没有完全讨论。

这比 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))

它不会引起任何分块。事实上,它甚至没有意识到序列的存在。
by
啊,(completing reduced),太聪明了!比我基于 xforms 的建议要简洁得多。
by
对不起,我是指 `(completing (fn [_ x] (reduced x)))`。已修复。
+2
by

FWIW,曾经有一个类似的提案被拒绝过:[链接](https://clojure.atlassian.net/browse/CLJ-2056)

by
亚历克斯的回答很有趣,我确实看到了他的观点。

话虽如此,虽然我同意线性搜索在处理大数据集时效率低下,但我的常见用途是在处理短序列时,例如在处理文本行时。为单个查找构建索引结构仍然需要线性处理数据,并且对于短序列来说效率更低(与ArrayMap相比)。

如果不同的结构总是正确的方式,那么就不会有一打人立即投票支持这个工单,其中我们中有几个人在评论中提到我们经常会这样做。
+2
by

我明白这个观点,即允许或支持线性搜索可能会让人一开始就使用错误的数据结构。

然而,在小集合中进行简单的线性搜索通常是正确的做法,而使用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)
```
好吧,这样一来,人们也可以对这篇帖子进行点赞 https://ask.clojure.org/index.php/8945/something-like-when-pred-in-the-core
+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`,因为另一种做法是用`first`包装`filter`。
0投票

编辑了

正如另一点回答中提到的,这一点在过去已被考虑并拒绝。我认为那时以来没有什么变化。

关于这个问题的轶事并不特别有用,但数据是。如果你能使用grep.app或grasp或其他工具收集使用数据,这将很有帮助。同样有用的还有列出普通库中现有的类似函数及其使用情况(以及如果有的话,它们的实现差异)。

此外,考虑从更好地考虑用法语料库的角度来解决这个问题可能也很有用,看看是否存在更深层次或更有趣的共性或问题,这可能有许多其他替代解决方案。

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