Я начинаю набрасывать какой-то код -
(define (find t q) ;; find q in t
(path empty) ;; the return path will be a list, we'll start it off as an empty list
(if (empty? t) ;; fundamental laws: always check for empty list first
#f ;; if the tree is empty, there is nothing to find, we use #f to signal this
(if (eq? q (car t)) ;; otherwise we can check if the node matches q ...
;; wups we can't do eq? test yet, it's possible `(car t)` is a list of nodes
))
Как мне это увидеть? Я смотрю на наш список ввода -
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
empty?
(car tree)
(cdr tree)
1
(2 (3) (4))
(5 (6 (7) (8)) (9))
eq?
и проверить, соответствует ли он q
сразу list?
, прежде чем попытаться eq?
Исправить мою бу-бу - [1161 ]
(define (find t q)
(path empty)
(if (empty? t)
#f
(if (list? (car t))
;; when the node is a list of nodes
(if (eq? q (car t))
;; when the node matches q
;; when the node does not match q
)))
Свернуть цепочки от if
до cond
для лучшей читаемости -
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when the node is a list of nodes
)
((eq? q (car t))
;; when the node matches q
)
(else
;; when the node does not match q
))
Код теперь более плоский и приятный для чтения. Некоторые из этих пробелов сложно заполнить, но меня тянет ко второму пробелу; когда q
равно (car t)
, это означает, что мы нашли совпадение, и пришло время вернуть path
-
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when the node is a list of nodes
;; we'll come back to this ...
)
((eq? q (car t))
(cons q path)) ;; return the path with the final node
(else
;; when the nodes does not match q
;; and save this for later too ...
))
Хорошо, это было не так уж плохо. Поэтому я проверил, когда (car t)
совпадает с q
, теперь я должен сказать, что происходит, когда он не совпадает. Когда (car t)
не совпадает, я добавлю его в path
и каким-то образом проверю, соответствует ли q
кому-либо из потомков узла, (cdr t)
-
(define (find t q)
(path empty)
(cond ((empty? t)
#f)
((list? (car t))
;; when node is a list of nodes
;; we'll come back to this ...
)
((eq? q (car t))
(cons q path))
(else
;; add the node to the path ...
(cons (car t) path)
;; check the node's children for a match
(find (cdr t) q)
;; this doesn't quite work ...
))
, который я запускаю в ситуации, когда нам нужно обновить path
новым узлом, и мне нужно вызвать find
, у которого нет параметра path
. Чтобы исправить это, я ввел цикл, который позволяет нам многократно оценивать выражение с любыми указанными нами аргументами -
(define (find t q)
(let loop ;; lazily and sloppily insert a named loop
((path empty) ;; initialize the parameters that will change
(t t))
(cond ((empty? t) ;; the expression to repeat, (cond ...)
#f)
((list? (car t))
;; when the node is a list of nodes
)
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path) ;; updated path
(cdr t)))) ;; updated tree
Предложение else
научило меня сопоставлять дочерние элементы узла, представляющие собой список узлов. Это, безусловно, облегчит работу с последним пробелом в коде, что и нужно делать, когда узел представляет собой список узлов! -
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
;; we could just recur the loop with
(loop path
(car t))
;; but what about (cdr t) in this case?
(loop path
(cdr t))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))
Последняя проблема здесь - у меня есть два (2) списка для проверки; (car t)
определяется как список, а (cdr t)
является списком. Я должен проверить их обоих. Простое решение состоит в том, чтобы объединить два loop
вызова с or
. Если один loop
вернет #f
, другой будет проверен -
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path ;; or represents dysjunction!
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))
Исправьте скобки, запустите автоматический индентор -
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(cons q path))
(else
(loop (cons (car t) path)
(cdr t))))))
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find tree 4)
;; '(4 2 1)
(find tree 8)
;; (8 6 5 1)
(find tree 9)
;; (9 5 1)
Обратите внимание, что результат обратный потому что действительно path
построен в обратном порядке. Условие выхода, которое возвращает путь, просто требует вызова reverse
перед возвратом -
(define (find t q)
(let loop
((path empty)
(t t))
(cond ((empty? t)
#f)
((list? (car t))
(or (loop path
(car t))
(loop path
(cdr t))))
((eq? q (car t))
(reverse (cons q path))) ;; don't forget to reverse!
(else
(loop (cons (car t) path)
(cdr t))))))
(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find tree 4)
;; '(1 2 4)
(find tree 8)
;; (1 5 6 8)
(find tree 9)
;; (1 5 9)
Мне нравится добавлять ссылку к NUnit в моих внешних инструментах.
Под Инструментами-> Внешние Инструменты добавляют NUnit
Title: &NUnit
Command: <path to nunit>
Arguments $(ProjectFileName) /run
Initial directory: $(ProjectDir)
После этого можно быстро выполнить его путем компиляции затем совершающего нападки alt-t + n
Да, это - в основном это. Лично я нахожу бегуна модульного теста, который идет с ReSharper, чтобы быть превосходным - и сам инструмент определенно стоит чувства лицензии. Кроме того, существует TestDriven.NET.
Наличие тестового проекта, который выполняет nunit-gui или nunit-консоль отдельно, очень хорошо, но Вы действительно хотите целый опыт поблочного тестирования быть максимально бесшовными. Чем легче записать и запустить тесты, тем более вероятно необходимо сделать это - который является очень хорошей вещью. Не недооценивайте постепенное наращивание разочарования из-за немного более плохого пользовательского опыта, зеркально отражающего между окнами и т.д.
NUnit - что-то, что не является внутренней Visual Studio 2008. Это действительно имеет консоль ИЛИ графический интерфейс пользователя (gui), который может быть выполнен оба вне VS2008 ИЛИ может быть присоединен к процессу VS2008 для отладки.
Если Вы действительно хотите что-то в VS2008, у Вас должно быть третье лицо, включающееся как ReSharper.
Править: Этому ответили в прошлом (не для VS2008 specificly, но все еще релевантному)
Я использовал TestDriven.NET с VS2005, и он изменился, как я разрабатываю и тестовый код.
Можно запустить все тесты на любом классе, модуле, проекте или решении. Можно также запустить тест в отладчике, который чрезвычайно полезен, чтобы диагностировать и устранить проблемы, когда они неожиданно возникают.
GUI хорош, но если Вы будете часто запускать свои тесты, то Вы, вероятно, откажетесь от него для более быстрого/интегрированного бегуна.
В любом случае у Вас есть некоторые опции о том, как запустить Ваши тесты:
Можно использовать сменный NUnitForVS, который доступен здесь: http://www.codeplex.com/NUnitForVS
Это интегрирует тестовое выполнение и приводит к Вашему IDE VS 2008. Мы использовали его в течение нескольких месяцев, и это работает хорошо на нас.
Вы также должны помнить, что с VS 2008 Professional вы можете использовать инструмент тестирования MS Unit раньше было доступно только в командных версиях.
] Вы можете запустить его как внешнюю программу, но для меня это не очень приятно. Мне нравится, когда тест начинается внутри VS. Итак, если у вас есть ReSharpe, вы можете зайти в Tools -> Options -> Environment -> Keyboard и установить горячую клавишу для ReSharper_UnitTest_ContextRun. Я установил ее в Ctrl + t.[
].