2015-06-03 2 views
6

Я портирование сценарий Python, чтобы Ракетка как опыт обучения, и у меня есть эта функция:Как я могу сделать этот код Racket DRYer?

(define (check-status) 
    (define git [find-executable-path "git"]) 
    (define-values (ckot out in err) 
     (subprocess #f #f #f git "checkout" "-q" "master")) 
    (define-values (local lout lin lerr) 
     (subprocess #f #f #f git "rev-parse" "@")) 
    (define-values (remote rout rin rerr) 
     (subprocess #f #f #f git "rev-parse" "@{u}")) 
    (define-values (merge-base mbout mbin mberr) 
     (subprocess #f #f #f git "merge-base" "@" "@{u}")) 
    (display-lines (port->lines mbout)) 
    (define ports '(ckot out in err local lout lin lerr remote rout rin rerr merge-base mbout mbin mberr)) 
    (map (lambda (x) 
     (cond ((input-port? x) (close-input-port x)) 
       ((output-port? x) (close-output-port x)))) ports)) 

Проблема заключается в том, что это не очень DRY. Так как я использую Lisp, и Lisp известен тем, что в состоянии делать сумасшедшие вещи, я хочу знать, если есть способ взять весь код подпроцесс и извлечь его так, что я могу сделать что-то вроде:

(define (check-status) 
    (define commands '(
         '("checkout" "-q" "master") 
         '("rev-parse" "@") 
         '("rev-parse" "@{u}") 
         '("merge-base" "@" "@{u}")) 
    (map currently-immaginary-git-command-fn commands)) 

и в итоге выведите список результатов каждой команды в списке команд. Как мне это сделать? Поскольку я новичок во всей работе Lisp/Scheme, я выясняю синтаксис, когда я иду, и я не полностью осведомлен о доступных мне ресурсах.

+0

Это был бы хороший вопрос для [StackExchange CodeReview] (https://codereview.stackexchange.com/). –

+0

У этого уже есть принятый ответ, хотя @Jonathan может принять его рабочий код и опубликовать его в Code Review, если он хочет, чтобы он просмотрел – Phrancis

+0

BTW, код закрытия порта не будет работать: ''(ckot out in. ..) '- это список символов; это то же самое, что '(list 'ckot' out 'in ...)'. Вместо этого вы хотите '(list ckot out in ...)'. –

ответ

6

Прежде всего, хорошо для вас, если вы хотите придумать более чистое решение! Вы правы, что есть более элегантный способ сделать то, что вы пытались.

Для начала использования subprocess почти наверняка будет излишним в вашем конкретном прецеденте. Модуль racket/system обеспечивает более простой интерфейс, который должен быть достаточным для ваших нужд. В частности, я бы использовал функцию system*, которая выполняет один процесс с предоставленными аргументами, а затем выводит свой вывод на stdout.

Используя system*, можно создать очень общую вспомогательную функцию, которая может выполнять команду для конкретного исполняемого файла и возвращает его вывод в виде строки.

(define (execute-command proc-name) 
    (define proc (find-executable-path proc-name)) 
    (λ (args) 
    (with-output-to-string 
    (thunk (apply system* proc args))))) 

сами эта функция возвращает новой функции при вызове. Это означает, что с помощью его вызова команды Git будет выглядеть следующим образом:

((execute-command "git") '("checkout" "-q" "master")) 

Причиной этого станет очевидным в ближайшее время.

На самом деле, глядя на реализацию execute-command, мы используем with-output-to-string перенаправить весь вывод из system* вызова в строку (вместо того, чтобы просто напечатать его на стандартный вывод). Это фактически аббревиатура для использования parameterize для установки параметра current-output-port, но это проще.

С этой функцией мы можем очень легко реализовать check-status.

(define (check-status) 
    (define commands 
    '(("checkout" "-q" "master") 
     ("rev-parse" "@") 
     ("rev-parse" "@{u}") 
     ("merge-base" "@" "@{u}"))) 
    (map (execute-command "git") commands)) 

Теперь причина для возвращения (execute-command "git") новая функция становится очевидным: мы можем использовать, чтобы создать функцию, которая будет затем сопоставьте над commands список для создания нового списка строк.

Также обратите внимание, что в определении списка commands используется только один ' в начале. Предоставленное вами определение не будет работать, и фактически список ports, который вы определили в своей первоначальной реализации, не то, что вы ожидаете. Это потому, что '(...)не точно так же, как (list ...)-они разные, поэтому будьте осторожны при их использовании.

+0

Awesome ! Благодарю. Забавно, я подумал, что это будет связано с возвратом функции или макросов. – Jonathan

Смежные вопросы