我需要一个谓词,如果给定的值是非空集合,则逻辑返回true,如果是其他任何内容(数字,字符串等),则逻辑上为false。
更具体地说,如果应用于单个数字或字符串,则谓词不会抛出IllegalArgumentException
。
我提出了以下功能,但我想知道是否有更多惯用法?
(defn not-empty-coll? [x]
(and (coll? x) (seq x)))
这将满足以下测试:
(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)
编辑:一个潜在的用例:
假设我们需要处理一些尚未受我们控制的原始外部数据。输入可以是例如包含原始值或嵌套集合的集合。其他示例可能是一个包含一些不一致(可能已损坏?)树结构的集合。因此,我们可以将提到的谓词视为第一行数据清理。
否则,我同意最好明确区分和处理收集和非收集数据的评论。
答案 0 :(得分:0)
正如评论中所建议的那样,我会考虑使用非集合参数调用not-empty?
作为无效用法,这应该生成IllegalArgumentException
。
已有一个功能not-empty?
可供使用in the Tupelo library。以下是单元测试:
(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)))
首选方法是使函数只接收给定参数中的集合参数,而不是混合标量和参数。集合参数。然后,只有在知道所讨论的值不是标量的情况下,才需要not-empty
。 I often use Plumatic Schema强制执行此假设并捕获调用代码中的任何错误:
(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 ] ))
符号:- [s/Any]
的两种用法检查arg&返回值都声明为顺序集合(列表或向量)。每个元素都不受s/Any
部分的限制。
如果您出于某种原因无法执行上述策略,我只会修改您的第一种方法,如下所示:
(defn not-empty-coll? [x]
(and (coll? x) (t/not-empty? x)))
我希望你至少知道一下param x
,所以问题就变成了:x是标量还是非空向量。然后你可以这样说:
(defn not-empty-coll? [x]
(and (sequential? x) (t/not-empty? x)))
答案 1 :(得分:0)
如何使用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)
仅仅扩展String
和Number
而不是Object
可能会更加清晰 - 取决于您对传入数据的了解。另外,如上所示,最好过滤掉nil
s而不是为它创建一个案例。
另一个 - 概念上相似 - 解决方案可以使用多方法。