Exception Handling در کلوژر - throw و lein test

error
exception

#1

سلام.
من تازه برنامه نویسی Clojure رو شروع کردم و دارم تمرینهای Exercism رو انجام میدم.
سیستم کارش TDD هست و یه سری اسکریپت تست بهمون میده تا کدی که میزنیم رو با اونا تست کنیم. (بخش test رو من نمینویسم)

توی بعضی از تمرینها، یه تست هست که ورودی اشتباه میفرسته و باید Exception بفرستیم.
توی یکیشون هرچی میفرستم قبول نمیکنه. اینجا دوتا مثال میزنم از exceptionی که مورد تایید هست و چیزی که تایید نمیشه. (کد من مثل همه ولی test ها فرق میکنن)



چیزی که اوکی میده:
تست:

(is (thrown? AssertionError (rna-transcription/to-rna "INPUT")))

کد:

(catch Exception e (throw (AssertionError. "Wrong input.")))

چیزی که fail میشه:
تست:

(is (thrown? Throwable (nucleotide-count/count "INPUT")))

کد:

(catch Exception e (throw (AssertionError. "Wrong input.")))

اینم تست کردم ارور میداد:

(catch Exception e (throw (Exception. "Wrong input.")))

همچنین این هم ارور میده:

(catch Exception e (throw (Throwable. "Wrong input.")))

متن پیامی که توی test نشون میده:

lein test nucleotide-count-test

lein test :only nucleotide-count-test/validates-nucleotides

FAIL in (validates-nucleotides) (nucleotide_count_test.clj:23)
expected: (thrown? Throwable (nucleotide-count/count \X "GACT"))
  actual: nil

Ran 7 tests containing 7 assertions.
1 failures, 0 errors.
Tests failed.

حرفه‌ای های Clojure یه لطف کنن منو از گمراهی به روشنایی برسونن :joy:


#2

اگر می تونی کل کد رو بزار ببینم . اینجوری گیج کنندست. یا اگر دوست داری لینک بده


#3

بخش تست:

(deftest repetitive-cytidine-gets-counted
  (is (= 3 (nucleotide-count/count \C "CCTAC"))))

(deftest repetitive-sequence-has-only-guanosine
  (is (= {\A 0, \T 0, \C 0, \G 8}
         (nucleotide-count/nucleotide-counts "GGGGGGGG"))))

(deftest validates-nucleotides
  (is (thrown? Throwable (nucleotide-count/count \X "GACT"))))

کدی که من نوشتم:

