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上,我找到大约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)))
注意,我不想基于filter实现first-by,因为

https://twitter.com/borkdude/status/1567525617549152257
(first xform coll)即使不受chunking的影响,因为它会使用filter返回的变换器arity。

差异很大

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

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

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

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

它不会引起任何chunking。事实上,它甚至没有意识到序列的存在。
啊,(完成简化),绝妙!比我基于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 或 grasp 或其他工具收集使用数据,这将很有帮助。还有一个非常有用的点是有一个现有公共实用库中的类似函数的列表及其用法(以及如果有的话它们是如何实现的区别)。

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

我现在正在处理一些数据
将数据添加到原始帖子中。
grep.app 搜索结果

* 200+ 个 `find-first` 用法:[https://grep.app/search?q=%5C%28%28%5Ba-zA-Z-%5D%2A/%3F%29%3Ffind-first%5B%5E-%5D&regexp=true&filter[lang][0]=Clojure](https://grep.app/search?q=%5C%28%28%5Ba-zA-Z-%5D%2A/%3F%29%3Ffind-first%5B%5E-%5D&regexp=true&filter[lang][0]=Clojure)

《find-first》实现有24个:https://grep.app/search?q=defn.%2Afind-first%5B%5E-%5D&regexp=true&filter[lang][0]=Clojure

几个《ffilter》实现:https://grep.app/search?q=%5C%28defn.%2Affilter&regexp=true&filter[lang][0]=Clojure

使用`(some #(when pred %) coll)`超过100次:https://grep.app/search?q=%5C%28some%20%23%5C%28when%20.%2A%20%25%5C%29&regexp=true&filter[lang][0]=Clojure

几个`(reduce (fn [_ cur] (when (pred cur) (reduced cur)) coll))`实现:https://grep.app/search?q=%5C%28reduce.%2A%5C%28when.%2A%5C%28reduced&regexp=true&filter[lang][0]=Clojure

《filter-first》的实现有几个:https://grep.app/search?q=filter-first&regexp=true&filter[lang][0]=Clojure

定义为一个clojure-contrib seq函数,但你知道那些~:https://github.com/richhickey/clojure-contrib/blob/40b960bba41ba02811ef0e2c632d721eb199649f/src/main/clojure/clojure/contrib/seq.clj#L179
...