简体   繁体   English

如果任何给定的值不是空集合,如何在Clojure中测试?

[英]How to test in Clojure if any given value is not-empty collection?

I need a predicate which returns logically true if the given value is a not-empty collection and logically false if it's anything else (number, string etc.). 我需要一个谓词,如果给定的值是非空集合,则逻辑返回true,如果是其他任何内容(数字,字符串等),则逻辑上为false。 And more specifically, that the predicate won't throw the IllegalArgumentException if applied to single number, or string. 更具体地说,如果应用于单个数字或字符串,则谓词不会抛出IllegalArgumentException

I came up with the following function, but I'm wondering if there is some more idiomatic approach? 我提出了以下功能,但我想知道是否有一些更惯用的方法?

(defn not-empty-coll? [x]
  (and (coll? x) (seq x)))

This will satisfy following tests: 这将满足以下测试:

(is (not (not-empty-coll? nil)))    ;; -> false
(is (not (not-empty-coll? 1)))      ;; -> false
(is (not (not-empty-coll? "foo")))  ;; -> false
(is (not (not-empty-coll? [])))     ;; -> nil      (false)
(is (not (not-empty-coll? '())))    ;; -> nil      (false)
(is (not (not-empty-coll? {})))     ;; -> nil      (false)
(is (not-empty-coll? [1]))          ;; -> (1)      (true)
(is (not-empty-coll? '(1)))         ;; -> (1)      (true)
(is (not-empty-coll? {:a 1}))       ;; -> ([:a 1]) (true)

EDIT: A potential use case: 编辑:一个潜在的用例:

Let's say we need to process some raw external data which are not (yet) under our control. 假设我们需要处理一些尚未受我们控制的原始外部数据。 Input could be for example a collection which contains either primitive values, or nested collections. 输入可以是例如包含原始值或嵌套集合的集合。 Other example could be a collection holding some inconsistent (maybe broken?) tree structure. 其他示例可能是一个包含一些不一致(可能已损坏?)树结构的集合。 So, we can consider mentioned predicate as first line data cleaning. 因此,我们可以将提到的谓词视为第一行数据清理。

Otherwise, I agree with comments that is better to explicitly separate and process collection and non-collection data. 否则,我同意更好地明确分离和处理收集和非收集数据的注释。

As suggested in the comments, I would consider calling not-empty? 正如评论中所建议的那样,我会考虑打电话给not-empty? with a non-collection argument to be an invalid usage, which should generate an IllegalArgumentException . 使用非集合参数作为无效用法,应该生成IllegalArgumentException

There is already a function not-empty? 已有功能not-empty? available for use in the Tupelo library . 在Tupelo图书馆使用 Here are the unit tests: 以下是单元测试:

(deftest t-not-empty
  (is (every?      not-empty? ["one" [1] '(1) {:1 1} #{1}     ] ))
  (is (has-none?   not-empty? [ ""   [ ] '( ) {}     #{ }  nil] ))

  (is= (map not-empty? ["1" [1] '(1) {:1 1} #{1} ] )
         [true true true true true]  )
  (is= (map not-empty? ["" [] '() {} #{} nil] )
         [false false false false false false ] )

  (is= (keep-if not-empty?  ["1" [1] '(1) {:1 1} #{1} ] )
                            ["1" [1] '(1) {:1 1} #{1} ] )
  (is= (drop-if not-empty?  [""  []  '()  {}     #{}  nil] )
                            [""  []  '()  {}     #{}  nil] )

  (throws? IllegalArgumentException (not-empty? 5))
  (throws? IllegalArgumentException (not-empty? 3.14)))

Update 更新

The preferred approach would be for a function to only receive collection parameters in a given argument, not a mixture scalar & collection arguments. 首选方法是函数只接收给定参数中的集合参数,而不是混合标量和集合参数。 Then, one only needs not-empty given the pre-knowledge that the value in question is not a scalar. 然后,只有在知道所讨论的值不是标量的情况下,才需要not-empty I often use Plumatic Schema to enforce this assumption and catch any errors in the calling code: 我经常使用Plumatic Sc​​hema来强制执行此假设并捕获调用代码中的任何错误:

(ns xyz
  (:require [schema.core :as s] )) ; plumatic schema
(s/defn foo :- [s/Any]
  "Will do bar to the supplied collection"
  [coll :- [s/Any]]
  (if (not-empty coll)
    (mapv bar foo)
    [ :some :default :value ] ))

The 2 uses of notation :- [s/Any] checks that the arg & return value are both declared to be a sequential collection (list or vector). 表示法的两种用法:- [s/Any]检查arg&return值是否都被声明为顺序集合(列表或向量)。 Each element is unrestricted by the s/Any part. 每个元素不受s/Any部分的限制。


If you can't enforce the above strategy for some reason, I would just modify your first approach as follows: 如果由于某种原因无法执行上述策略,我只需修改您的第一种方法,如下所示:

(defn not-empty-coll? [x]
  (and (coll? x) (t/not-empty? x)))

I'm hoping you know at least a little about the param x so the question becomes: Is xa scalar or a non-empty vector. 我希望你至少知道关于param x的一点,所以问题就变成了:是xa标量还是非空向量。 Then you could say something like: 然后你可以这样说:

(defn not-empty-coll? [x]
  (and (sequential? x) (t/not-empty? x)))

How about using Clojure protocols and type extensions to solve this? 如何使用Clojure协议和类型扩展来解决这个问题?

(defprotocol EmptyCollPred
  (not-empty-coll? [this]))

(extend-protocol EmptyCollPred
  Object
    (not-empty-coll? [this] false)
  nil
    (not-empty-coll? [this] false)
  clojure.lang.Seqable
    (not-empty-coll? [this] (not (empty? (seq this)))))

(is (not (not-empty-coll? nil)))    ;; -> false
(is (not (not-empty-coll? 1)))      ;; -> false
(is (not (not-empty-coll? "foo")))  ;; -> false
(is (not (not-empty-coll? [])))     ;; -> nil      (false)
(is (not (not-empty-coll? '())))    ;; -> nil      (false)
(is (not (not-empty-coll? {})))     ;; -> nil      (false)
(is (not-empty-coll? [1]))          ;; -> (1)      (true)
(is (not-empty-coll? '(1)))         ;; -> (1)      (true)
(is (not-empty-coll? {:a 1}))       ;; -> ([:a 1]) (true)

Maybe it would be cleaner to extend just String and Number instead of Object - depends on what do you know about the incoming data. 可能更简洁的是扩展StringNumber而不是Object - 取决于你对输入数据的了解。 Also, it would be probably better to filter out nil s beforehand instead of creating a case for it as you see above. 此外,如上所述,最好过滤掉nil而不是为它创建一个案例。

Another - conceptually similar - solution could use multimethods. 另一个 - 概念上类似 - 解决方案可以使用多方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM