2015-06-03 8 views
6

che sto porting di uno script Python per Racket come esperienza di apprendimento, e ho questa funzione:Come faccio a rendere questo codice rack 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)) 

Il problema è che non è molto secco. Dal momento che sto utilizzando un Lisp, e Lisp è noto per essere in grado di fare cose folli, voglio sapere se c'è un modo per prendere tutto il codice sottoprocesso ed estrarlo in modo da poter fare qualcosa di simile:

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

e termina con un elenco dell'output di ciascun comando nell'elenco di comandi. Come lo farei? Dato che sono nuovo dell'intera cosa di Lisp/Scheme, sto cercando di capire la sintassi mentre vado e non sono pienamente consapevole delle risorse a mia disposizione.

+0

Questa sarebbe una buona domanda per [StackExchange CodeReview] (https://codereview.stackexchange.com/). –

+0

Questo ha già una risposta accettata, anche se @Jonathan è il benvenuto a prendere il suo codice di lavoro e postarlo alla revisione del codice, se lo desidera rivedere – Phrancis

+0

BTW, il codice di chiusura della porta non funzionerà: ''(ckot out in. ..) 'è una lista di simboli; è lo stesso di '(lista 'ckot' out 'in ...)'. Vuoi '(lista ckot out in ...)' invece. –

risposta

6

Prima di tutto, fa bene a te per voler trovare una soluzione più pulita! Hai ragione che esiste un modo più elegante per fare ciò che hai tentato.

Per iniziare, l'utilizzo di subprocess è quasi certamente eccessivo in una particolare situazione d'uso. Il modulo racket/system fornisce un'interfaccia più semplice che dovrebbe essere sufficiente per le tue esigenze. Nello specifico, utilizzerei la funzione system*, che esegue un singolo processo con gli argomenti forniti, quindi stampa il suo output su stdout.

Utilizzando system*, è possibile creare una funzione di supporto molto generale che può eseguire un comando per un particolare eseguibile e restituisce il suo output come una stringa.

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

Questa funzione si restituisce una nuova funzione quando si chiama. Questo significa che ad usarlo per chiamare un comando Git sarebbe simile a questa:

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

La ragione di questo diventerà evidente a breve.

In realtà guardando l'attuazione di execute-command, usiamo with-output-to-string per reindirizzare tutto l'output dalla chiamata system* in una stringa (invece di stamparlo a stdout). Questo è in realtà solo l'abbreviazione di utilizzo parameterize per impostare il parametro current-output-port, ma è più semplice.

Con questa funzione, possiamo implementare check-status molto facilmente.

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

Ora la ragione per avere (execute-command "git") ritorno una nuova funzione diventa evidente: possiamo usarlo per creare una funzione che poi mappare oltre l'elenco commands di produrre una nuova lista di stringhe.

Inoltre, si noti che la definizione dell'elenco commands utilizza solo un singolo ' all'inizio. La definizione fornita non funzionerebbe, e infatti l'elenco ports definito nella tua implementazione originale non è quello che ti aspetteresti. Questo perché '(...) è non esattamente come (list ...) -sono differenti, quindi state attenti quando li utilizzano.

+0

Awesome ! Grazie. Divertente, ho pensato che avrebbe avuto qualcosa a che fare con il ritorno di una funzione o di macro. – Jonathan