วิธีที่ดีที่สุดในการดีบักโค้ด Clojure ในขณะที่ใช้งาน repl คืออะไร
วิธีที่ดีที่สุดในการดีบักโค้ด Clojure ในขณะที่ใช้งาน repl คืออะไร
คำตอบ:
นอกจากนี้ยังมี dotrace ซึ่งให้คุณดูอินพุตและเอาต์พุตของฟังก์ชันที่เลือก
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
ผลิตผลลัพธ์:
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
ใน Clojure 1.4 dotrace
ได้ย้าย:
คุณต้องพึ่งพา:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
และคุณต้องเพิ่ม ^: ไดนามิกเข้ากับนิยามฟังก์ชัน
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
จากนั้นบ๊อบก็เป็นลุงของคุณอีกครั้ง:
(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)
ฉันมีมาโครแก้จุดบกพร่องเล็กน้อยที่ฉันพบว่ามีประโยชน์มาก:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
คุณสามารถแทรกมันทุกที่ที่คุณต้องการดูว่าเกิดอะไรขึ้นและเมื่อ:
;; 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
เป็นจำนวนมากเช่น
วิธีที่ฉันชอบคือการโปรยprintln
รหัสให้ทั่ว ... การเปิดและปิดเป็นเรื่องง่ายขอบคุณ#_
มาโครผู้อ่าน (ซึ่งทำให้ผู้อ่านอ่านในรูปแบบต่อไปนี้แล้วแกล้งทำเป็นว่าไม่เคยเห็นมาก่อน) หรือคุณสามารถใช้มาโครขยายไปยังเนื้อความที่ส่งผ่านหรือnil
ขึ้นอยู่กับค่าของตัวแปรพิเศษบางตัวพูดว่า*debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
ด้วยในการมีนี้จะขยายตัวออกไป(def *debug* false)
nil
ด้วยtrue
ก็จะขยายตัวออกไปในห่อbody
do
คำตอบที่ยอมรับสำหรับคำถาม SO นี้: Idiomatic Clojure สำหรับการรายงานความคืบหน้า? มีประโยชน์มากเมื่อทำการดีบักการดำเนินการตามลำดับ
จากนั้นก็มีอะไรบางอย่างที่เป็นอยู่ในปัจจุบันไม่เข้ากันกับยกตนข่มท่าน-Clojure 's REPL debug-repl
แต่เป็นสิ่งที่ดีเกินไปที่จะไม่เอ่ยถึง: คุณสามารถใช้มันใน REPL แบบสแตนด์อโลนซึ่งเป็นเรื่องง่ายที่จะได้รับเช่นกับ Leiningen ( lein repl
); และถ้าคุณกำลังเปิดตัวโปรแกรมของคุณจากบรรทัดคำสั่งก็จะนำ REPL ของตัวเองขึ้นมาในเทอร์มินัลของคุณ แนวคิดก็คือคุณสามารถวางdebug-repl
แมโครลงในที่ใดก็ได้ตามต้องการและนำมาแทนที่ REPL เมื่อการดำเนินการของโปรแกรมถึงจุดนั้นโดยที่คนในพื้นที่ทั้งหมดอยู่ในขอบเขตเป็นต้นลิงก์ที่เกี่ยวข้องสองสามตัว: Clojure debug-repl , Clojure debug เทคนิค -repl , วิธี 'การแข่งขันการแก้ปัญหา-repl (บนกลุ่ม Clojure Google) ตรวจแก้จุดบกพร่องบน repl Clojars
swank-clojure ทำงานได้อย่างเพียงพอในการทำให้ดีบักเกอร์ในตัว SLIME มีประโยชน์เมื่อทำงานกับรหัส Clojure - สังเกตว่าบิตที่ไม่เกี่ยวข้องของ stacktrace นั้นเป็นสีเทาดังนั้นจึงง่ายต่อการค้นหาปัญหาที่แท้จริงในรหัสที่กำลังดีบั๊ก สิ่งหนึ่งที่ต้องคำนึงถึงก็คือฟังก์ชั่นที่ไม่ระบุตัวตนซึ่งไม่มี "แท็กชื่อ" ปรากฏในสแต็คแท็บโดยที่ไม่มีข้อมูลที่เป็นประโยชน์ติดอยู่ เมื่อมีการเพิ่ม "แท็กชื่อ" แท็กชื่อจะปรากฏในสแต็กแท็กและทุกอย่างดีอีกครั้ง:
(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)
^^^
นอกจากนี้คุณยังสามารถแทรกรหัสเพื่อวางตัวคุณเองลงใน REPL ที่มีการเชื่อมโยงท้องถิ่นโดยใช้Alex Osborne'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))))
จากนั้นใช้แทรกทุกที่ที่คุณต้องการให้รีสตาร์ท
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
ฉันติดสิ่งนี้ใน user.clj ดังนั้นมันจึงมีอยู่ในทุกช่วง REPL
"วิธีที่ดีที่สุดในการ Debug Clojure code ขณะใช้งาน repl"
ฟิลด์ซ้ายเล็กน้อย แต่ 'ใช้ REPL ด้วยตนเอง'
ฉันเขียน Clojure ซึ่งเป็นงานอดิเรกมานานกว่าหนึ่งปีและไม่ได้รู้สึกว่าจำเป็นต้องใช้เครื่องมือการดีบักใด ๆ หากคุณทำให้ฟังก์ชั่นของคุณมีขนาดเล็กและรันแต่ละอันด้วยอินพุตที่คาดไว้ที่ REPL และสังเกตผลลัพธ์คุณควรมีภาพที่ชัดเจนว่าโค้ดของคุณทำงานอย่างไร
ฉันพบว่าตัวดีบั๊กมีประโยชน์มากที่สุดสำหรับการสังเกตสถานะในแอปพลิเคชันที่ทำงานอยู่ Clojure ทำให้มันง่าย (และสนุก!) ในการเขียนในรูปแบบการทำงานกับโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบ (ไม่มีการเปลี่ยนแปลงสถานะ) สิ่งนี้ช่วยลดความจำเป็นในการดีบักเกอร์อย่างหนาแน่น เมื่อฉันรู้ว่าส่วนประกอบทั้งหมดทำงานตามที่ฉันคาดหวัง (ให้ความสนใจเป็นพิเศษกับประเภทของสิ่งต่าง ๆ ) จากนั้นพฤติกรรมขนาดใหญ่ก็ไม่ค่อยมีปัญหา
หากคุณใช้ emacs / slime / swank ให้ลองทำสิ่งนี้ที่ REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
มันไม่ได้ทำให้คุณมีร่องรอยเต็มสแต็คเหมือนคุณอยู่ภายใต้ LISP แต่มันดีสำหรับการจิ้ม ๆ
นี่คือผลงานที่ดีของ:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
ตามที่ระบุไว้ในความคิดเห็นด้านบน
สำหรับ IntelliJ มีปลั๊กอิน Clojure ที่ยอดเยี่ยมชื่อCursiveเล่นหางเหนือสิ่งอื่นใดมันมี REPL ซึ่งคุณสามารถทำงานในโหมดแก้ไขข้อบกพร่องและก้าวผ่านรหัส Clojure ของคุณเช่นเดียวกับที่คุณต้องการเช่น Java
ฉันต้องการคำตอบของ Peter Westmacott ที่สองแม้ว่าในประสบการณ์ของฉันเพียงแค่รันโค้ดของฉันใน REPL เป็นส่วนใหญ่การดีบักในรูปแบบที่เพียงพอ
Leiningen
ได้อย่างไร:Error running 'ring server': Trampoline must be enabled for debugging
ring
หรือlein
- อาจคุ้มค่าการโพสต์คำถามแยกต่างหาก
ในปี 2559 คุณสามารถใช้Debuxซึ่งเป็นไลบรารีการดีบักอย่างง่ายสำหรับ Clojure / Script ที่ทำงานร่วมกับผู้จำลองของคุณรวมถึงคอนโซลของเบราว์เซอร์ของคุณ คุณสามารถโรยdbg
(debug) หรือclog
แมโคร (console.log) ในรหัสของคุณและสังเกตผลลัพธ์ของฟังก์ชั่นแต่ละอย่างได้อย่างง่ายดาย ฯลฯ พิมพ์ไปที่ REPL และ / หรือคอนโซลของคุณ
จากReadmeของโครงการ:
การใช้งานขั้นพื้นฐาน
นี่เป็นตัวอย่างง่ายๆ แมโคร dbg พิมพ์รูปแบบดั้งเดิมและพิมพ์ค่าที่ประเมินในหน้าต่าง REPL จากนั้นจะส่งคืนค่าโดยไม่รบกวนการเรียกใช้โค้ด
ถ้าคุณหุ้มโค้ดด้วย dbg เช่นนี้
(* 2 (dbg (+ 10 20))) ; => 60
สิ่งต่อไปนี้จะถูกพิมพ์ในหน้าต่าง REPL
เอาท์พุท REPL:
dbg: (+ 10 20) => 30
dbg ที่ซ้อนกัน
แมโคร dbg สามารถซ้อนกันได้
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
เอาท์พุท REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan และผู้ทำงานร่วมกันยังคงทำงานที่น่าทึ่งกับโครงการritz Ritz-nreplเป็นเซิร์ฟเวอร์ nREPL ที่มีความสามารถในการตรวจแก้จุดบกพร่อง นาฬิกาของฮิวโก้แก้จุดบกพร่องใน Clojureพูดคุยที่ Clojure / conj ปี 2012 ที่จะเห็นมันในการดำเนินการในวิดีโอบางส่วนของภาพนิ่งไม่สามารถอ่านได้ดังนั้นคุณอาจต้องการที่จะดูภาพนิ่งจากที่นี่
ใช้ spyscope ซึ่งใช้แมโครตัวอ่านแบบกำหนดเองเพื่อให้รหัสการดีบักของคุณเป็นรหัสการผลิต https://github.com/dgrnbrg/spyscope
มาจาก Java และคุ้นเคยกับ Eclipse ฉันชอบสิ่งที่ทวนเข็มนาฬิกา (ปลั๊กอิน Eclipse สำหรับการพัฒนา Clojure) มีให้: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
นี่เป็นแมโครที่ดีสำหรับการแก้ไขข้อบกพร่องในlet
รูปแบบที่ซับซ้อน:
(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)))
รุ่นฟังก์ชั่นของ def-let ซึ่งเปลี่ยนให้เป็นชุดของ defs เครดิตบางส่วนไปที่นี่
(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)))))
การใช้งาน: จำเป็นต้องอ้างเนื้อหาด้วยใบเสนอราคาเช่น
(def-let '[a 1 b 2 c (atom 0)])