请在2024 Clojure状态调查!中分享您的想法。

欢迎!有关此如何工作的更多信息,请参阅关于页面。

+39
Clojure
编辑

我经常写:(some #(when (pred %) %) ...),并经常错误地写成:(some #(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形式的some的使用。

7 个答案

+7

我更愿意像评论中提到的CLJ-2056中的那样有:(first xform coll)。这个想法被提到了那里,但我不认为当时进行了充分讨论。

它比 somefirst-by 更通用:(some <pred> <coll>) just means (first (keep <pred>} <coll>)),而 (first-by <pred> <coll>) just means (first (filter <pred>} <coll>))

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

(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返回的transducer阶数,因此不会受chunking影响。

巨大的差异

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

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

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

它不会引起任何chunking。事实上,它甚至根本不会意识到序列本身。
啊,(完成简化),天才!比基于xforms的提议更加简洁。
抱歉,我是想说 `(completing (fn [_ x] (reduced x)))`。已修复。

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

Alex的回复很有趣,我确实同意他的观点。

话虽如此,虽然我同意线性搜索效率低下,但这通常是在考虑大规模数据集时的情况。我最常用的这种操作是处理短序列,例如处理文本行。对于单次查找,仍然需要对数据进行线性处理,并且在处理短序列时效率更低(与ArrayMap进行比较)。

如果不同的结构总是正确的做法,那么我们就不会有一打人立即对这个议题进行投票,其中一些人还会评论说我们经常这样做。

我明白,允许甚至支持线性搜索可能会导致人们一开始就使用错误的数据结构。

然而,在很多情况下,通过小型集合进行简单的线性搜索恰恰是正确的事情,而在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,我通常用 solve this,只要我要返回的项目不是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`,因为我另一种方法是封装`first`在`filter`中。
0
如在其他答案中所述,这已在过去被考虑并拒绝。我认为从那时起没有发生变化。


编辑

关于这个话题的轶事并不特别有用,但数据是有用的。如果您可以使用grep.app或grasp或其他工具收集使用数据,那将很有帮助。同样有用的还有列出常见实用库中现有的类似函数及其使用情况(以及如果有的话,它们的不同实现方式)。

另外,可能有必要更好地考虑用法语料库,看看是否存在更深层次或更有趣的共同点或问题,这可能有其他替代解决方案。

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

* 使用 `find-first` 的 200+ 次调用:[链接](https://grep.app/search?q=(\(a-zA-Z-%)\*/%F3find-first[^-%])®exp=true&filter[lang][0]=Clojure)

* `find-first` 的 24 实现实例:[链接](https://grep.app/search?q=defn.%2Afind-first[^-%]®exp=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 序列函数但你知道的 ;):https://github.com/richhickey/clojure-contrib/blob/40b960bba41ba02811ef0e2c632d721eb199649f/src/main/clojure/clojure/contrib/seq.clj#L179
...