Предотвращение ошибки времени выполнения, если представлено с неправильным типом - PullRequest
0 голосов
/ 13 января 2020

В настоящее время я пытаюсь настроить простую функцию, которая читает список целых чисел и возвращает квадрат каждого целого числа следующим образом:

(defn square-seq
  [s]
  (if (string? s)
    (s = nil)
    (map #(* % %) s)))

Однако я хочу, чтобы код мог обрабатывать строки входы и просто по умолчанию их числовое значение, чтобы предотвратить ClassCastException. Например, если я сделаю

(square-seq ["Hello" 2 3])

, тогда возвращаемое значение будет:

(1 4 9) or (nil)

Не уверен, что я слишком обдумываю это или просто задумываюсь, но я не могу понять, как его получить работать на жизнь мне, любая помощь будет оценена!

Ответы [ 4 ]

0 голосов
/ 13 января 2020

Я попытаюсь ответить на часть «Предотвращение ошибок времени выполнения» . Основная идея c заключается в том, чтобы обернуть вашу целевую функцию обработчиком исключений, чтобы можно было устранить ошибку во время выполнения (с помощью еще одной функции). С помощью обработчика generic c вы можете легко применить этот механизм к любой целевой функции, которую вы используете ...

(defn square-seq
  [xs]
  ;; here safe-fn is a high order function to make * safe
  ;; by attaching an exception handler to it (which always return 1)
  (map (safe-fn * (constantly 1)) xs xs))

(square-seq ["Hello" 2 3])
;; => (1 4 9)

(defn half-seq
  [xs]
  ;; here we make / safe and also provide a different handler
  (map (safe-fn / (constantly 99)) xs (repeat 2)))

(half-seq ["Hello" 2 3])
;; => (99 1 3/2)

(defn triple-seq
  [xs]
  ;; yet another handler
  (apply map (safe-fn * (constantly 3)) (repeat 3 xs)))

(triple-seq ["Hello" 2 3])
;; => (3 8 27)

И safe-fn можно легко реализовать как:

(defn safe-fn [f ex-handler]
  (fn [& args]
    (try
      (apply f args)
      (catch Throwable t
        (ex-handler t args)))))
0 голосов
/ 13 января 2020

в качестве упражнения я бы предложил функцию полезности следующим образом:

(letfn [(single-or-all [arg & args] (if (seq args)
                                      (vec (cons arg args))
                                      arg))]
  (defn mapper [f & {:keys [guard guard-failed]
                     :or {guard (constantly true)
                          guard-failed single-or-all}}]
    (fn [& items] (if (apply guard items)
                    (apply f items)
                    (apply guard-failed items)))))

фабричная функция mapper здесь производит функцию, защищающую ввод, и приводящую к аварийному вызову функции в случае сбоя охранник:

;; default usage:
user> (map (mapper #(* % %)) [1 2 3])
;;=> (1 4 9)

;; guard with default result:
user> (map (mapper #(* % %) :guard number?) [1 2 "aaa" 3])
;;=> (1 4 "aaa" 9)

;; guard with custom result:
user> (map (mapper #(* % %)
                   :guard number?
                   :guard-failed (constantly -1)) [1 2 "aaa" 3])
;;=> (1 4 -1 9)

;; multiple collections mapping with guards:
user> (map (mapper +
                  :guard #(every? number? %&))
           [1 2 "aaa" 3]
           [10 "zzz" 20 30])
;;=> (11 [2 "zzz"] ["aaa" 20] 33)

user> (map (mapper +
                   :guard #(every? number? %&)
                   :guard-failed (partial vector :guard/failed))
           [1 2 "aaa" 3]
           [10 "zzz" 20 30])
;;=> (11 [:guard/failed 2 "zzz"] [:guard/failed "aaa" 20] 33)

user> (map (mapper +
                   :guard #(every? number? %&)
                   :guard-failed #(apply + (filter number? %&)))
           [1 2 "aaa" 3]
           [10 "zzz" 20 30]
           [100 "x" 200 300])
;;=> (111 2 220 333)
0 голосов
/ 13 января 2020

Похоже на повод для простого try/catch:

(defn square
  [x]
  (try
    (* x x)
    (catch Exception ex
      :error)))

и при использовании:

(mapv square ["hello" 2 3]) => [:error 4 9]

Хотя я бы сказал, что вызывающая сторона должна иметь оператор try/catch , поскольку только вызывающий абонент может (возможно!) сделать что-то умное, когда присутствует не номер.

0 голосов
/ 13 января 2020

Вы проверяете строку на верхнем уровне (уровнях), поэтому вы, скорее всего, никогда не попадете в эту ветку Вы должны сделать проверку, где это фактически вызывает ошибку (внутри anon-fn).

Так что я бы сначала написал функцию, которая делает "safe-square" и ваш square-seq может просто позвони.

(defn square
  [x]
  (if (string? x)
    x
    (* x x)))

(defn square-seq
  [xs]
  (map square xs))

(square-seq ["Hello" 42])
; ⇒ ("Hello" 1764)
...