แก้ไขข้อบกพร่องใน Clojure? [ปิด]


227

วิธีที่ดีที่สุดในการดีบักโค้ด Clojure ในขณะที่ใช้งาน repl คืออะไร


นอกเหนือจากคำตอบด้านล่างโปรดดู 'เครื่องมือแก้จุดบกพร่องและเทคนิค' ในคู่มือ REPL: clojure.org/guides/repl/ ......
Valentin Waeselynck

คำตอบ:


158

นอกจากนี้ยังมี 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

2
ดี แต่คุณจะหา clojure เพื่อค้นหา 'clojure.contrib.trace ได้อย่างไร? ฉันมีขวดปิดตาบน classpath ของฉัน แต่ REPL พูดว่า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)
LarsH

2
คุณสามารถสะกดคำว่าปิดการสะกดคำเป็นปิดหรือว่าพิมพ์ผิดในความคิดเห็น? คุณสามารถโหลดไลบรารี clojure.contrib อื่น ๆ ได้หรือไม่?
John Lawrence Aspden

12
จาก 1.3 สิ่งนี้ได้ย้ายไปที่ clojure.tools.trace ( github.com/clojure/tools.trace )
George

4
หากคุณได้รับ: "IllegalStateException ไม่สามารถผูก var ไม่ใช่ไดนามิก" แบบไดนามิกดูที่นี่: stackoverflow.com/questions/8875353/…
คอร์นีเลียส

2
มันทำงานในรีลีส 1.5 ได้หรือไม่ ฉันกำลังเรียนรู้ Clojure กับ koans clojure แต่ไม่สามารถรับ dotrace เพื่อทำงานได้
nha

100

ฉันมีมาโครแก้จุดบกพร่องเล็กน้อยที่ฉันพบว่ามีประโยชน์มาก:

;;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เป็นจำนวนมากเช่น
Zaz

4
ได้ดียิ่งขึ้น: Spyscope
Zaz

@Zaz ฉันเห็นด้วยทั้งหมด Spyscope ยอดเยี่ยมมาก! อาจจะดีกว่าตัวดีบักก็ได้ แน่นอนสำหรับการพิมพ์
J Atkin

66

CIDER ของ Emacs มีตัวดีบักแหล่งที่มาซึ่งคุณสามารถขั้นตอนการแสดงออกโดยการแสดงออกภายในบัฟเฟอร์ Emacs และแม้กระทั่งฉีดค่าใหม่ คุณสามารถอ่านทั้งหมดเกี่ยวกับเรื่องที่นี่ ภาพหน้าจอตัวอย่าง:

ดีบัก CIDER


46

วิธีที่ฉันชอบคือการโปรยprintlnรหัสให้ทั่ว ... การเปิดและปิดเป็นเรื่องง่ายขอบคุณ#_มาโครผู้อ่าน (ซึ่งทำให้ผู้อ่านอ่านในรูปแบบต่อไปนี้แล้วแกล้งทำเป็นว่าไม่เคยเห็นมาก่อน) หรือคุณสามารถใช้มาโครขยายไปยังเนื้อความที่ส่งผ่านหรือnilขึ้นอยู่กับค่าของตัวแปรพิเศษบางตัวพูดว่า*debug*:

