Câu trả lời:
Ngoài ra còn có dotrace, cho phép bạn xem xét đầu vào và đầu ra của các chức năng được chọn.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
tạo ra đầu ra:
TRACE t4425: (fib 3)
TRACE t4426: | (fib 2)
TRACE t4427: | | (fib 1)
TRACE t4427: | | => 1
TRACE t4428: | | (fib 0)
TRACE t4428: | | => 0
TRACE t4426: | => 1
TRACE t4429: | (fib 1)
TRACE t4429: | => 1
TRACE t4425: => 2
2
Trong Clojure 1.4, dotrace
đã di chuyển:
Bạn cần sự phụ thuộc:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
Và bạn cần thêm ^: động vào định nghĩa hàm
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Sau đó, Bob là một lần nữa chú của bạn:
(clojure.tools.trace/dotrace [fib] (fib 3))
TRACE t4328: (fib 3)
TRACE t4329: | (fib 2)
TRACE t4330: | | (fib 1)
TRACE t4330: | | => 1
TRACE t4331: | | (fib 0)
TRACE t4331: | | => 0
TRACE t4329: | => 1
TRACE t4332: | (fib 1)
TRACE t4332: | => 1
TRACE t4328: => 2
user=> (use 'closure.contrib.trace) java.io.FileNotFoundException: Could not locate closure/contrib/trace__init.class or closure/contrib/trace.clj on classpath: (NO_SOURCE_FILE:0)
Tôi có một macro gỡ lỗi mà tôi thấy rất hữu ích:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Bạn có thể chèn nó bất cứ nơi nào bạn muốn xem những gì đang diễn ra và khi nào:
;; Examples of dbg
(println (+ (* 2 3) (dbg (* 8 9))))
(println (dbg (println "yo")))
(defn factorial[n] (if (= n 0) 1 (* n (dbg (factorial (dec n))))))
(factorial 8)
(def integers (iterate inc 0))
(def squares (map #(dbg(* % %)) integers))
(def cubes (map #(dbg(* %1 %2)) integers squares))
(take 5 cubes)
(take 5 cubes)
clojure.tools.trace/trace
.
Phương pháp ưa thích của tôi là rắc rắc tự do println
lên tất cả các mã ... Bật và tắt chúng dễ dàng nhờ #_
macro người đọc (khiến người đọc đọc theo hình thức sau, sau đó giả vờ rằng nó chưa bao giờ nhìn thấy nó). Hoặc bạn có thể sử dụng macro mở rộng sang phần thân truyền hoặc nil
tùy thuộc vào giá trị của một số biến đặc biệt, giả sử *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Với một (def *debug* false)
trong đó, điều này sẽ mở rộng đến nil
. Với true
, nó sẽ mở rộng để body
bọc trong một do
.
Câu trả lời được chấp nhận cho câu hỏi SO này: Clojure thành ngữ cho báo cáo tiến độ? là rất hữu ích khi gỡ lỗi hoạt động trình tự.
Sau đó, có một cái gì đó hiện không tương thích với REPL của swank-clojure , nhưng quá tốt để không đề cập đến : debug-repl
. Bạn có thể sử dụng nó trong REPL độc lập, dễ dàng lấy ví dụ với Leiningen ( lein repl
); và nếu bạn đang khởi chạy chương trình của mình từ dòng lệnh, thì nó sẽ đưa REPL của chính nó lên ngay trong thiết bị đầu cuối của bạn. Ý tưởng là bạn có thể thả debug-repl
macro ở bất cứ nơi nào bạn thích và để nó tự bật REPL khi thực thi chương trình đạt đến điểm đó, với tất cả các địa phương trong phạm vi, v.v ... Một vài liên kết có liên quan: Gỡ lỗi Clojure , gỡ lỗi Clojure -có các thủ thuật , làm thế nào để gỡ lỗi (trên nhóm Clojure Google), gỡ lỗi trên Clojars .
swank-clojure thực hiện một công việc thích hợp để làm cho trình gỡ lỗi tích hợp sẵn của SLIME trở nên hữu ích khi làm việc với mã Clojure - lưu ý cách các bit không liên quan của stacktrace bị mờ đi để dễ dàng tìm ra vấn đề thực sự trong mã được gỡ lỗi. Một điều cần lưu ý là các hàm ẩn danh không có "thẻ tên" xuất hiện trong stacktrace mà về cơ bản không có thông tin hữu ích nào được đính kèm; khi một "thẻ tên" được thêm vào, nó sẽ xuất hiện trong stacktrace và tất cả lại hoạt động tốt:
(fn [& args] ...)
vs.
(fn tag [& args] ...)
example stacktrace entries:
1: user$eval__3130$fn__3131.invoke(NO_SOURCE_FILE:1)
vs. ^^
1: user$eval__3138$tag__3139.invoke(NO_SOURCE_FILE:1)
^^^
Bạn cũng có thể chèn mã để thả mình vào REPL với tất cả các ràng buộc cục bộ, sử dụng Alex Ostern'sdebug-repl
:
(defmacro local-bindings
"Produces a map of the names of local bindings to their values."
[]
(let [symbols (map key @clojure.lang.Compiler/LOCAL_ENV)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(declare *locals*)
(defn eval-with-locals
"Evals a form with given locals. The locals should be a map of symbols to
values."
[locals form]
(binding [*locals* locals]
(eval
`(let ~(vec (mapcat #(list % `(*locals* '~%)) (keys locals)))
~form))))
(defmacro debug-repl
"Starts a REPL with the local bindings available."
[]
`(clojure.main/repl
:prompt #(print "dr => ")
:eval (partial eval-with-locals (local-bindings))))
Sau đó, để sử dụng nó, chèn nó bất cứ nơi nào bạn muốn thay thế bắt đầu:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Tôi gắn cái này vào user.clj của mình để nó có sẵn trong tất cả các phiên REPL.
"cách tốt nhất để gỡ lỗi mã Clojure, trong khi sử dụng thay thế"
Hơi bên trái trường, nhưng 'sử dụng bản thân REPL'.
Tôi đã viết Clojure sở thích trong hơn một năm và không cảm thấy cần một công cụ sửa lỗi nào. Nếu bạn giữ các chức năng của mình nhỏ và chạy từng chức năng với các đầu vào dự kiến tại REPL và quan sát kết quả thì có thể có một bức tranh khá rõ ràng về cách hoạt động của mã của bạn.
Tôi thấy một trình gỡ lỗi là hữu ích nhất để quan sát STATE trong một ứng dụng đang chạy. Clojure giúp bạn dễ dàng (và vui vẻ!) Để viết theo kiểu chức năng với các cấu trúc dữ liệu bất biến (không thay đổi trạng thái). Điều này ồ ạt làm giảm nhu cầu sửa lỗi. Một khi tôi biết rằng tất cả các thành phần hoạt động như tôi mong đợi (đặc biệt chú ý đến các loại sự vật) thì hành vi quy mô lớn hiếm khi là một vấn đề.
Nếu bạn sử dụng emacs / slime / swank, thì hãy thử điều này tại REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Nó không cung cấp cho bạn một dấu vết ngăn xếp đầy đủ như bạn nhận được trong LISP, nhưng nó tốt cho việc chọc ngoáy.
Đây là công việc tốt của:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_en môi.xhtml
như đã được đề cập trong một bình luận ở trên.
Đối với IntelliJ, có một plugin Clojure tuyệt vời có tên là Cursive . Trong số những thứ khác, nó cung cấp REPL mà bạn có thể chạy trong chế độ gỡ lỗi và chuyển qua mã Clojure giống như bạn làm, ví dụ như Java.
Tôi sẽ trả lời câu trả lời của Peter Westmacott mặc dù theo kinh nghiệm của tôi, việc chạy các đoạn mã của tôi trong REPL hầu hết là một dạng gỡ lỗi đầy đủ.
Leiningen
, nó cho thấy:Error running 'ring server': Trampoline must be enabled for debugging
ring
hoặc lein
- có lẽ đáng để đăng một câu hỏi riêng biệt?
Kể từ năm 2016, bạn có thể sử dụng Debux , một thư viện gỡ lỗi đơn giản cho Clojure / Script hoạt động cùng với bản thay thế cũng như bảng điều khiển của trình duyệt của bạn. Bạn có thể rắc macro dbg
(gỡ lỗi) hoặc clog
(console.log) trong mã của mình và dễ dàng quan sát kết quả của các chức năng riêng lẻ, v.v., được in ra REPL và / hoặc bảng điều khiển của bạn.
Từ Readme của dự án :
Sử dụng cơ bản
Đây là một ví dụ đơn giản. Macro dbg in một biểu mẫu gốc và in đẹp giá trị được đánh giá trên cửa sổ REPL. Sau đó, nó trả về giá trị mà không can thiệp vào việc thực thi mã.
Nếu bạn bọc mã bằng dbg như thế này,
(* 2 (dbg (+ 10 20))) ; => 60
sau đây sẽ được in trong cửa sổ REPL.
Đầu ra REPL:
dbg: (+ 10 20) => 30
Dbg lồng
Macro dbg có thể được lồng nhau.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Đầu ra REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan và các cộng tác viên tiếp tục làm công việc tuyệt vời với dự án ritz . Ritz-nrepl là một máy chủ nREPL với khả năng gỡ lỗi. Xem Debuggers của Hugo trong Clojure nói chuyện tại Clojure / Conj 2012 để thấy nó hoạt động, trong video một số slide không thể đọc được vì vậy bạn có thể muốn xem các slide từ đây .
Sử dụng spyscope thực hiện macro trình đọc tùy chỉnh để mã gỡ lỗi của bạn cũng là mã sản xuất https://github.com/dgrnbrg/spyscope
Đến từ Java và quen thuộc với Eclipse, tôi thích những gì Ngược chiều kim đồng hồ (plugin Eclipse để phát triển Clojure) phải cung cấp: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Đây là một macro đẹp để gỡ lỗi các let
hình thức phức tạp :
(defmacro def+
"def with binding (def+ [{:keys [a b d]} {:a 1 :b 2 :d 3}])"
[bindings]
(let [let-expr (macroexpand `(let ~bindings))
vars (filter #(not (.contains (str %) "__"))
(map first (partition 2 (second let-expr))))
def-vars (map (fn [v] `(def ~v ~v)) vars)]
(concat let-expr def-vars)))
Phiên bản chức năng của def-let, biến một let thành một loạt defs. Một số tín dụng đi đến đây
(defn def-let [aVec]
(if-not (even? (count aVec))
aVec
(let [aKey (atom "")
counter (atom 0)]
(doseq [item aVec]
(if (even? @counter)
(reset! aKey item)
(intern *ns* (symbol @aKey) (eval item)))
; (prn item)
(swap! counter inc)))))
Cách sử dụng: Cần trích dẫn nội dung bằng một trích dẫn, vd
(def-let '[a 1 b 2 c (atom 0)])