Domanda

OK, non barare ora.

No, davvero, un minuto o due e provare questo fuori.

Che cosa significa "posizioni" che fare?

Modifica:. Semplificata secondo il suggerimento di cgrand

(defn redux [[current next] flag] [(if flag current next) (inc next)])

(defn positions [coll]
  (map first (reductions redux [1 2] (map = coll (rest coll)))))

Ora, che ne dici di questa versione?

def positions(coll) {
  def (current, next) = [1, 1]
  def previous = coll[0]
  coll.collect {
    current = (it == previous) ? current : next
    next++
    previous = it
    current
  }
}

Sto imparando Clojure e me ne sono innamorato, perché mi è sempre piaciuto programmazione funzionale. Mi c'è voluto più tempo per trovare la soluzione Clojure, ma mi sono divertito dover che di una soluzione elegante. La soluzione Groovy è bene, ma io sono al punto in cui ho trovato questo tipo di programmazione imperativa noioso e meccanica. Dopo 12 anni di Java, mi sento in un solco e funzionale programmazione con Clojure è la spinta di cui avevo bisogno.

A destra, arrivare al punto. Beh, devo essere onesto e dire che mi chiedo se capirò il codice Clojure quando torno ad esso mesi più tardi. Sicuro che avrei potuto lasciare un commento, diamine fuori di esso, ma non ho bisogno di commentare il mio codice Java per capirlo.

Quindi la mia domanda è: si tratta di una questione di ottenere più abituati a modelli di programmazione funzionale? Sono guru di programmazione funzionali lettura di questo codice e trovando un gioco da ragazzi per capire? Quale versione ha fatto trova più facile da capire?

Modifica: che questo codice non fa altro che calcolare le posizioni dei giocatori in base ai loro punti, mentre tenere traccia di quelli che sono legati. Ad esempio:


Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
È stato utile?

Soluzione

Non credo che ci sia una cosa come la leggibilità intrinseca. C'è quello che si è abituati a, e quello che non sono abituati a. Sono stato in grado di leggere entrambe le versioni del codice OK. Ho potuto effettivamente leggere la versione Groovy più facilmente, anche se non so Groovy, perché anche ho passato un decennio guardando C e Java, e solo un anno guardando Clojure. Che non dice nulla circa le lingue, si dice solo qualcosa su di me.

Allo stesso modo posso leggere l'inglese più facilmente di spagnolo, ma che non dice nulla circa la leggibilità intrinseca di tali lingue sia. (Spagnolo è in realtà probabilmente il linguaggio "più leggibile" dei due in termini di semplicità e coerenza, ma ancora non in grado di leggerlo). Sto imparando giapponese in questo momento e avere un diavolo di un momento difficile, ma madrelingua giapponese dire lo stesso di inglese.

Se hai speso la maggior parte della vostra vita lettura Java, naturalmente le cose che assomigliano a Java sarà più facile da leggere rispetto le cose che non lo fanno. Fino a quando avete speso così tanto tempo guardando lingue Lispy come guardare linguaggi C-like, questo sarà probabilmente rimarrà fedele.

