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使用与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](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 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`,因为我以前的做法是将`filter`与`first`包装在一起。
0

编辑

正如另一个答案所述,这已经在过去被考虑并拒绝。我不认为自从那时以来有什么改变。

关于这一点的轶事并不特别有用,但数据是有用的。如果你可以使用grep.app或grasp或其他工具收集使用数据,这将是有帮助的。还有一个有用的列表,列出了常见工具库中现有的类似函数及其用法(以及它们的实现差异,如果有的话)。

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

...