Category Archives: Codice dalla vita vera

Fondamenti di programmazione in Python per ingegneri

Cosa aspettarsi da questo articolo

Parto dal presupposto che, a mio avviso, chiunque lavori con i computer deve saper programmare almeno un minimo.
Scopo di questa mini-lezione non è formare grandi programmatori (io programmo da circa dieci anni e sono lontano anni luce da essere un grande programmatore) ma darvi le basi per poter scrivere un piccolo script.
Questa guida non si occupa di problemi di prestazioni e performance, problemi di scrittura e leggibilità del codice, teoria del codice e quant’altro. Vi fornisco solo elementi di utilizzo immediato: chi di voi diventerà “più esperto” e supererà questa mini-lezione probabilmente non utilizzerà mai più niente di tutto ciò che c’è scritto qui. Ma il fatto è che quanto state per imparare funziona e funziona subito. Livello di difficoltà prossima allo zero.
Ovviamente con le conoscenze qui sotto potrete al più scrivere dei piccoli script imperativi e nient’altro. Quanto basta per inserire “pezzi di codice” in programmi di uso comune per l’ingegneria.

Cosa significa scrivere un programma

Un programma è essenzialmente un insieme di istruzioni che il vostro computer riceve ed esegue. Linguaggi di programmazione “seri” hanno la possibilità di definire quale sia il punto d’ingresso del programma, in che tempo e con quali risorse devono essere eseguite le varie istruzioni e altre cose di questo tipo.

Un programma viene generalmente scritto in un editor di testo (anche il blocco note di windows) e poi passato ad un compilatore. Il compilatore ha il compito di convertire il vostro file (o insieme di file) in quello che definiamo bytecode. Cosa sia il bytecode se vi interessa ve lo cercate su internet, anche perchè è molto interessante sapere perché serve e quali vantaggi offre una “compilazione intermedia”… insomme, dovreste aver sentito la parola Java almeno una volta! Ma quello che conta sapere è che il vostro codice non viene eseguito dalla macchina così com’è, ma subisce prima alcune trasformazioni. In realtà una volta convertito in bytecode il vostro codice deve essere convertito in linguaggio macchina prima di poter essere realmente eseguito.

Noi ci occuperemo di illustrare in seguito i principi di base della cosiddetta programmazione imperativa, ovvero una forma di programmazione in cui essenzialmente si mettono in fila, disposte per riga, le diverse istruzioni che si vuole il computer esegua. Le istruzioni vengono compilate (ed eseguite) dall’alto verso il basso. In questo modo possiamo pensare l’intero codice come un unico algoritmo.

Giusto per incuriosirvi rispetto a qualcosa di cui non parleremo: la programmazione imperativa è oggi sostanzialmente abbandonata. Oggi si utilizzano paradigmi di programmazione assai diversi – il più diffuso è forse il paradigma OOP, in cui sostanzialmente si programma mettendo in risalto le qualità dei dati rispetto alle routine (Object Oriented Programming). Ma allora perchè fare una lezione sulla programmazione imperativa? Semplice: l’OOP è qualcosa di decisamente evoluto, che pone le sue basi sul concetto di astrazione dei tipi. Qualcosa che esula completamente dal nostro scopo e che richiederebbe troppo tempo.

La caratteristica dei programmi di cui tratteremo è:

  1. Tutto il codice è contenuto in un unico file
  2. Non viene fatto riferimento a stream, persistenza dei dati
  3. Non vengono presi in considerazione problemi di astrazione
  4. Non vengono fatti riferimenti a librerie esterne (quasi mai)

Il primo punto spiega un po’ la filosofia: vi voglio dare solo gli elementi che vi consentano di scrivere piccoli script da inserire dentro Dynamo o Rhyno… non ha senso spiegarvi come gestire progetti veri e propri. Non vi insegnerò neppure come salvare file o caricare dati esternamente. Anche se questo sarebbe interessante (per esempio ingrare un foglio Excel con Dynamo e Python non sarebbe male), ma richiede conoscenze più avanzate e un’approfondimento di concetti che non toccherò neppure. Altresì per l’astrazione, che essenzialmente mi imporrebbe di spiegare l’OOP o almeno le funzioni. Non vi spiegherò neppure come fare grafici o utilizzare strumenti scritti da terze parti, tranne il minimo indispensabile per utilizzare alcune funzioni matematiche di base (come seno e coseno).

Ok, iniziamo.

Python. Cos’è e primo avvio

Python è il linguaggio più semplice in assoluto. Possiamo dire che non ha neppure una vera grammatica, sostanzialmente scriverete esattamente quello che volete che il computer faccia.

A causa di questa semplicità e al fatto che defacto Python non viene neppure compilato (ma interpretato), Python viene utilizzato come linguaggio di programmazione per scriptare dentro molti programmi. E’ veloce e semplice. Questo vi basta.

Potete installare l’interprete Python (che a sua volta è scritto in linguaggio C) da qui: http://www.python.it/download/ – scegliete la versione 2.7 perchè su Dynamo in realtà quello che eseguirete non è Python ma IronPython (cosa cambia? a voi niente) che è basato sulla versione 2.7 di Python. Dico questo perchè sebbene quello che diremo in questa sede vale per tutte le versioni di Python, se deciderete di andare avanti è utile avere una versione perfettamente adatta ai vostri scopi.

In alternativa potete installare Canopy, che fornisce una versione di Python che include al suo interno numerose librerie che potrebbero esservi utili: https://www.enthought.com/products/canopy/

Ad installazione completata, se non ci sono intoppi troverete fra i vostri programmi “Idle”, che non è altro che una console Ppythonintroython JIT. Significa che voi scrivete del codice Python, premete invio, e questo viene eseguito. E’ lo step prima dello script. Tutta la prima parte di questo corso, per spiegare un po’ come funziona, sarà eseguita su Idle.

Ad esempio io ho scritto:

print("hello world")

e Python ha risposto “hello world”. Il comando print() produce la stampa dell’argomento.

Non fate caso alla mia versione di Python: voi scaricate la 2.7!

Fondamenti

Variabili

Provate a scrivere:

nome = "Cristiano"

e premete invio. Come vedete Python non stampa niente, ma ha memorizzato la stringa “Cristiano” dentro la variabile nome. Se ora digitate

print(nome)

otterrete la stampa del mio nome.
Altresì potete scrivere:

nome = "Cristiano"
print(nome) #Stampa: Cristiano
nome = "Filippo"
print(nome) #Stampa: Filippo
nome = "Mario"
nome = "Giulio"
print(nome) #Stampa: Giulio

Come vedete le istruzioni vengono eseguite dall’alto verso il basso, nell’ordine di scrittura.
Possiamo anche concatenare più stringhe:

nome = "Mario"
cognome = "Rossi"
print(nome + cognome) # MarioRossi
print(nome + " " + cognome) #Mario Rossi

Notate bene:

  1. Tutto ciò che segue il carattere # non viene considerato parte del codice, si tratta dunque di un commento del programmatore.
  2. I commenti e le stringhe devono essere su una linea singola
  3. Ogni linea deve contenere una sola istruzione
  4. Le stringhe devono essere contenute dentro le virgolette “”

Numeri

Potete immaginare facilmente come eseguire delle operazioni matematiche e quali “cose strane” possono capitare:

i = 5
j = 2
print(i+j) # 7
print(2*3) # 6
i = "5"
j = "2"
print(i+j) #stampa 52 (i e j vengono considerati stringhe: sono fra virgolette)
print(i*3) #stampa 555
print("ciao"*2) #stampa ciaociao

Liste (matrici)

Le variabili viste fino ad ora possono contenere solo un elemento per volta. Se le riassegnate perdete il valore precedente (abbiamo già visto degli esempi). E’ possibile creare e gestire una lista in questo modo:

