giovedì 30 gennaio 2025

ANDROID STUDIO - Ridurre la RAM dell'emulatore

Utilizzare Android Studio su un computer con Windows 11 e 8 giga di RAM è possibile, anche se bisogna scendere a qualche compromesso.

Contesto
Partiamo del contesto: mantenendo aperti solo alcuni programmi (pochi), la RAM utilizzata si aggira attorno al 56% del totale.
Android Studio "mangia" da solo circa 1,5 GB di RAM ed avviare un qualsiasi "device virtualizzato" di fatto ammazza il PC (a parte rari casi...), che praticamente smette di rispondere ai comandi finché non si riesce (a fatica) a chiudere l'AVD e poi Android Studio.

Soluzione
C'è però una soluzione di compromesso: abbassare la RAM utilizzata dall'AVD.

Di base, questo valore non è modificabile dall'interfaccia, ma si può modificare dentro i file di configurazione del dispositivo virtualizzato.

Per prima cosa, creiamo un nuovo device virtualizzato con le caratteristiche che vogliamo. Nel mio caso, ho simulato il mio cellulare personale con Android 13 (quindi Android SDK Platform 33).

A questo punto, bisogna cliccare sul burger menu, quindi su Tools e poi su Device Manager. Si aprirà un riquadro a destra.
Bisogna cliccare sui tre pallini accanto al nuovo device virtualizzato e quindi su Show on disk.
Una volta aperta la directory con i file del device, bisogna:

  1. cancellare (se presente) la cartella hardware-qemu.ini.lock
  2. aprire il file hardware-qemu.ini, trovare la riga hw.ramSize e cambiare il valore (io ho impostato 2048)
  3. aprire il file config.ini, trovare la riga hw.ramSize e cambiare il valore (io ho impostato 2048)

A questo punto, dopo aver riavviato Android Studio per caricare le modifiche, controllando i dettagli del device virtualizzato (identificate il device sotto Device Manager, cliccate sui tre pallini e selezionate View Details) dovreste trovare il nuovo quantitativo di RAM che avete impostato.

La macchina quindi si avvierà con la RAM specificata, che così non andrà ad appesantire il sistema operativo. Ovvio che risulterà un po' inchiodata... però almeno il sistema continuerà a rispondere ai comandi!

mercoledì 29 gennaio 2025

JAVASCRIPT - Replace di tutte le istanze trovate

Con javascript o jquery capita a volte di dover utilizzare la funzione .replace() per cambiare il contenuto di qualche stringa.
Il problema è che il funzionamento "base" prevede la modifica solo della prima "istanza" trovata, ed il resto viene lasciato inalterato.

Per eseguire il replace su tutte le istanze presenti nella stringa, bisogna utilizzare un'espressione regolare:
const stringToEdit = 'tom;jack;jones';
const newString = stringToEdit.replace(/;/g, '  |  ');

giovedì 23 gennaio 2025

SQL SERVER - arrotondare i DATETIME

In SQL Server, per arrotondare i DATETIME si utilizzano le funzioni DATEADD e DATEDIFF, in pratica si sottrae il tipo di valore che si vuole arrotondare a 0 (minuti, secondi, ore, etc).

Questo è un esempio:

DECLARE @dt DATETIME

SET @dt = '09-22-2007 15:07:38.850'

SELECT DATEADD(mi, DATEDIFF(mi, 0, @dt), 0)     -- ritorna 2007-09-22 15:07:00.000
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, @dt), 0) -- ritorna 2007-09-22 15:00:00.000

mercoledì 22 gennaio 2025

PYTHON - Uscire da 2 o più loop annidati

Facendo esercizi di programmazione (soprattutto Advent of Code e CodeWars) mi sono imbattuto in alcuni casi in cui, trovato un valore -ad esempio in una matrice- sarei dovuto uscire da due loop annidati.

Guardando in rete ci sono parecchi metodi per effettuare questa operazione (uso di try/catch, refactoring della funzione in modo che a quel punto esca dalla stessa, etc), però quello che mi è parso più sensato è l'utilizzo di una variabile di controllo.

Prendiamo ad esempio la ricerca della prima coppia di numeri contenuti in una lista che sono perfettamente divisibili (quindi senza resto) tra loro, con lo scopo di trovare il risultato di questa divisione.

Il codice è questo:

result = -1
el = [9, 4, 5, 7, 12, 3]

# variabile di controllo
found = False

for i in range(len(el)):
    for j in range(i + 1, len(el)):
        if el[i] % el[j] == 0:
            result = el[i] // el[j]
            found = True
            break
    if found:
        break

print(result)

Utilizziamo la variabile found per indicare che il valore è stato trovato e mettiamo dentro ogni ciclo un controllo seguito da un break.
In questo modo, una volta trovato il risultato siamo sicuri di uscire da entrambi i cicli.