Per capire una lingua, tra le altre cose è necessario avere familiarità con:

  • sintassi ([vector] vs. (list), hyphens-in-names)
  • (vocabolario cosa significa reductions significa? Come / dove si può guardare in su?)
  • regole di valutazione (fa trattare le funzioni come oggetti di lavoro? E 'un errore nella maggior parte delle lingue.)
  • idiomi, come (map first (some set of reductions with extra accumulated values))

Tutte queste richiedono tempo e la pratica e la ripetizione di imparare e interiorizzare. Ma se si spendono i prossimi 6 mesi lettura e scrittura di un sacco di Clojure, non solo si sarà in grado di capire che il codice Clojure 6 mesi da oggi, probabilmente capiscono meglio di te ora, e forse anche in grado di semplificare la esso. Che ne dite di questo:

(use 'clojure.contrib.seq-utils)                                        ;;'
(defn positions [coll]
  (mapcat #(repeat (count %) (inc (ffirst %)))
          (partition-by second (indexed coll))))

Guardando il codice Clojure ho scritto un anno fa, sono inorridito quanto è fatto male, ma posso leggerlo OK. (Non dico il codice Clojure è orribile;. Ho dovuto leggere affatto alcuna difficoltà, e io non sono guru)

Altri suggerimenti

Sono d'accordo con Timothy: si introduce troppo astrazioni. Ho rielaborato il codice e si è conclusa con:

(defn positions [coll]
  (reductions (fn [[_ prev-score :as prev] [_ score :as curr]] 
                (if (= prev-score score) prev curr))
    (map vector (iterate inc 1) coll)))

A proposito di codice,

(defn use-prev [[a b]] (= a b))
(defn pairs [coll] (partition 2 1 coll))
(map use-prev (pairs coll))

può essere semplicemente riscritta come:

(map = coll (rest coll))

modifica:. non può essere più pertinente

L'unica Clojure è complicata per me. Esso contiene più astrazioni che hanno bisogno di essere capito. Questo è il prezzo di utilizzare funzioni di ordine superiore, dovete sapere che cosa significano. Quindi, in un caso isolato, richiede imperativo meno conoscenza. Ma il potere di astrazioni è nei loro mezzi di combinazione. Ogni ciclo imperativo deve essere letto e compreso, mentre astrazioni sequenza consentono di rimuovere la complessità di un ciclo e combinare opperations potenti.

mi sostengono inoltre che la versione Groovy è almeno parzialmente funzionale in quanto utilizza raccogliere, che è realmente mappa, una funzione di ordine superiore. Ha qualche stato in esso anche.

Ecco come avrei scritto la versione Clojure:

(defn positions2 [coll]
  (let [current (atom 1)
        if-same #(if (= %1 %2) @current (reset! current (inc %3)))]
    (map if-same (cons (first coll) coll) coll (range (count coll)))))

Questo è molto simile alla versione Groovy in quanto utilizza una "corrente" mutevole, ma si differenzia in quanto non dispone di una variabile / prev next - invece di usare sequenze immutabili per chi. Come Brian elloquently messo, la leggibilità non è intrinseca. Questa versione è la mia preferenza per questo caso particolare, e sembra di sedersi da qualche parte nel mezzo.

L'unica Clojure è più complicata a prima vista; anche se forse più elegante. OO è il risultato di rendere un linguaggio più "facilmente riconoscibili" a livello più alto. I linguaggi funzionali sembra avere una "algorithimc" di più (primitiva / elementare) si sentono ad essa. Questo è proprio quello che ho sentito in questo momento. Forse questo cambierà quando ho più esperienza di lavoro con clojure.

Ho paura che stiamo decending nel gioco di cui il linguaggio può essere il più conciso o risolvere un problema in meno riga di codice.

Il problema sono 2 pieghe per me:

  1. E 'facile a prima vista per avere un'idea di ciò che il codice sta facendo ?. Questo è importante per i manutentori di codice.

  2. Come è facile intuire qual è la logica che sta dietro il codice ?. Troppo prolisso / prolisso ?. Troppo conciso?

"rendere tutto il più semplice possibile, ma non più semplice".

Albert Einstein

Anche io sto imparando Clojure e amarla. Ma in questa fase del mio sviluppo, la versione Groovy era più facile da capire. Quello che mi piace di Clojure è però la lettura del codice e avere la "Ah!" esperienza quando finalmente "ottiene" ciò che sta accadendo. Quello che davvero godere è l'esperienza simile che accade a pochi minuti più tardi, quando ci si rende conto di tutti i modi in cui il codice potrebbe essere applicato ad altri tipi di dati, senza modifiche al codice. Ho perso il conto del numero di volte in cui ho lavorato attraverso un codice numerico in Clojure e poi, poco dopo, il pensiero di come lo stesso codice può essere utilizzato con le stringhe, i simboli, i widget, ...

L'analogia che uso è sui colori di apprendimento. Ricordate quando si hanno descritto il colore rosso? Hai capito abbastanza rapidamente - c'è tutta questa roba rossa in tutto il mondo. Allora avete sentito il termine magenta e sono stati persi per un po '. Ma ancora una volta, dopo un po 'di più l'esposizione, è capito il concetto e aveva un modo molto più preciso per descrivere un particolare colore. Bisogna interiorizzare il concetto, tenere un po 'più di informazioni nella tua testa, ma si finisce con qualcosa di più potente e conciso.

Groovy supporta diversi stili di risolvere anche questo problema:

coll.groupBy{it}.inject([]){ c, n -> c + [c.size() + 1] * n.value.size() }

sicuramente non riscritta essere abbastanza, ma non troppo difficile da capire.

So che questa non è una risposta alla domanda, ma sarò in grado di "capire" il codice molto meglio se ci sono test, come ad esempio:

assert positions([1]) == [1]
assert positions([2, 1]) == [1, 2]
assert positions([2, 2, 1]) == [1, 1, 3]
assert positions([3, 2, 1]) == [1, 2, 3]
assert positions([2, 2, 2, 1]) == [1, 1, 1, 4]

Questo mi, dirà un anno da ora, ciò che il codice si prevede di fare. Molto meglio di qualsiasi versione eccellente del codice che ho visto qui.

Sono I davvero fuori tema?

L'altra cosa è, penso che "leggibilità" dipende dal contesto. Dipende da chi manterrà il codice. Ad esempio, al fine di mantenere la versione "funzionale" del codice Groovy (per quanto brillante), ci vorranno non solo programmatori Groovy, ma i programmatori Groovy funzionali ... L'altro, più rilevante, ad esempio è il seguente: se un paio di righe di codice lo rendono più facile da capire per i "principianti" programmatori Clojure, allora il codice sarà complessiva sarà più leggibile perché sarà compreso da una comunità più ampia: non c'è bisogno di aver studiato Clojure per tre anni per essere in grado di cogliere il codice e apportare modifiche ad esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top