클로저에서 디버깅? [닫은]


227

repl을 사용하는 동안 Clojure 코드를 디버깅하는 가장 좋은 방법은 무엇입니까?


아래 답변 외에도 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)

그리고 함수 정의에 ^ : dynamic을 추가해야합니다

(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.contrib.trace를 찾습니까? 클래스 경로에 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)
클로저

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/…
Cornelius

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

Emacs의 CIDER에는 Emacs 버퍼 내부의 표현식으로 표현을 수행하고 새로운 값을 주입 할 수있는 소스 디버거가 있습니다. 여기에서 모든 내용을 읽을 수 있습니다 . 데모 스크린 샷 :

CIDER 디버그


46

내가 가장 좋아하는 방법은 println코드 전체에 자유로이 뿌리는 것입니다 . #_독자 매크로 덕분 코드를 켜고 끄는 것이 쉽습니다 (리더를 다음과 같은 형식으로 읽은 다음 본 적이없는 척). 또는 전달 된 본문으로 확장하거나 nil특수 변수의 값에 따라 매크로를 사용할 수 있습니다 *debug*.

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

(def *debug* false)안에 있으면으로 확장됩니다 nil. 으로는 true, 그것은 확장 것이다 bodyA의 포장 do.


이 SO 질문에 대한 대답 : 진행 상황보고를위한 관용적 Clojure? 시퀀스 작업을 디버깅 할 때 매우 유용합니다.


그런 다음 swank-clojure 의 REPL 과 현재 호환되지 않는 것이 있지만 언급하지 않는 것이 좋습니다 debug-repl. Leiningen ( lein repl) 을 사용하면 쉽게 얻을 수있는 독립 실행 형 REPL에서 사용할 수 있습니다 . 명령 줄에서 프로그램을 시작하면 터미널에 자체 REPL이 표시됩니다. 아이디어는 당신이 떨어질 수 있다는 것입니다 debug-repl당신처럼 어디에서 매크로를하고 자신의 REPL을 가지고있을 때 범위에있는 모든 주민 등 관련 링크의 부부와 함께 프로그램의 실행에 도달 그 시점 : Clojure의 디버그 - REPL , Clojure의 디버그 -repl 트릭 , 어떻게 '디버그 - REPL의 한판 승부 합니다 (Clojure의 구글 그룹에), Clojars에 디버그 REPL .


swank-clojure는 Clojure 코드로 작업 할 때 SLIME의 내장 디버거를 유용하게 만드는 적절한 작업을 수행합니다. 스택 트레이스의 관련없는 비트가 회색으로 표시되어 디버깅되는 코드에서 실제 문제를 쉽게 찾을 수 있습니다. 명심해야 할 것은 "이름 태그"가없는 익명 함수는 기본적으로 유용한 정보가 첨부되지 않은 상태로 스택 트레이스에 나타납니다. "이름 태그"가 추가되면 스택 추적에 나타나고 모두 다시 잘 나타납니다.

(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
실제로 swank 와 함께 작동하는 debug-repl 버전이 있습니다 : hugoduncan.org/post/2010/… (스포일러 경고 : 대단합니다)

1
맞습니다. 여기에 링크를 두는 것이 좋습니다. 감사합니다! 굉장히 동의했습니다. :-)
Michał Marczyk

이것이 당신의 스타일이라면, 후속 응답에서 언급 된 debux 라이브러리를 좋아할 것입니다. github.com/philoskim/debux
Mallory-Erik

@ Mallory-Erik 감사합니다. 확인해 보겠습니다!
Michał Marczyk

37

Alex Osborne의debug-repl 다음을 사용하여 코드를 삽입하여 모든 로컬 바인딩으로 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))))

그런 다음 사용하려면 repl을 시작하려는 위치에 삽입하십시오.

(defn my-function [a b c]
  (let [d (some-calc)]
    (debug-repl)))

내 user.clj 에이를 붙여서 모든 REPL 세션에서 사용할 수 있습니다.


16

"repl을 사용하는 동안 Clojure 코드를 디버깅하는 가장 좋은 방법"

약간 왼쪽 필드이지만 'REPL 항목 사용'.

나는 1 년 넘게 취미 애호가 클로저를 작성해 왔으며 디버깅 도구가 크게 필요하지 않았습니다. 함수를 작게 유지하고 REPL에서 예상되는 입력으로 각 기능을 실행하고 결과를 관찰하면 코드의 작동 방식을 매우 명확하게 파악할 수 있습니다.

디버거가 실행중인 응용 프로그램에서 STATE를 관찰하는 데 가장 유용하다는 것을 알았습니다. Clojure를 사용하면 변경 불가능한 데이터 구조로 상태를 변경하지 않고도 기능적인 스타일로 쉽게 작성할 수 있습니다. 이것은 디버거의 필요성을 크게 줄입니다. 모든 구성 요소가 예상대로 작동한다는 것을 알면 (사물 유형에 특히주의를 기울임) 대규모 동작은 거의 문제가되지 않습니다.


이것은 대부분 사실이지만 예를 들어 여러 함수에서 재귀가 발생하는 경우 쉽지 않습니다.
John


9

IntelliJ에는 Cursive 라는 훌륭한 Clojure 플러그인이 있습니다. 무엇보다도 디버그 모드에서 실행하고 Java와 같이 Clojure 코드를 단계별로 실행할 수있는 REPL을 제공합니다.

Peter Westmacott의 대답을 두 번째로 생각하지만 REPL에서 내 코드 조각을 실행하는 것은 대부분 충분한 형태의 디버깅입니다.


나는 La Clojure를 성공적으로 사용하고 있었지만, 현재는 github.com/JetBrains/la-clojure/blob/master/README.md
leeor에서 15:15

그러나로 디버깅하는 방법 Leiningen은 다음 과 같습니다.Error running 'ring server': Trampoline must be enabled for debugging
Gank

그것은 구체적 ring이거나 lein아마도 별도의 질문을 게시 할 가치가 있는 것 같습니다 .
dskrvk 2018

6

2016 년 현재 , Rebu 및 브라우저 콘솔과 함께 작동하는 Clojure / Script 용 간단한 디버깅 라이브러리 인 Debux 를 사용할 수 있습니다 . 코드에 dbg(디버그) 또는 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





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의 함수 버전으로, 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.