PYTHON - Split senza elementi vuoti

Ci sono dei casi in cui usare la funzione .split() può generare dei valori "vuoti" all'interno della lista prodotta.

In alcuni casi, questo è utile e quell'elemento va mantenuto. Potrebbe però esserci la necessità di processare solo i valori presenti in una stringa e magari in questa stringa ci sono degli "spazi di troppo"... lo split (che non utilizza espressioni regolari) non riesce a distinguere un singolo carattere " " (uno spazio)  da un "blocco di spazi".

Come si tolgono, quindi, gli elementi vuoti? Con la funzione filter.

Funzionamento di base
La funzione applica ad ogni elemento "iterable" una funzione passata, usando questo formato:

f = filter(lambda x: x > 18, userAgeList)

Rimozione delle stringhe vuote
Se però noi al posto della funzione passiamo None, la funzione rimuove tutti i valori che sono considerati "nulli" da Python (per cui la stringa vuota, gli zeri, il valore None):

f = filter(None, userAgeList)

Se invece abbiamo una lista "mista" e vogliamo togliere esclusivamente i valori "" (e non 0 o None), bisogna specificare una funzione precisa:

f = filter(lambda x: x != '', s)

martedì 21 gennaio 2025

WINDOWS - Spegnere il PC da riga di comando

