Clojure 2024 现状调查!中分享您的想法。

欢迎!请查看关于页面以获取更多关于这个工作方式的信息。

+39
Clojure
编辑

我经常写: (some #(when (pred %) %) ...)并且经常错误地写成: (some #(pred %) ...)。我认为在clojure.core中内置(some #(when (pred %) %) ...)将是有价值的。

建议名称: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 上,我发现 some+when 形式的使用大约占 some 使用量的8%。

7 个答案

+7
by
注意,我不想基于filter来实现first-by,因为分块

https://twitter.com/borkdude/status/1567525617549152257
by
(first xform coll) 不会受到分块的影响,因为它会使用filter返回的转换器类型。

巨大差异

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

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

(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

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

然而,很多时候在小集合中进行简单的线性检索正是正确的事情,而且在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-arity的情况下返回函数 #(select % pred)。
  ([pred]
   #(select % pred))
  ([x pred]
   (when (pred x) x)))

(some-> x (select number?) inc)

(keep (select pos?) xs)
```
+1

编辑

使用cgrand/xforms我通常用来解决,只要我想要返回的项目不是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或其他工具收集使用数据那将很有帮助。同时,了解常见功能库中现有相似功能的列表及其用法(以及它们的实现差异,如果有)也是很有用的。

另外,从更好地考虑用法语料库的角度来看,这可能是有用的,看看是否有更深或更有趣的共同点或问题,可能还有其他替代解决方案。

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