参与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)))
by
注意,我不想以filter为基础实现first-by,因为分块处理会影响first xform coll,尽管它将使用filter返回转换器的arity。

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

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

顺便提一下,类似的提议以前已经拒绝过:[https://clojure.atlassian.net/browse/CLJ-2056](https://clojure.atlassian.net/browse/CLJ-2056)

by
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或其他工具收集使用数据,这将很有帮助。另外,有一份通用工具库中类似函数的列表及其使用情况(以及如果它们有差异,这些差异是怎样的)也会有帮助。

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

我现在正在处理一些数据
将数据添加到原始帖子中。
...