(defmacro debug-do [& body]
  (when *debug*
    `(do ~@body)))

ด้วยในการมีนี้จะขยายตัวออกไป(def *debug* false) nilด้วยtrueก็จะขยายตัวออกไปในห่อbodydo


คำตอบที่ยอมรับสำหรับคำถาม 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)
                   ^^^

5
อันที่จริงมีรุ่นของ debug-repl ที่ทำงานกับswank ได้แล้วในตอนนี้: hugoduncan.org/post/2010/ … (การแจ้งเตือนผู้สปอยเลอร์: มันยอดเยี่ยม)

1
ใช่และมันก็ดีที่มีลิงค์อยู่ที่นี่ขอบคุณ! ตกลงที่ยอดเยี่ยม :-)
Michał Marczyk

หากนี่เป็นสไตล์ของคุณคุณอาจชอบไลบรารี debux ที่กล่าวถึงในการตอบกลับในภายหลัง github.com/philoskim/debux
Mallory-Erik

@ Mallory-Erik ขอบคุณฉันจะตรวจสอบออก!
Michał Marczyk

37

นอกจากนี้คุณยังสามารถแทรกรหัสเพื่อวางตัวคุณเองลงใน 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


16

"วิธีที่ดีที่สุดในการ Debug Clojure code ขณะใช้งาน repl"

ฟิลด์ซ้ายเล็กน้อย แต่ 'ใช้ REPL ด้วยตนเอง'

ฉันเขียน Clojure ซึ่งเป็นงานอดิเรกมานานกว่าหนึ่งปีและไม่ได้รู้สึกว่าจำเป็นต้องใช้เครื่องมือการดีบักใด ๆ หากคุณทำให้ฟังก์ชั่นของคุณมีขนาดเล็กและรันแต่ละอันด้วยอินพุตที่คาดไว้ที่ REPL และสังเกตผลลัพธ์คุณควรมีภาพที่ชัดเจนว่าโค้ดของคุณทำงานอย่างไร

ฉันพบว่าตัวดีบั๊กมีประโยชน์มากที่สุดสำหรับการสังเกตสถานะในแอปพลิเคชันที่ทำงานอยู่ Clojure ทำให้มันง่าย (และสนุก!) ในการเขียนในรูปแบบการทำงานกับโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบ (ไม่มีการเปลี่ยนแปลงสถานะ) สิ่งนี้ช่วยลดความจำเป็นในการดีบักเกอร์อย่างหนาแน่น เมื่อฉันรู้ว่าส่วนประกอบทั้งหมดทำงานตามที่ฉันคาดหวัง (ให้ความสนใจเป็นพิเศษกับประเภทของสิ่งต่าง ๆ ) จากนั้นพฤติกรรมขนาดใหญ่ก็ไม่ค่อยมีปัญหา


สิ่งนี้เป็นจริงส่วนใหญ่ แต่เมื่อคุณเรียกซ้ำหลายฟังก์ชันเช่นนั้นไม่ใช่เรื่องง่าย
จอห์น

9

หากคุณใช้ 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

ตามที่ระบุไว้ในความคิดเห็นด้านบน


9

สำหรับ IntelliJ มีปลั๊กอิน Clojure ที่ยอดเยี่ยมชื่อCursiveเล่นหางเหนือสิ่งอื่นใดมันมี REPL ซึ่งคุณสามารถทำงานในโหมดแก้ไขข้อบกพร่องและก้าวผ่านรหัส Clojure ของคุณเช่นเดียวกับที่คุณต้องการเช่น Java

ฉันต้องการคำตอบของ Peter Westmacott ที่สองแม้ว่าในประสบการณ์ของฉันเพียงแค่รันโค้ดของฉันใน REPL เป็นส่วนใหญ่การดีบักในรูปแบบที่เพียงพอ


ฉันใช้ La Clojure กับความสำเร็จ แต่ดูเหมือนว่าจะตายเพราะเล่นหางตอนนี้github.com/JetBrains/la-clojure/blob/master/README.md
leeor

แต่จะแก้ไขข้อบกพร่องLeiningenได้อย่างไร:Error running 'ring server': Trampoline must be enabled for debugging
Gank

ที่ดูเหมือนจะเฉพาะringหรือlein- อาจคุ้มค่าการโพสต์คำถามแยกต่างหาก
dskrvk

6

ในปี 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


5

Hugo Duncan และผู้ทำงานร่วมกันยังคงทำงานที่น่าทึ่งกับโครงการritz Ritz-nreplเป็นเซิร์ฟเวอร์ nREPL ที่มีความสามารถในการตรวจแก้จุดบกพร่อง นาฬิกาของฮิวโก้แก้จุดบกพร่องใน Clojureพูดคุยที่ Clojure / conj ปี 2012 ที่จะเห็นมันในการดำเนินการในวิดีโอบางส่วนของภาพนิ่งไม่สามารถอ่านได้ดังนั้นคุณอาจต้องการที่จะดูภาพนิ่งจากที่นี่



1

มาจาก Java และคุ้นเคยกับ Eclipse ฉันชอบสิ่งที่ทวนเข็มนาฬิกา (ปลั๊กอิน Eclipse สำหรับการพัฒนา Clojure) มีให้: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code


ไม่มีการสนับสนุนเบรกพอยต์ที่เหมาะสม ด้ายยากที่จะฆ่า
CodeFarmer

1

นี่เป็นแมโครที่ดีสำหรับการแก้ไขข้อบกพร่องใน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)))

... และการเขียนเรียงความอธิบายการใช้งาน


-4

รุ่นฟังก์ชั่นของ 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)])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.