请分享您的想法,参与2024 Clojure状态调查!

欢迎!有关如何使用此功能,请参阅关于页面以获取更多信息。

+39
Clojure
编辑

我经常编写: (some #(when (pred %) %) ...) 而且经常错误地写: (some #(pred %) ...) 。我认为在 clojure.core 中将 (some #(when (pred %) %) ...) 作为内置函数是值得的。

建议的名称: 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+when 形式。

7 条回答

+7

我更倾向于像在该评论中提到的《code>(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)))
注意,我不想在first-by上基于filter,因为分块

https://twitter.com/borkdude/status/1567525617549152257
(first xform coll)不会受分块的影响,因为它将使用返回转换器的filter的阶数。

差异巨大

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

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

编辑
是的,如果我们这样实现

(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的回答很有趣,我确实看到了他的观点。

鉴于我同意线性搜索效率低下,但这种情况是在考虑更大的数据集时。我最常见的操作是处理短序列,例如处理文本行。即使对于单个查找,构建索引结构仍需要进行线性数据处理,而且对于短序列来说效率也远低于使用数组映射。

如果不同的结构始终是正确的解决方案,那么我们就不需要有一打人为这个工单立即点赞,其中许多人评论说他们经常这样做。
+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-arity,则返回函数 #(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`,因为另一种方式是通过用 `first` 包装 `filter` 来实现的。
0
by
编辑 by

如另一回答中所述,这之前已被考虑并拒绝。我认为自那时以来没有改变。

关于这一点的故事并不特别有用,但数据是。如果你可以使用grep.app或grasp或其他工具收集使用数据,那么这将会很有帮助。同样有用的还有列举出常见库中现有的类似函数及其使用情况(以及它们是否有所不同)。

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

...