mesi = [] #definisce una matrice
mesi.append("Gennaio") #aggiunge un primo elemento (indice 0)
mesi.append("Febbraio") #aggiunge un elemento in coda alla lista
mesi.append("marzo") #aggiunge un elemento in coda alla lista
mesi[2] = "Marzo" #modifica l'elemento con indice '2', cioè il terzo elemento
mesi.pop(2) #rimuove l'elemento con indice 2
mesi.append("Aprile")
mesi.insert(2, "Marzo") #inserisce marzo in terza posizione facendo scalare aprile in quarta

Vi ho mostrato 3 metodi dell’oggetto list.Un metodo è essenzialmente una funzione a cui accedete tramite l’operatore “.” che segue il nome di una variabile che fa riferimento ad un oggetto.

La tecnica del “.” fa riferimento al paradigma OOP (che non vedremo), vi basta sapere che per accedere alle funzionalità delle liste, create con [] dovete inserire il nome della variabile della lista seguita dal “.”. I metodi utilizzati sono:

  1. append(variabile) – aggiunge l’argomento alla lista (per valore)
  2. pop(index) – estrae dalla lista l’elemento numero index. se non impostate index, estrae l’ultimo inserito
  3. insert(index, var) – inserisce l’elemento in una posizione specifica

Questi tre sono i metodi base per le liste. Ovviamente ce ne sono molti di più, una ricerca su internet vi basterà per scoprirlo: https://developers.google.com/edu/python/lists

Altri metodi per istanziare e lavorare sulle liste

Potete provare anche istruzioni di questo tipo:

spesa = ["Carote", "Latte", "Miele"]
conteggio = len(spesa)
print(conteggio) #3
spesa.append("Biscotti")
conteggio2 = len(spesa)
print(conteggio) #3
print(conteggio2) #4

E’ anche possibile creare matrici di matrici ed eseguire operazioni su matrici:

vettore = [[[1],[0],[0]],[[0],[1],[0]],[[0],[0],[1]]]
mischio = ["mischio", "testo", "a", 3, "numeri"]
list1 = [1,2,3]
list2 = [4,5,6]
list1+list2 #[1,2,3,4,5,6]
sum(list1) #6

Insomma, spero di aver reso l’idea. Ulteriori metodi li trovare online. Basta cercare qualcosa come “intersezione liste con Python”, magari in inglese. Andate su Google e provate a cercare qualcosa come “how to intersect lists python” – troverete svariati post (in genere quelli che puntano sul sito stackoverflow forniscono risposte rapide ed attendibili).

Iterazioni

Il modo più semplice per iterare in una lista è illustrato in questo esempio. Digitate su Idle:

list = ["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"]
for giorno in list:
	print("oggi è " + giorno)
print("Domani che giorno sarà?")

Come vedete la funzione print è indentata di un TAB dopo l’istruzione for. Questo comunica a Python che l’istrzione fa riferimento al blocco for, mentre “Domani che giorno sarà” no. Se non fosse presente il TAB, Python restituirebbe errore, infatti il blocco for richiede almeno un istruzione.
Notate la differenza scrivendo:

list = ["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"]
for giorno in list:
	print("oggi è " + giorno)
	print("Domani che giorno sarà?")

Spiegazione del ciclo for

for è una parola riservata del Python. Nessuna variabile può chiamarsi in questo modo, in effetti “for” è una vera e propria istruzione. Il blocco for così come scritto indica che la variabile di iterazione “giorno” deve essere desunta dall’iterazione dentro la lista di nome “list”. La variabile “giorno” continua ad esistere anche fuori dal ciclo. Provate a scrivere:

print(giorno) #domenica

Questo comportamento può sembrarvi ovvio, in realtà non lo è e porta numerosi problemi (che non vedremo). Si dice, per chi volesse approfondire, che Python ha gestito globalmente lo “scope” della variabile.
Possiamo iterare in una lista anche per indice:

for i in range(0, len(list)):
	print(list[i])

Selezione (istruzione if)

Il costrutto if consente di eseguire una parte di codice solo se è vera la condizione del blocco if e alternativamente eseguirne un’altra. Ad esempio il codice seguente non produce nessun output

nome = "mario"
if nome == "cristiano":
	print("benvenuto")

La parola riservata else consente di inserire codice alternativo:

