'让'未找到抛出异常的替换?

时间:2014-06-27 04:32:44

标签: clojure

我想访问地图和记录中的值,当密钥不存在时抛出异常。这是我尝试过的。有更好的策略吗?

这不起作用,因为每次评估throw

(defn get-or-throw-1 [rec key]
  (get rec key (throw (Exception. "No value."))))

也许有一个使用宏的简单方法?嗯,这不是它;它与第一个定义具有相同的问题,即使throw的评估稍后发生:

(defmacro get-or-throw-2 [rec key]
  `(get ~rec ~key (throw (Exception. "No value."))))

这个通过让get返回一个(理论上)永远不会以任何其他方式生成的值来工作:

(defn get-or-throw-3 [rec key]
  (let [not-found-thing :i_WoUlD_nEvEr_NaMe_SoMe_ThInG_tHiS_021138465079313
        value (get rec key not-found-thing)]
    (if (= value not-found-thing)
        (throw (Exception. "No value."))
        value)))

我不想猜测通过其他进程永远不会出现哪些关键字或符号。 (我可以使用gensym生成not-found-thing的特殊值,但我不明白为什么会更好。我不必担心有人故意试图破坏目的通过在地图或记录中使用not-found-thing的值来起作用。)

有什么建议吗?

5 个答案:

答案 0 :(得分:7)

这是preconditions的意思。它们内置于语言中,应该用于输入验证(尽管如果前提条件对于特定情况不灵活,您可以使用断言)。

user> (defn strict-get
        [place key]
        {:pre [(contains? place key)]}
        (get place key))
#'user/strict-get
user> (strict-get {:a 0 :b 1} :a)
0
user> (strict-get {:a 0 :b 1} :c)
AssertionError Assert failed: (contains? place key)  user/eval6998/fn--6999/strict-get--7000 (form-init7226451188544039940.clj:1)

答案 1 :(得分:6)

这是find函数的用途:如果找不到任何内容,则(find m k)会返回nil,如果从[k v]到{{k,则会v 1}}被发现了。您可以随时区分这两者,并且不需要猜测地图中可能存在的内容。所以你可以写:

(defn strict-get [m k]
  (if-let [[k v] (find m k)]
    v
    (throw (Exception. "Just leave me alone!"))))

答案 2 :(得分:3)

您可以使用名称空间限定关键字,这可以减少意外使用关键字的可能性:

(defn get-or-throw [coll key]
  (let [result (get coll key ::not-found)]
    (if (= result ::not-found)
      (throw (Exception. "No value."))
      result)))

或者,您可以使用contains?

(defn get-or-throw [coll key]
  (if (contains? coll key)
    (get coll key)
    (throw (Exception. "No value."))))

这应该是安全的,因为你的地图/记录应该是不可变的。

答案 3 :(得分:1)

此功能也在名为grab的{​​{3}}库中实施。请注意,参数顺序在此处是相反的,看似有意:(grab :my-key my-map)

答案 4 :(得分:0)

我更喜欢simulant的名称和实现:getxgetx-in

(defn getx
  "Like two-argument get, but throws an exception if the key is
   not found."
  [m k] 
  (let [e (get m k ::sentinel)]
    (if-not (= e ::sentinel)
      e 
      (throw (ex-info "Missing required key" {:map m :key k})))))

(defn getx-in
  "Like two-argument get-in, but throws an exception if the key is
   not found."
  [m ks] 
  (reduce getx m ks))

https://github.com/Datomic/simulant/blob/d681b2375c3e0ea13a0df3caffeb7b3d8a20c6a3/src/simulant/util.clj#L24-L37