Spegnere un PC Windows da riga di comando potrebbe tornare utile quando si è connessi e si sta lavorando in desktop remoto (anche perché l'opzione non è presente nel menu start).

Il comando è:

shutdown /s /t 0

venerdì 17 gennaio 2025

SQL SERVER - Più valori in un singolo campo con STUFF

 A volte è necessario recuperare più di un valore associato ad un record, ma senza produrre più record "duplicati".

Ad esempio, vorremmo ottenere l'elenco degli utenti con i gruppi ad esso associati o un elenco di utenti (per nome) con tutti gli account associati.

Nel primo caso, potremmo avere questa lista di utenti nella tabella "sys_utenti":

utente
----------------------------------------
m.rossi
a.bianchi
p.verdi

Con questi permessi nella tabella "sys_gruppiUtente":

utente     | gruppo
----------------------------------------
m.rossi    | admin
m.rossi    | ufficioIT
a.bianchi  | ufficioAmministrazione
p.verdi    | segreteria
p.verdi    | ufficioVendite

E vorremmo ottenere:

utente     | listaGruppi
----------------------------------------
m.rossi    | admin;ufficioIT
a.bianchi  | ufficioAmministrazione
p.verdi    | segreteria;ufficioVendite

Questo risultato si può ottenere utilizzando la funzione STUFF:

SELECT DISTINCT
        sUte.utente,
        ISNULL(STUFF(( SELECT DISTINCT ';' + sUtGp.gruppo
                       FROM     sys_gruppiUtente AS sUtGp
                       WHERE    sUtGp.utente = sUte.utente
                     FOR
                       XML PATH('')
                     ), 1, 1, ''), '') AS gruppi
FROM    sys_utenti AS sUte
ORDER BY sUte.utente

La query è leggermente complessa, utilizza una subquery per costruire i valori da restituire che, grazie alla mappatura in XML, produce una tabella ridotta e poi la "impacchetta" con STUFF.

La costruzione della tabella ridotta utilizza la mappatura XML: grazie la clausola FOR XML  -che trasforma la "tabella dei dati" in una struttura XML- e poi specifica PATH('') -che indica a SQL di non usare nessun elemento per racchiudere i dati-.

La funzione STUFF, invece, consente di inserire una stringa in un'altra stringa eliminando un numero di caratteri specificato nella posizione iniziale della prima stringa e inserendo la seconda stringa in tale posizione.

I parametri sono:

  • la tabella dei dati di origine
  • la posizione da cui iniziare l'elaborazione
  • il numero di caratteri da sostituire
  • la stringa con cui fare la sostituzione

Nel nostro caso, abbiamo questi valori:

  • la tabella "mappata" in xml
  • la posizione iniziale della stringa (in SQL, si parte da 1 e non da 0!)
  • il numero dei caratteri nella stringa di prefisso (';' è un solo carattere)
  • una stringa vuota con cui fare la sostituzione (in questo modo, si fa effettivamente l'eliminazione)

Questo è un sistema flessibile e potente per ricostruire alcune informazioni utili.

Nota:
è importante notare l'inserimento di una stringa di X caratteri per separare i valori, che verrà successivamente rimossa da STUFF per il primo valore.

In pratica, da una stringa ;valoreA;valoreB otterremo valoreA;valoreB.

Nota:
il collegamento tra la subquery usata nella SELECT e la query principale è dato dal WHERE, che collega un campo della subquery ad un campo della query principale.
Questo è uno dei pochi casi (se non l'unico, forse) in cui si possono usare riferimenti esterni allo scope della query in oggetto (nel nostro caso, la subquery).

giovedì 9 gennaio 2025

PYTHON - Utilizzo di numeri complessi per la gestione di "mappe"

Analizzando le soluzioni di altri sviluppatori per risolvere i problemi dell'Advent of Code del 2024, ho trovato una soluzione semplice ed elegante per gestire le mappe.

La soluzione canonica per la gestione di una "mappa" (cioè una tabella fatta di righe e colonne) è utilizzare un dizionario con una tupla contenente le coordinate come chiave ed il dato caricato dentro come valore.

Utilizzando poi una struttura del genere, c'è bisogno di elaborare un sistema per recuperare e gestire le coordinate "x" e "y" presenti nella tupla, soprattutto nel caso dobbiamo "muoverci" all'interno della mappa o processare elementi attigui.

Ad esempio, se siamo nella casella (0, 0) e vogliamo spostarci in basso e a destra di una riga finendo in (1, 1), dobbiamo effettuare un calcolo che mappi i dati e faccia le somme:

a = (0, 0)
dest = tuple(map(sum, zip(a, (1, 1))))

Una soluzione che facilita questo genere di calcoli utilizza i numeri complessi, cioè i numeri che combinano una parte reale ed una parte immaginaria.

Dato che il dato è composto da due parti, è possibile usare il valore reale per definire la coordinata della riga ed il valore immaginario per definire la coordinata della colonna.

In questo modo, per effettuare la stessa operazione di prima basta sommare un valore "numerico" al dato:

a = complex(0, 0)
dest = a + 1 + 1j

PYTHON - Il costrutto WITH per leggere un file e processarlo

Per leggere il contenuto di un file, si può utilizzare i comandi l'apertura e la chiusura di un file e, in mezzo, il codice per leggere e processare il contenuto... oppure si sfrutta lo statement with.

Questo costrutto rimpiazza un blocco try-catch ed assicura che le risorse invocate vengano chiuse dopo il loro utilizzo.
Nel caso di un'attività su un file, si assicura che il suddetto file venga chiuso.

Questo è un esempio di utilizzo:

fileInput = "test.txt"
rows = []

with open(fileInput, "r") as file:
    for r in file.readlines():
        rows.append(r.strip())

PYTHON - Funzione per calcolare le combinazioni "distinte e non complete" di una lista

Nel risolvere il codice dell'Advent of Code del 2015 (nella fattispecie, il puzzle del giorno 17), mi sono imbattuto nel problema di ottenere tutte le possibili combinazioni "distinte e non complete" di una lista.

Per "distinte e non complete" si intende tutte le possibili combinazioni (senza contare le permutazioni degli elementi e senza ripetizioni) degli elementi presenti nella lista, con un numero di elementi che va da 0 al numero totale degli elementi della lista.
In pratica, l'elenco finale va da una lista vuota alla lista con tutti gli elementi presenti.

Un paio di annotazioni:
  • sono state sviluppate due funzioni, una che processa tutti gli "step" da 0 al numero di elementi presenti, e l'altra che effettua tutte le possibili combinazioni "distinte"
  • la funzione che produce le combinazioni sfrutta la ricorsione
Questo è un codice funzionante per ottenere questo genere di combinazioni (senza l'utilizzo della libreria itertools):
inputList = ["a", "b", "c"]

def getAllCombinations(listToComb):
    res = []
    for i in range(0, len(listToComb) + 1):
        res += getCombinations(listToComb, i)

    return res

def getCombinations(listToComb, n):
    if n == 0:
        return [[]]
    else:
        res = []
        for i in range(len(listToComb)):
            for c in getCombinations(listToComb[i+1:], n - 1):
                res.append([listToComb[i]] + c)
        return res

print(getAllCombinations(inputList))
Il codice sviluppato produce il seguente risultato:
[[], ['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']]

PYTHON - Funzione per calcolare le permutazioni di una lista

Nel risolvere il codice dell'Advent of Code del 2015 (nella fattispecie, il puzzle del giorno 13), mi sono imbattuto nel problema di ottenere tutte le permutazioni possibili di una lista.

L'algoritmo è abbastanza facile da ricavare, però sviluppandolo in python si possono sfruttare dei meccanismi di manipolazione delle liste peculiari del linguaggio.

Questo è un codice funzionante per ottenere le permutazioni (senza l'utilizzo della libreria itertools):
inputList = ["a", "b", "c", "d"]

def getPermutations(listToP):
    permutations = []

    if len(listToP) == 1:
        return [listToP]

    for i, el in enumerate(listToP):
        remaining_el = listToP[:i] + listToP[i+1:]
        subs = getPermutations(remaining_el)
        for p in subs:
            permutations.append([el] + p)

    return permutations

print(getPermutations(inputList))
Da notare che questa versione utilizza la ricorsione.