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更高效。如果您想与其他序列函数结合使用,可以将它们统一到转换生成器中,比如:

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

而不是

(first-by #(> % 100) (map #(* % %) (range)))
注意,我不想基于filter来实现first-by,因为分块操作会使

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))

它不会导致任何分块操作。事实上,它甚至没有意识到序列的存在。
啊,(completing reduced),太聪明了!比基于xforms的建议更简洁。
对不起,我是想说`(completing (fn [_ x] (reduced x)))`。已修复。
+2 投票

值得一提的是,之前的类似提议已被拒绝:[https://clojure.atlassian.net/browse/CLJ-2056](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`。
   若函数为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)
这个与分块操作是如何配合的?一些 + when 并不分块。
你在这哪里看到分块操作?
现在看来,并没有分块操作。
0

我每天都在做同样的事情!已点赞。

我个人将这个函数称为“find”,并这样定义它。这是我在其他语言和功能库中看到的名称。我认为“first”是解决方案的一部分,但不是我想取的名字,因为它是filter+first,它们都是相等的组件部分,所以我感觉名字应该描述两个元素混合的结果。
我选择了`ffilter`,因为我之前的方法是使用`first`包装`filter`。
0

编辑

正如另一个答案中提到的,这个建议在过去已经被考虑并拒绝。我不认为自那时以来有什么变化。

关于这个话题的轶事不太有用,但数据很有用。如果您可以使用grep.app或其他软件收集使用数据,那将是有帮助的。同样有用的是有一个常见实用库中类似函数的列表及其使用情况(以及如果它们存在差异,它们是如何实现的)。

分别来说,可以考虑从更好地考虑语料库的角度来解决这个问题,看看是否存在更深或更有趣的共性或问题,这可能还有其他替代解决方案。

by
我现在正在处理一些数据。
by
已将数据添加到原始帖子里。
by
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)
...