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,因为分块(chunking)

https://twitter.com/borkdude/status/1567525617549152257
by
(first xform coll) 即使在分块方面也不会受到影响,因为它会使用filter返回的变换器数量。

巨大的差异

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

(first (filter <pred> <coll>))
by
编辑 by
是的,如果我们这样实现它

(defn first' [xf coll]
  (transduce xf (completing (fn [_ x] (reduced x))) nil coll))

它不会引起任何分块。实际上,它甚至没有意识到序列的存在。
by
啊,(完成简化),天才!比基于xforms的建议更简洁。
by
对不起,我指的是`(completing (fn [_ x] (reduced x)))`。已修复。
+2
by

顺便说一下,类似的提议之前已经被拒绝: https://clojure.atlassian.net/browse/CLJ-2056

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

虽然我同意线性搜索效率不高,但这是在考虑大数据集时。我使用这个操作最常见的是处理短序列,例如处理文本行。为单个查找构建索引结构仍需要线性处理数据,并且对于短序列来说效率更低(与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)
```
+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,它们都是同等重要的组成部分,所以我感觉名字应该描述这两个组合。
因为我以前用`filter`包裹`first`,所以我选择了`ffilter`。
0

编辑

正如在另一个回答中提到的,这种情况在过去已被考虑并拒绝。我不认为那时有什么改变。

关于这个故事的个人经历并不特别有用,但数据是有用的。如果你可以使用grep.app或grasp或其他工具收集使用数据,那将很有帮助。同样有用的是列出一个常用库中现有相似函数及其使用情况(以及如果有的话,它们在实现上的不同)。

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

...