(defn count [nucleotide strand]
  (try (clojure.core/count (filter #(= nucleotide %) strand))
       (catch Exception e (throw (Throwable. "Wrong input.")))))

(defn nucleotide-counts [strand]
  {
   \A (count \A strand),
   \T (count \T strand),
   \C (count \C strand),
   \G (count \G strand)})

توضیح:
دو تا تابع داریم. یکیش اینطوریه که میگیم دنبال /C بگرد و تعدادش رو بهمون میگه. یکی دیگه اینطوریه که نمیگیم دنبال چی میگردیم و تعداد همشو بهمون میده.
مساله هم درمورد پیدا کردن تعداد آمینو‌اسیدهای DNA هست. پس وقتی ازش میخوایم دنبال /X بگرده باید exception بده.

کد من همه‌ی موارد رو درست هندل میکنه و مشکلی نداره بجز اینی که باید throw کنه. (اون‌هم ارور نمیده ولی بخش test چیزی که براش میفرستمو قبول نمیکنه)


پ.ن۱: میتونم کل پروژه‌ی leiningen رو زیپ کنم اینجا بذارم یا یه ریپازیتوری موقت تو گیتهاب بسازم.
پ.ن۲: سیستم Exercism اوپن سورسه و توی گیتهاب وجود داره. اگه فکر میکنید مشکل از سمت اونهاست، میتونم بخش تست اونها رو ادیت کنم :smiley:
پ.ن۳: کدم خوبه؟ جزء اولین کدهای clojure هست که نوشتم. نمیدونم دارم اصولی کار میکنم یا نه.


#4

یه سری مشکلات داری. یه ریپو بزار فیکس می کنم با کامنت و اینجا هم می ذارم.


#5

ریپازیتوری ایجاد شد.
با تشکر فراوان.


#6

از اونجایی که دسترسی برای نوشتن به ریپو ندارم و خیلی تنبلم، فایل اصلی رو همینجا پیست می کنم . :blush:

(ns nucleotide-count
  (:refer-clojure :exclude [count]))

(def valid-nucleotide [\A \T \C \G])

(defn valid-nucleotide?
  [nucleotide]
  (some #(= % nucleotide) valid-nucleotide))

;; Old Code ---------------------------------------------------
;; (defn count [nucleotide strand]
;;   1. Try to avoid using the same names as clojure.core defines. Or at least
;;      make sure to use `:refer-clojure` the way I did in the ns declaration.
;;   2. You don't need a try/catch in here. You should validate the input and
;;      throw judging by the test cases.
;;   3. Throwing a java Exception is not recommended in clojure unless you're
;;      working side by side with some java code. Otherwise, use `ex-info`.
;;   4. Never catch `Exception` type, you should be specific about the exception
;;      type.
;;   (try (clojure.core/count (filter #(= nucleotide %) strand))
;;        (catch Exception e (throw (Throwable. "Wrong input.")))))
;; ------------------------------------------------------------

;; ex-info example
;;
;; (throw
;;  (ex-info "Wrong input."
;;           {:cause nucleotide :location {:file "blah.clj" :lineno 333}}))
;;
;; More info: https://clojuredocs.org/clojure.core/ex-info

(defn count
  [nucleotide strand]
  (when-not (valid-nucleotide? nucleotide)
    (throw (Throwable. "Wrong input.")))  ;; ex-info would be a much better choice here
  (clojure.core/count (filter #(= nucleotide %) strand)))

;; Note: You can use a threading macro if instead of the experssion on line 36:
;;
;; (->> strand
;;      (filter #(= nucleotide %))
;;      clojure.core/count)
;;
;; When you have more functions to chain together threading macros increase the readablity of
;; the code.

;; Old Code ---------------------------------------------------
;; While this function seems enough for current use case but
;; why not to write a more clojury version ?
;; (defn nucleotide-counts [strand]
;;   {
;;    \A (count \A strand),
;;    \T (count \T strand),
;;    \C (count \C strand),
;;    \G (count \G strand)})
;; ------------------------------------------------------------

(defn- nucleotide-reducer
  [strand]
  (fn [acc nucleotide]
    (assoc acc nucleotide (count nucleotide strand))))

(defn nucleotide-counts
  [strand]
  (reduce (nucleotide-reducer strand) {} valid-nucleotide))

#7

دسترسی که دادم! تو تنظیمات پروژه invitation فرستادم! ولی به هر حال مهم نیست.


اوه! چه خفن :sweat_smile:
ممنون بابت پاسخ کامل و طولانی و اصلاح کلی کد.

چند تا سوال.

  1. بزرگترین مشکل من که از زبانهای C based اومدم، اینه که نمیتونم سبک کد زدن کلوژری رو درک کنم. انجام تمرینها کمک کرده ولی کافی نیست. دارم کد‌های دیگران رو میخونم تا یه کم بیشتر دستگیرم بشه. منبع دیگه‌ای پیشنهاد میکنید؟ مثلا یه ریپازیتوری که کدهای اصولی و تمیز نوشته باشه :slight_smile:
  2. اینکه از threading macro خوشم نمیاد چیز بدیه؟ خوانایی کد معمولی به نظرم بیشتره. از این ماکرو‌ها زیاد استفاده میشه؟ (میدونم که چندتا از این مدل ماکرو‌ها داریم)

#8

همون کد خوندن بهترین راه هست اما نباید عجله کنی این داستان زمان می بره و همش به تجربه بر می گرده که با خوندن و نوشتن به دست میاد.

نه عجیب نیست و البته این رو بگم که به موقعش با این ماکرو ها هم ارتباط برقرار میکنی.

در کل اگر دوست داری خوشحال می شم به من تو hellhound کمک کنی


#9

تا همینجاشم که طول کشیده، سابقه‌ی درخشانمو خراب کرده :smile: عادت ندارم اینقدر گیج و گنگ بمونم تو یه زبان.

فعلا تا مرحله‌ی دوست داشتن میتونم پیش برم.
باید core functionها رو کامل یاد بگیرم و یه کم Clojurish تر کد بزنم. بعد میتونم بیام کدهای hellhound رو بخونم :nerd_face: فعلا سوادم در حد همین تاپیکه.