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使用的是some+when的形式。

7个回答

+7

我更喜欢类似这个评论中提到的(first xform coll)。这个想法在那里被提到,但我不认为那时有充分的讨论。

这比 somefirst-by 更通用:(some <pred> <coll>) 实际上等同于 (first (keep <pred>) <coll>),而 (first-by <pred> <coll>) 则是 (first (filter <pred> <coll>))

此外,它可能比 somefirst-by 更高效。如果您想将其与其他序列函数结合使用,它们可以统一到透传器(transducer)中,例如

(first (comp (map #(* % %))
             (filter #(> % 100)))
       (range))

而不是

(first-by #(> % 100) (map #(* % %) (range)))
请注意,我不想基于过滤器来实现 first-by,因为分块(chunking)

https://twitter.com/borkdude/status/1567525617549152257
尽管它将使用过滤器返回的透传器算子,但 (first xform coll) 不会受到分块的影响。

差异巨大

(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的答复很有趣,我也明白他的观点。

话虽如此,虽然我同意线性搜索在处理大型数据集时效率不高,但我的常用操作是处理短序列,例如在处理文本行时。建立一个索引结构进行单次查找仍然需要对数据进行线性处理,并且对于短序列来说效率更低(与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的组合,它们都是相等的组成部分,所以我感觉这个名字应该描述这两种结合。
我选择了`ffilter`,因为另一种我实现的方法是使用`filter`和`first`的组合。
0
编辑

正如另一条回答中提到的,这个问题在以前已被考虑并拒绝。我不认为自从那时起有什么变化。

关于这个问题的轶事可能并不特别有用,但是数据是有用的。如果您可以使用grep.app或grasp或其他工具收集使用数据,将会有所帮助。另外,拥有一系列常用库中类似函数及其使用情况(以及如果有的话,这些实现之间的差异)的列表也是有用的。

另外,可能从更好地考虑使用情况集合的角度来解决这个问题会有所帮助,看看是否存在更深或更有趣的一致性或问题,这可能具有其他替代解决方案。

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

* `find-first` 使用超过200次:[链接](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
...