nome = "mario"
if nome == "cristiano":
	print("benvenuto")
else
	print("chi sei?")

Operatori booleani

Essenzialmente Python verifica il contenuto dell’istruzione contenuta in if, se vera esegue il blocco. Se è presente il blocco else e l’istruzione è falsa, viene eseguito questo.

Il blocco if viene eseguita se l’operatore restituisce il valore booleano True. I principi della logica booleana non fanno parte di questa lezione, ma pensate a “True” come “+” e “False” come “-” in matematica. Due “frasi” vere restituiscono vero, due frasi false restituiscono vero, una falsa e una vera, falso. Gli operatori di confronto sono:

  • a == b #vero se a è identico a b. Attenzione al doppio “=”. Il singolo “=” significa assegnazione.
  • a != b #vero se a è diverso da b
  • a < b, a > b, a<=b, a >= b #provate a immaginare…
  • Potete anche correlare più frasi con “and” o “or”

Quindi provate a digitare per esempio:

for giorno in list:
	print("Che bella giornata")
	if giorno != "Sabato" and giorno != "Domenica":
		print("Oggi ci si riposa")
	else:
		print("Oggi si lavora")
	print("... perchè oggi è " + giorno)

E adesso?

Questo ultimo programmino contiene tutti gli elementi che a mio avviso sono fondamentali.
Con queste informazioni ritengo possiate orientarvi e iniziare a scrivere qualche script per conto vostro. O aiutandovi con internet.

Sicuramente queste informazioni vi basteranno per iniziare a lavorare su Dynamo. Su YouTube ho caricato un video che utilizza Python per attraversare delle liste e selezionare determinati elementi: è un esempio pratico che vi consiglio di seguire per sostanziare quanto detto.

  • Questa non è una guida a Python, è solo un’introduzione. Io penso che chi vuole imparare questa roba possa facilmente farlo facendo due ricerche su internet e sbattendoci la testa sopra. Non avrei neppure il tempo per fare un maxi spiegone su Python. Peraltro spiegare i fondamentali è anche noioso e nei pochi articoli su questo blog mi sono concentrato su temi un po’ più interessanti e che un minimo invitano a ragionare… Diciamo che ho scritto questa paginetta per darvi gli strumenti per partire da soli 😉

Per i più temerari

  1. Un corso di Python di 13 ore online: https://www.codecademy.com/learn/python
  2. Il libro “Imparare Python” di Lutz è la base per imparare davvero a programmare in Python. Il libro costa una sessantina di euro, ma se volete essere dei pro, è l’inizio: https://www.amazon.it/Imparare-Python-Mark-Lutz/dp/8848125956/ref=sr_1_1?ie=UTF8&qid=1473362244&sr=8-1&keywords=imparare+python – una vola letto e appreso, il passo successivo sarà “Programmare Python”, sempre di Lutz. Questo secondo testo vi porterà a scrivere applicazioni più impegnative, magari anche piccoli giochi o programmi con una grafica decente.
  3. Python fornisce numerose librerie utili per numerosi scopi. Fra queste segnalo pyplot (qui: http://matplotlib.org/api/pyplot_api.html). Pyplot vi consente di stampare a video il grafico di qualsiasi funzione, provateci un po :). Ogni libreria è fornita di apposita documentazione che vi fornisce la descrizione dei vari comandi che potete eseguire. Prendete dimestichezza con queste “API” (Application Programming Interface) percé è il modo in cui i programmatori dicono ad altri programmatori come fare le cose. Ma attenzione: la vostra installazione di Python non andrà bene per questo scopo. Per usare Pyplot il modo più semplice sarà procurarsi Canopy: https://www.enthought.com/products/canopy/

Trovare la lettera successiva ad un’altra

Lavorando con le griglie capita spesso di trovarsi in una situazione in cui le righe vengono rappresentate con dei numeri e le colonne con delle lettere. In un processo di automazione, mentre è semplice numerare i numeri appare più complessa l’operazione sulle lettere. Infatti, ad esempio, dopo la lettera Z si trova AB e così via…
Un procedimento come questo non funzionerà, poichè nella tabella ASCII il numero 27 corrisponde al simbolo “[“:

int number = 27;
Console.WriteLine((char)('A' + number));

Una soluzione semplice

Le varie soluzioni che si possono proporre si dividono fra quelle che includono un dizionario e quelle matematiche. Propongo la soluzione matematica scritta in C# che utilizza il concetto di resto:

		private string IntToLetters(int num)
		{
			string result = string.Empty;
			while (num >= 0)
			{
				result = (char)('A' + num % 26) + result;
				num /= 26; num--;
			}
			return num;
		}

Il metodo parte da 0 (dove A => 0) e procede all’infinito. Ovviamente è possibile anche effettuare l’operazione inversa, anche se la logica è un po’ più complessa:

		private static int LettersToInt(string value)
		{
			int result = 0;
			List<char> digits = new List<char>("ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray());
			char[] chars = value.ToUpper().ToCharArray();
			int weight = chars.Length;
			for (int position = 0; position < chars.Length; position++)
			{
				int currentVal = digits.FindIndex(x => x == chars[position]);
				weight = chars.Length - position;
				int r = (weight - 1) * (currentVal + 1) * 26 + currentVal;
				result += r;
			}
			return result;
		}

…Devo ammettere che ci ho messo un bel po’ per ricavare la funzione qui sopra 🙂

Utilità del prodotto cartesiano

Definizione

Il prodotto cartesiano non è altro che l’insieme delle coppie ordinate di due insiemi. Ad esempio se ho due insiemi:
A = {a,b,c}
B = {d,e,f}
AxB = {(a,d),(b,d),(c,d),(c,e),(c,f)

Per tre, invece:
A = {a,b}
B = {c,d}
C = {e,f}
AxBxC = {(a,c,e),(a,d,e),(a,c,f),(a,d,f),(b,c,e),(b,d,e),(b,c,f),(b,d,f)}

Per i database il prodotto cartesiano dei dati è il modo naturale per unire due set differenti.

cartesianoLa caratteristica del prodotto cartesiano risiede nel modo di determinare la griglia: è sufficiente conoscerne gli elementi degli assi. L’immagine seguente, realizzata in Python, mostra proprio il prodotto cartesiano fra i punti di 3 assi, appunto, cartesiani.

Il prodotto cartesiano non voluto

Un esempio di prodotto cartesiano non desiderato è facilmente riscontrabile nell’esempio del negozio, riporto un mock semplificato di due tabelle:

Account:
+-----------------+--------+
+ NOME		  + ACCOID +
+-----------------+--------+
+ Cristiano 	  + 1      +
+ Mario		  + 2      +
++++++++++++++++++++++++++++
Ordini:
+-----------------+---------+
+ ORDINI	  + ACCOID  +
+-----------------+---------+
+ 100€		  + 1	    +
+ 50€		  + 1 	    +
+ 70€		  + 1	    +
+ 50€		  + 1 	    +
+ 30€		  + 2	    +
+ 10€		  + 2 	    +
+++++++++++++++++++++++++++++

Eseguendo una query SQL del tipo

SELECT * FROM Account, Ordini

si ottiene proprio il prodotto cartesiano fra le due, che non è probabilmente il risultato che si vuole ottenere! (Per intenderci otterremo 12 righe).

+-----------------+-------------+
+ ORDINI	  + NOME	+
+-----------------+-------------+
+ 100€		  + Cristiano   +
+ 50€		  + Cristiano   +
+ 70€		  + Cristiano   +
+ 50€		  + Cristiano   +
+ 30€		  + Cristiano   +
+ 10€		  + Cristiano   +
+ 100€		  + Mario	+
+ 50€		  + Mario	+
+ 70€		  + Mario	+
+ 50€		  + Mario	+
+ 30€		  + Mario	+
+ 10€		  + Mario	+
+++++++++++++++++++++++++++++++++

Il motivo per cui la query non produce il risultato che vogliamo, è che viene valutato di default il prodotto cartesiano fra le tabelle interrogate (operazione di CROSS JOIN): l’output è proprio l’insieme di tutte le possibili combinazioni di account e ordini, e non un insieme con il nome dell’account all’intero del db. Si dice che il JOIN cerca il percorso più lungo (o globale) per unire le due tabelle.

SELECT * FROM Account NATURAL JOIN Ordini

In questo modo si otterrà invece il risultato desiderato: i parametri con lo stesso nome e valore verranno utilizzati come fonte per l’unione. La colonna ACCOID verrà riprodotta una sola volta.

+-----------------+-----------------+
+ ORDINI	  + ACCOID	    +
+-----------------+-----------------+
+ 100€		  + Cristiano	    +
+ 50€		  + Cristiano	    +
+ 70€		  + Cristiano	    +
+ 50€		  + Cristiano 	    +
+ 30€		  + Mario	    +
+ 10€		  + Mario	    +
+++++++++++++++++++++++++++++++++++++

Lo scopo di questo articolo non è presentare le JOIN, motivo per cui non mi soffermerò ulteriormente su altri esempi di questo tipo o su come modificare la query nel caso in cui ci siano colonne da unire con nomi diversi.

Quando è utile

In realtà le applicazioni del prodotto cartesiano sono moltissime. L’esempio più chiaro è nella creazione di grafici e report, ovvero ogni qual volta la combinazione di due valori deve dare un unica coppia. Ad esempio:

SELECT Negozi.Negozio, Prodotti.Prodotto FROM Negozi CROSS JOIN Prodotti

In questo modo avremo per ogni negozio i prodotti disponibili in una matrice.

Per fare un altro esempio, la prossima volta che dovete creare tutte le combinazioni di data e ora, potreste fare il prodotto cartesiano fra ore e minuti invece che innestare un ciclo dentro un altro.

App per daltonici: individuare il nome di colore dal valore RGB

Tempo fa mi sono cimentato nello sviluppo di una semplicissima app per daltonici il cui unisco scopo era quello di individuare il nome del colore dominante in una foto. In pratica l’utente che avesse scaricato l’applicazione avrebbe dovuto scattare una foto e la mia app gli avrebbe detto di che colore era.

Il senso? Il mio amico daltonico Davide non può comprarsi vestiti da solo: deve sempre chiedere di che colore sia questo o quel pantalone/maglietta o qualsiasi altra cosa. Non può neppure cucinarsi un hamburger da solo perché non riesce a capire se la carne è cotta dal colore (in realtà potrebbe assaggiarla – ma lui dice che non è lo stesso, giustamente).

Classico esempio del problema della luce
Classico esempio del problema della luce

Dopo alcuni test ho realizzato che effettuare una fotografia, per quanto sia la scelta più intuitiva, non è la scelta migliore per raggiungere il mio obiettivo – infatti il colore della luce influenza il colore percepito dall’occhio della camera – cosicché un colore può apparire in un modo ma essere un altro. La scelta giusta sarebbe utilizzare uno spettrometro… giustamente non incluso in nessuno smartphone, così l’idea si è arenata dopo qualche test sul prototipo.

In ogni caso è stato interessante porsi questa domanda: come posso risalire al nome di un colore semplicemente dal suo valore RGB?

Attacco al problema

La cosa chiara fin da subito è che c’è bisogno di una lista di valori noti a cui confrontare il valore RGB ignoto. Una volta ottenuta la lista, ci si può chiedere (ed è la domanda chiave): come effettuo il confronto?

Sarebbe da pazzi e probabilmente inutile confrontare i singoli valori della terna con quelli disponibili, magari all’interno di uno switch lunghissimo, per ottenere qualcosa… Già solo provarci è da pazzi.

La soluzione più rapida e quella di immaginare la nostra tripletta RGB all’interno dello spazio tridimensionale di assi [(R,0,0),(0,G,0),(0,0,B)]. A questo punto ogni colore non è altro che un vettore! E come sapete passare ad un PC un oggetto matematico è davvero la cosa più facile e veloce.

Allora il procedimento sarà proprio questo. Bisognerà creare una lista di vettori del nostro spazio RGB (che poi è uno spazio tridimensionale normato) contenente i colori che ci vengono in mente (inclusi i grigi) in modo che la distribuzione sia “abbastanza uniforme”. A questo punto prendiamo il vettore-colore sconosciuto e calcoliamo la distanza pitagorica del vettore con quelli in lista. Il nome del colore sarà quello del vettore meno distante. Formulato in JavaScript:

DefaultColors = [
  new Color(0,0,0, "nero"),
  new Color(255,255,255, "bianco"),
  new Color(255,0,0, "rosso"),
  new Color(0,255,0, "verde"),
  new Color(0,0,255, "blue"),
  new Color(255,255, 0, "giallo"),
  new Color(255, 128, 0, "arancione"),
  new Color(102,102,102, "ocra"),
  new Color(75, 153, 0, "verde scuro"),
  new Color(0, 204, 102, "verde acqua"),
  new Color(0, 204,204, "celeste"),
  new Color(0, 51, 102, "blu scuro"),
  new Color(153, 153,255, "lilla"),
  new Color(102, 0, 102, "viola"),
  new Color(204, 0, 204, "fucsia"),
  new Color(255, 152, 255, "lilla"),
  new Color(153, 0, 76, "porpora"),
  new Color(96,96,96, "grigio"),
  new Color(192,192,192, "grigio chiaro"),
  new Color(102, 51, 0, "marrone"),
  new Color(153, 102, 51, "marrone chiaro"),
  new Color(204, 204, 0, "giallo verde"),
  new Color(204, 51, 0, "terra rossa"),
  new Color(51, 153, 51, "smeraldo"),
  new Color(102, 153, 153, "azzurro petrolio"),
  new Color(255, 204, 204, "rosa"),
  new Color(0, 51, 204, "blu nitido")]

function Color(red, green, blue, name) {
  this.Red = red
  this.Green = green
  this.Blue = blue
  if(name != null) { this.Name = name }

  this.Distance = function(color) {
      return Math.sqrt(this.Pow(this.Red - color.Red) + this.Pow(this.Green - color.Green) + this.Pow(this.Blue - color.Blue))
  }

  this.GetApproxName = function () {
      var min = null
      if (this.Name != undefined) return this.Name
      for (i in DefaultColors) {
          if (this.Distance(DefaultColors[i]) &lt; min || min == null) {
              min = this.Distance(DefaultColors[i])
              this.Name = DefaultColors[i].Name
          }
      }
      return this.Name
  }

  this.Darkness = function () {
      toBlackDistance = this.Distance(DefaultColors[0]);
      maxDistance = DefaultColors[0].Distance(DefaultColors[1])
      ratio = 1 - toBlackDistance / maxDistance;
      return ratio.toFixed(2);
  } 
  
  this.Lightness = function () {
      toWhiteDistance = this.Distance(DefaultColors[1]);
      maxDistance = DefaultColors[0].Distance(DefaultColors[1])
      ratio = 1 - toWhiteDistance / maxDistance;
      return  ratio.toFixed(2);
  }

  this.Pow = function(what) {
      return what*what
  }
}

L’esecuzione per esempio potrebbe essere:

var cc = new Color(102, 0, 51)
console.log("nome approx:" + cc.GetApproxName())
console.log("chiarezza:" + cc.Lightness())
console.log("scurezza:" + cc.Darkness())

Ho anche inserito un paio di metodi per scoprire quanto un colore sia chiaro o scuro. Potrebbe esservi utile nel caso vogliate adattare il colore del testo allo sfondo (nel qual caso nella grigli dei colori basterà lasciare anche solo bianco e nero.

Considerazioni finali

Ovviamente lo script è tanto più preciso quanto più la distribuzione dei colori noti (ovvero i vettori della matrice) è egualmente distribuita nello spazio.

Non è il caso del mio esempio: per una migliore (e più lunga) implementazione bisognerebbe dividere ogni asse in, per esempio, 10 parti e quindi effettuare il prodotto cartesiano fra i punti trovati. Adesso si deve inserire per ogni terna il nome del colore corrispondente (a mano!) e avere così lo script ottimizzato.