Come sappiamo il 22 / 02 / 2022 si legge allo stesso modo in un verso e al contrario, per cui è un giorno palindromo. Molti giornali ne stanno parlando e la curiosità , unita all’eventuale misticismo, in questi casi è sempre tanta. Un semplice palindromo che potete divertirvi a constatare da soli è ad esempio i topi non avevano nipoti oppure A te, o poeta, ma a quel punto provato a pensare quante altre date del genere ci siano. Domanda a cui non è semplice trovare una risposta immediata, e per fortuna esistono gli algoritmi! 🙂
Mi sono chiesto, in altri termini, come generare anche le prossime date che avranno questo formato. Di fatto in informatica il check delle stringhe palindrome è molto semplice, per cui mi è sembrato abbastanza naturale generare tutte le date da oggi fino ad un limite ragionevole, e poi fare in modo di verificare una per volta se lo sia o meno.
Quali sono le date palindrome?
Mancava una lista ufficiale sul web, e abbiamo così provveduto a farla noi!
Per conoscere tutte le date palindrome dal 22 02 2022 fino al 10 03 3001ho scritto un piccolo pezzetto di codice in Javascript (che ho pubblicato su Github) per generare la totalità delle date di questo tipo. L’algoritmo non è nulla di particolarmente raffinato ma funziona, a quanto pare, dato che si basa sulla semplice generazione di tutte le date valide nel range indicato, ed ha tirato fuori questa lista dei prossimi giorni palindromi:
Quando sarà la prossima data palindroma, a questo punto? Per la prossima data del genere bisognerà aspettare il 03/02/2030, tra 8 anni.
Cosa vuol dire palindromo
La parola “palindromo” venne utilizzata per la prima volta nel 17° secolo da Ben Jonson, e deriva dal greco antico come composizione di due parole: (πάλιν, che significa di nuovo) e (δÏà³Î¼Î¿Ï‚, ovvero strada o direzione). Possono esistere parole, frasi o numeri palindromi, e in genere ogni lingua possiede i propri, quasi sempre ben documentati.
Ogni tanto ne viene scoperto uno nuovo e si aggiunge, così, alla lista.
Giorni o date palindrome già trascorse
Se volete curiosare, quella che segue è invece la lista dei giorni palindromi che sono già trascorsi:
Go, noto anche come Golang, è un linguaggio di programmazione open source sviluppato da Google. È stato creato da Robert Griesemer, Rob Pike e Ken Thompson nel 2007 e il suo sviluppo è stato reso pubblico nel 2009. Go è progettato per essere efficiente, conciso, veloce e altamente scalabile, con un’enfasi sulla semplicità e la produttività degli sviluppatori. Go ha anche un altro nome, Golang (dove il nome significava Go Language, “linguaggio, vai“), ma il suo nome proprio è Go, a causa del suo precedente nome di dominio, golang.org, ma il suo nome proprio è Go.
Hello, world! in Go
Se vuoi scrivere un programma “Hello, World!” in Go, ecco un esempio semplice:
Nel linguaggio Go, il programma inizia con il pacchetto main, che è speciale perché definisce un’applicazione eseguibile. Poi, importiamo il pacchetto fmt, che fornisce funzioni per l’input e l’output formattati. La funzione main è il punto di ingresso del programma, dove l’esecuzione comincia. Infine, usiamo fmt.Println per stampare la stringa “Hello, World!” seguita da una nuova riga.
Per eseguire questo programma, devi salvare il file con estensione .go (ad esempio, hello.go) e usare i comandi della linea di comando di Go per eseguirlo:
go run hello.go
Questo comando stamperà “Hello, World!” sulla console.
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Dettagli sul funzionamento di Go
Go è un linguaggio di programmazione compilato e tipizzato in modo statico, progettato interamente dallo staff di programmatori di Google. Go è stato creato da Robert Griesemer, Rob Pike e Ken Thompson, che attualmente lavorano al progetto. Si tratta di un linguaggio sintatticamente molto simile a C, ma con una gestione migliorata della sicurezza sulla memoria, meccanismi di garbage collection, tipizzazione strutturata e programmazione concorrente in stile linguaggio CSP. Ci sono due implementazioni principali disponibili, una hosted by Google e l’altra da lanciare in autonomia: il motore software di Go è infatti disponibile come software open source.
Sintassi di Go
La sintassi di Go include aspetti tipici del linguaggio C, e non prevede l’uso di punti e virgola per delimitare le istruzioni (come in Python). È stato introdotto un particolare operatore combinato di dichiarazione/inizializzazione che consente al programmatore di scrivere i := 2 o s := “Ciao Mondo!”, senza specificare i tipi, esattamente come avverrebbe in PHP.
Go aggiunge la possibilità di utilizare letterali per l’inizializzazione dei parametri struct in base al nome e per l’inizializzazione di map e tipi complessi o composti. In alternativa al ciclo for a tre istruzioni di C, le espressioni di intervallo di Go consentono un’iterazione concisa su array, sezioni, stringhe, mappe e canali.
Go offre inoltre una serie di tipi incorporati, inclusi quelli numerici (ad esempio byte, int64, float32), Booleani e stringhe di caratteri (stringa). Le stringhe sono immutabili, la codifica e decodifica è nativamente UTF-8. I tipi di record personalizzati possono essere definiti con la parola chiave struct, come abbiamo appena accennato
Per ogni tipo T e per ogni costante intera non negativa n, esiste un tipo di array indicato con [n]T; array di lunghezze diverse sono quindi di tipi diversi. Gli array dinamici sono disponibili come “slice”, indicati con []T per alcuni tipi T. Questi hanno una lunghezza e una capacità che specificano quando è necessario allocare nuova memoria per espandere l’array.
I puntatori sono disponibili per tutti i tipi e il tipo da puntatore a T è indicato con *T. L’assunzione di indirizzi e l’indirizzamento indiretto utilizzano gli operatori & e *, come in C, o avvengono implicitamente tramite la chiamata al metodo o la sintassi di accesso all’attributo. Non è consentito effettuare aritmetica del puntatore, se non tramite lo speciale tipo di variabile unsafe.Pointer nella libreria standard di Go.
Cosa si può fare in Go
Ecco alcuni punti chiave su Go:
Sintassi chiara e concisa: Go è progettato con una sintassi semplice e leggibile che facilita la scrittura e la comprensione del codice. Ha un numero limitato di parole chiave e una struttura di controllo dei flussi di programma chiara e intuitiva.
Concorrenza e gestione della concorrenza: Go offre un supporto integrato per la programmazione concorrente attraverso le goroutine e i canali. Le goroutine sono thread leggeri che consentono agli sviluppatori di scrivere codice altamente concorrente in modo semplice ed efficiente.
Rapido e performante: Go è noto per le sue prestazioni elevate e la sua efficienza nell’utilizzo delle risorse. È particolarmente adatto per applicazioni che richiedono elevata concorrenza o elaborazione parallela.
Tipizzazione statica e dinamica: Go è un linguaggio tipizzato staticamente, il che significa che le variabili devono essere dichiarate con un tipo specifico e il tipo delle variabili è verificato a tempo di compilazione. Tuttavia, Go offre anche la flessibilità della tipizzazione dinamica attraverso l’uso di interfacce.
Ampia libreria standard: Go include una libreria standard ricca di funzionalità per gestire una vasta gamma di compiti, tra cui operazioni di I/O, gestione delle stringhe, networking, criptografia e altro ancora.
Compilazione rapida: Il compilatore di Go è veloce e produce codice eseguibile altamente ottimizzato. Questo facilita lo sviluppo iterativo e la distribuzione rapida delle applicazioni.
Complessivamente, Go è diventato popolare tra gli sviluppatori per la sua semplicità, prestazioni elevate e capacità di gestire carichi di lavoro complessi, ed è utilizzato in una varietà di settori, tra cui lo sviluppo di applicazioni web, sistemi distribuiti, servizi cloud e molto altro ancora.
Con Go è possibile realizzare sia applicazioni a linea di comando che applicazioni web, eventualmente sfruttando GopherJS che permette di tradurre automaticamente Go in Javascript. All’interno del sito ufficiale è possibile testare il codice direttamente nel sito, realizzando un semplice Hello, World! con poche righe di codice:
package main
import "fmt"
func main() {
fmt.Println("Hello, amicone del cuore")
}
Il risultato è il seguente.
Se invece volessimo fare la stessa cosa lato web, si potrebbe fare in questi termini:
package main
import (
"fmt"
)
func main() {
var operator string
var num1, num2 float64
fmt.Print("Inserisci il primo numero: ")
fmt.Scanln(&num1)
fmt.Print("Inserisci il secondo numero: ")
fmt.Scanln(&num2)
fmt.Print("Inserisci l'operatore (+, -, *, /): ")
fmt.Scanln(&operator)
result := 0.0
switch operator {
case "+":
result = num1 + num2
case "-":
result = num1 - num2
case "*":
result = num1 * num2
case "/":
if num2 != 0 {
result = num1 / num2
} else {
fmt.Println("Errore: divisione per zero!")
return
}
default:
fmt.Println("Operatore non valido!")
return
}
fmt.Printf("Il risultato di %.2f %s %.2f è: %.2f\n", num1, operator, num2, result)
}
Altri esempi di funzioni Go
Queste sono solo alcune delle molte funzioni utili che si possono scrivere in Go. Spero che queste ti diano una buona base per iniziare a esplorare il linguaggio e le sue potenzialità!
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func main() {
// Inizializza il generatore di numeri casuali
rand.Seed(time.Now().UnixNano())
// 1. Funzione per calcolare la somma di due numeri
sum := add(5, 7)
fmt.Println("Somma di 5 e 7:", sum)
// 2. Funzione per trovare il massimo tra due numeri
max := maximum(10, 25)
fmt.Println("Massimo tra 10 e 25:", max)
// 3. Funzione per invertire un array
arr := []int{1, 2, 3, 4, 5}
reverseArray(arr)
fmt.Println("Array invertito:", arr)
// 4. Funzione per ordinare un array
arrToSort := []int{5, 2, 8, 1, 9}
sort.Ints(arrToSort)
fmt.Println("Array ordinato:", arrToSort)
// 5. Funzione per estrarre un numero casuale compreso tra un intervallo specificato
randomNum := generateRandomNumber(1, 100)
fmt.Println("Numero casuale tra 1 e 100:", randomNum)
// 6. Funzione per calcolare la media di un array di numeri
nums := []float64{5.5, 6.6, 7.7, 8.8, 9.9}
average := calculateAverage(nums)
fmt.Println("Media degli elementi:", average)
// 7. Funzione per verificare se un numero è presente in un array
exists := isNumberExists(arrToSort, 8)
fmt.Println("Il numero 8 esiste nell'array:", exists)
// 8. Funzione per calcolare la potenza di un numero
power := calculatePower(2, 3)
fmt.Println("2 elevato alla 3:", power)
// 9. Funzione per contare il numero di vocali in una stringa
vowelsCount := countVowels("Hello World")
fmt.Println("Numero di vocali nella stringa:", vowelsCount)
// 10. Funzione per invertire una stringa
reversedString := reverseString("Hello")
fmt.Println("Stringa invertita:", reversedString)
}
func add(a, b int) int {
return a + b
}
func maximum(a, b int) int {
if a > b {
return a
}
return b
}
func reverseArray(arr []int) {
for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
arr[i], arr[j] = arr[j], arr[i]
}
}
func generateRandomNumber(min, max int) int {
return rand.Intn(max-min+1) + min
}
func calculateAverage(nums []float64) float64 {
sum := 0.0
for _, num := range nums {
sum += num
}
return sum / float64(len(nums))
}
func isNumberExists(arr []int, target int) bool {
for _, num := range arr {
if num == target {
return true
}
}
return false
}
func calculatePower(base, exponent int) int {
result := 1
for i := 0; i < exponent; i++ {
result *= base
}
return result
}
func countVowels(str string) int {
count := 0
vowels := "aeiouAEIOU"
for _, char := range str {
if strings.ContainsRune(vowels, char) {
count++
}
}
return count
}
func reverseString(str string) string {
runes := []rune(str)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
Il linguaggio Logo ha posto le basi per un approccio all’apprendimento informatico che è rimasto rilevante e influente per decenni. Grazie al lavoro di visionari come Seymour Papert e Mitchel Resnick, questa filosofia è stata trasformata e adattata per il mondo digitale con strumenti come Scratch e il modulo turtle di Python. In definitiva, l’eredità di Logo vive attraverso queste piattaforme, che continuano a ispirare e preparare le menti creative e curiose di oggi per i compiti del domani.
Il linguaggio Logo è un pilastro nell’educazione informatica e nel mondo della programmazione creativa. Creato da Seymour Papert e il suo team negli anni ’60, Logo ha rivoluzionato il modo in cui i bambini e gli adulti imparano a programmare, introducendo il concetto di “programmazione attraverso il gioco”. Questo linguaggio è stato un pioniere nel campo dell’apprendimento basato sull’esplorazione, consentendo agli studenti di interagire con la matematica e l’informatica in modo divertente e coinvolgente.
Seymour Papert: Il Visionario dietro Logo
By Matematicamente.it – Matematica C3 Algebra DOLCE 1, Testo per il primo biennio della Scuola Secondaria di II grado, prima edizione anno 2014. Pagg. 344. ISBN 9788896354681; prezzo € 0,00; formato ebook pdf; licenza Creative Commons BY; editore Matematicamente.it: http://www.matematicamente.it/staticfiles/manuali-cc/algebra1_dolce_1ed.pdf, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=46565196
Seymour Papert, un pioniere dell’intelligenza artificiale e dell’educazione, ha sviluppato Logo con l’obiettivo di democratizzare l’accesso all’informatica. Credeva che ogni individuo, indipendentemente dalla loro età o background, dovesse avere l’opportunità di imparare a pensare in modo computazionale. Logo è stato progettato per aiutare le persone a sviluppare abilità di risoluzione dei problemi, pensiero critico e creatività attraverso l’esplorazione di concetti matematici e informatici.
Resnick e il Passaggio a un Nuovo Paradigma Digitale
Mitchel Resnick, uno dei principali ricercatori nel campo dell’apprendimento creativo e del pensiero computazionale, ha esteso il lavoro di Papert nel digitale. Il suo lavoro con il linguaggio di programmazione Scratch ha portato avanti la visione di Papert, fornendo agli studenti uno spazio in cui possono creare, condividere e collaborare in progetti digitali. Scratch, con la sua interfaccia visuale intuitiva, rende la programmazione accessibile a una vasta gamma di persone, incoraggiandole a diventare creatori digitali.
Da Logo a Scratch: Continuità nell’Apprendimento
Sebbene il linguaggio Logo e Scratch possano sembrare molto diversi superficialmente, con il passare degli anni è emersa una chiara continuità nel modo in cui facilitano l’apprendimento. Entrambi incoraggiano l’esplorazione e l’esperimento, consentendo agli utenti di creare programmi interattivi attraverso un’interfaccia intuitiva. Come Logo, Scratch promuove la creatività, il pensiero critico e la risoluzione dei problemi, preparando gli studenti per sfide più complesse nel mondo della programmazione e oltre.
Il Ruolo della Tartaruga: Connessioni con Python
Un aspetto iconico di Logo è la tartaruga, una grafica virtuale che risponde ai comandi di movimento. Questa tartaruga è diventata un simbolo di esplorazione e creatività nel mondo della programmazione. Collegando questo concetto alla programmazione Python, possiamo utilizzare il modulo turtle, che consente agli sviluppatori di Python di creare grafica vettoriale utilizzando comandi simili a quelli di Logo. Il modulo turtle offre agli studenti un modo diretto per sperimentare i concetti di programmazione geometrica, portando la filosofia di Logo nel mondo moderno della programmazione Python.
Qual è il percorso più breve per andare da Rotterdam a Groninga? Si tratta di utilizzare l’algoritmo del percorso più breve, che ho progettato in circa 20 minuti. Una mattina stavo facendo shopping ad Amsterdam con la mia giovane fidanzata e, stanchi, ci siamo seduti sulla terrazza di un bar per bere una tazza di caffè e stavo pensando se potevo farlo e poi ho progettato l’algoritmo per il percorso più breve. Come ho detto, fu un’invenzione di 20 minuti. In realtà, fu pubblicato nel 1959, tre anni dopo. La pubblicazione è ancora molto bella. Uno dei motivi per cui è così bello è che l’ho progettato senza carta e penna. Senza carta e penna si è quasi costretti a evitare tutte le complessità evitabili. Alla fine quell’algoritmo è diventato, con mio grande stupore, una delle pietre miliari della mia fama. (Edsger W. Dijkstra)
L’algoritmo di Dijkstra è utilizzato per trovare il percorso più breve da un nodo di partenza a tutti gli altri nodi in un grafo con pesi sugli archi non negativi. Questo procedimento è efficace su grafi non direzionati e direzionati con pesi sugli archi non negativi, e può essere usato su mappe geografiche ma anche come ausilio o base per algoritmi più complessi.
Ecco una spiegazione passo passo di uno dei più noti e celebri algoritmi di informatica.
Premesse:
Grafo: Si parte da un grafo con nodi e archi.
Nodo di partenza: Indica il nodo da cui inizia la ricerca del percorso più breve.
Pesi sugli archi: Ogni arco ha un peso che indica la distanza o il costo tra due nodi collegati.
Passo 1: Inizializzazione
Insieme dei nodi visitati: Inizialmente vuoto.
Distanza dal nodo di partenza: Inizialmente, per tutti i nodi, eccetto il nodo di partenza, la distanza viene impostata a infinito.
Distanza dal nodo di partenza a se stesso: È 0.
Passo 2: Scelta del nodo con la distanza minima
Scegli il nodo con la distanza minima non ancora visitato: Inizia con il nodo di partenza.
Calcola le distanze minime: Aggiorna la distanza minima da questo nodo ai suoi vicini considerando il peso degli archi e il percorso più breve attuale.
Passo 3: Aggiornamento delle distanze
Per ogni nodo adiacente non ancora visitato: Calcola la distanza minima rispetto al nodo di partenza passando attraverso il nodo attuale.
Aggiorna la distanza se la nuova distanza è minore di quella attuale: Se la nuova distanza è più breve, aggiorna il valore della distanza e l’informazione sul nodo precedente che ha portato a questa distanza più breve.
Passo 4: Marcatura del nodo visitato
Marca il nodo attuale come visitato: Una volta terminato il calcolo delle distanze minime per tutti i nodi adiacenti, marca il nodo attuale come visitato e continua il processo scegliendo il prossimo nodo con la distanza minima non ancora visitato.
Passo 5: Ripeti finché tutti i nodi non sono visitati
Continua il processo: Continua a scegliere il nodo con la distanza minima non visitato e aggiornare le distanze fino a quando tutti i nodi non sono stati visitati.
Passo 6: Ottieni il percorso più breve
Recupera il percorso più breve per ogni nodo: Una volta che tutti i nodi sono stati visitati e le distanze minime sono state calcolate, è possibile recuperare il percorso più breve dal nodo di partenza a ogni altro nodo utilizzando le informazioni sui nodi precedenti.
E per finire…
Alla fine dell’algoritmo, si otterranno le distanze minime da un nodo di partenza a tutti gli altri nodi nel grafo e i percorsi più brevi associati a ciascun nodo.
L’algoritmo reinterpretato dall’intelligenza artificiale di Midjourney su una mappa dell’Europa.
Implementazione in Python dell’algoritmo di Dijkstra
In questo esempio, graph è un dizionario che rappresenta il grafo con i nodi come chiavi e un altro dizionario interno che rappresenta i nodi collegati e i relativi pesi sugli archi.
L’algoritmo di Dijkstra utilizza una coda di priorità implementata con heapq per estrarre efficientemente il nodo con la distanza minima non visitato ad ogni iterazione. Il risultato restituito è un dizionario delle distanze minime dal nodo di partenza a tutti gli altri nodi nel grafo.
import heapq
def dijkstra(graph, start):
# Inizializzazione delle distanze, impostate tutte a infinito tranne il nodo di partenza
distances = {node: float('inf') for node in graph}
distances[start] = 0
# Coda di priorità per mantenere traccia dei nodi non visitati e delle loro distanze
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
# Se abbiamo già trovato un percorso più breve per questo nodo, passiamo avanti
if current_distance > distances[current_node]:
continue
# Esplora i vicini del nodo corrente
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
# Se la nuova distanza è più breve di quella attualmente nota
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# Esempio di utilizzo dell'algoritmo con un grafo di esempio
graph_example = {
'A': {'B': 5, 'C': 3},
'B': {'A': 5, 'C': 2, 'D': 1},
'C': {'A': 3, 'B': 2, 'D': 4},
'D': {'B': 1, 'C': 4}
}
start_node = 'A'
result = dijkstra(graph_example, start_node)
print("Distanze minime dal nodo di partenza '" + start_node + "':")
print(result)
Ecco un’implementazione di base dell’algoritmo di Dijkstra in Python utilizzando un dizionario per rappresentare il grafo e una coda di priorità per tenere traccia dei nodi non visitati con le relative distanze.
Foto: <a href=”https://commons.wikimedia.org/wiki/File:Edsger_Dijkstra_1994.jpg”>Andreas F. Borchert</a>, <a href=”https://creativecommons.org/licenses/by-sa/3.0/de/deed.en”>CC BY-SA 3.0 DE</a>, via Wikimedia Commons
Oggi 7 giugno 2024 segna un anniversario importante nel mondo dell’informatica e della filosofia: il settantesimo anniversario della morte di Alan Turing. Quest’uomo straordinario non è solo un pioniere nell’ambito dell’informatica, ma anche un pensatore rivoluzionario che ha influenzato profondamente la nostra comprensione della mente umana e della natura stessa della realtà.
Enigma
Alan Mathison Turing nasce il 23 giugno 1912, a Londra, in Inghilterra. Fin dalla sua giovinezza, ha dimostrato un’intelligenza eccezionale e un interesse precoce per la matematica e la logica. Il suo contributo più celebre è senza dubbio la sua fondamentale influenza nello sviluppo della teoria dell’informazione e nell’avanzamento della crittografia durante la Seconda Guerra Mondiale. Durante il secondo conflitto mondiale, Turing ha lavorato come parte del team britannico che ha decifrato i codici della macchina cifrante tedesca Enigma, un lavoro cruciale che ha aiutato gli Alleati a ottenere importanti vittorie. Il suo lavoro pionieristico nel campo della crittografia ha gettato le basi per molte delle moderne tecniche di sicurezza informatica che utilizziamo ancora oggi.
La macchina di Turing
Ma il contributo di Turing va ben oltre la sua opera durante la guerra. È universalmente riconosciuto come il padre della scienza dell’informatica, grazie al suo concetto di “macchina di Turing”, un modello astratto di un computer che ha aperto la strada alla nascita dei computer moderni. La sua visione della computazione universale ha dimostrato che qualsiasi calcolo matematico può essere eseguito da una macchina di Turing, aprendo la strada alla realizzazione pratica di computer programmabili.
Oltre ai suoi contributi pratici all’informatica, Turing ha anche affrontato domande filosofiche profonde sulla mente e sull’intelligenza artificiale. Nel suo famoso articolo del 1950, “Computing Machinery and Intelligence“, ha introdotto il concetto del “Test di Turing”, un criterio per determinare se una macchina può essere considerata “intelligente”. Questo test ha alimentato dibattiti filosofici senza fine sulla natura della coscienza e sull’intelligenza artificiale, dibattiti che continuano ancora oggi.
Turing, tuttavia, ha anche sofferto a causa dell’omofobia della sua epoca. Nel 1952, è stato processato per “atti osceni” in base alle leggi britanniche contro l’omosessualità e condannato a una terapia ormonale chimica, subendo un trattamento ingiusto e disumano. Nel 1954, all’età di soli 41 anni, Turing è morto per avvelenamento da cianuro, in circostanze che rimangono ancora oggetto di dibattito e speculazione.
Oggi, mentre riflettiamo sui settanta anni dalla scomparsa di Alan Turing, è importante ricordare il suo genio e il suo impatto duraturo sull’informatica, sulla crittografia e sulla filosofia. Il suo lavoro continua a ispirare generazioni di ricercatori e pensatori, mentre la sua vita e il suo tragico destino ci ricordano la necessità di combattere l’ingiustizia e l’oppressione in tutte le sue forme. Alan Turing rimane un faro di speranza e un modello di coraggio intellettuale, la cui eredità continuerà a illuminare il cammino dell’umanità nel futuro.
Temo che il seguente sillogismo possa essere utilizzato da qualcuno in futuro.
Turing ritiene che le macchine pensino Turing mente con gli uomini Quindi le macchine non pensano (A. Turing)
La ricerca in ampiezza (BFS – Breadth-First Search) e la ricerca in profondità (DFS – Depth-First Search) sono due algoritmi di ricerca utilizzati per esplorare o trovare un elemento all’interno di una struttura dati come un albero o un grafo. Entrambi sono utilizzati in contesti diversi e utilizzano approcci di esplorazione distinti.
Entrambi gli algoritmi, la ricerca in ampiezza (BFS) e la ricerca in profondità (DFS), vengono utilizzati in una vasta gamma di contesti in informatica, intelligenza artificiale, algoritmi di ricerca, grafica per computer e molti altri campi.
BFS è ampiamente utilizzata in problemi che richiedono la ricerca del percorso più breve tra due punti, come la navigazione in mappe, la ricerca del percorso ottimale in giochi, l’algoritmo di Dijkstra per trovare i cammini più brevi in grafi pesati positivamente, la ricerca del tragitto più breve tra nodi in reti di computer, tra gli altri.
DFS è preziosa per applicazioni in cui si desidera esplorare a fondo una struttura dati, come la verifica della presenza di cicli in un grafo, la ricerca di soluzioni in spazi di ricerca complessi come nel backtracking o in algoritmi di ricerca in profondità per trovare soluzioni a problemi di intelligenza artificiale.
La scelta dell’algoritmo dipende strettamente dal problema specifico che si sta affrontando e dalle caratteristiche della struttura dati su cui si sta operando. Entrambi hanno vantaggi e applicazioni specifiche in base alle esigenze e ai requisiti dell’applicazione.
Ricerca in Ampiezza (BFS):
La BFS esplora tutti i nodi adiacenti al livello corrente prima di spostarsi ai livelli successivi. Inizia dall’elemento di partenza e procede verso l’esterno, visitando tutti i nodi al livello corrente prima di passare ai nodi successivi.
Funzionamento:
Struttura dati utilizzata: Si utilizza una coda per tenere traccia dei nodi da visitare.
Ordine di visita: I nodi vengono visitati per livelli, visitando prima tutti i nodi adiacenti al livello corrente prima di passare ai livelli successivi.
Utilizzo tipico: Viene utilizzata per trovare la soluzione più vicina al nodo di partenza, per esempio, la ricerca del percorso più breve.
Applicazioni:
Trovare il percorso più breve tra due nodi in un grafo non pesato.
Trovare la soluzione ottimale in un grafo non pesato o con pesi uniformi.
Ricerca in Profondità (DFS)
La DFS esplora quanto più in profondità possibile lungo un ramo del grafo prima di tornare indietro. Inizia dall’elemento di partenza e continua a scendere in profondità lungo uno dei rami finché non raggiunge un punto in cui non ci sono ulteriori nodi da esplorare. Poi risale al nodo precedente e continua in un altro ramo.
Funzionamento:
Struttura dati utilizzata: Si utilizza uno stack (o ricorsione) per tenere traccia dei nodi da visitare.
Ordine di visita: Viene data priorità alla profondità, esplorando uno dei rami fino a che non si raggiunge un nodo terminale, quindi risale al nodo precedente e continua con un altro ramo.
Utilizzo tipico: Viene utilizzata per trovare soluzioni in grafi o alberi, ma non garantisce il percorso più breve.
Applicazioni:
Verificare la presenza di un elemento in una struttura dati.
Trovare cicli in grafi.
Confronto:
Memoria: BFS utilizza più memoria a causa della coda utilizzata, mentre DFS utilizza una quantità di memoria relativamente minore.
Percorsi: BFS restituisce il percorso più breve tra due nodi, mentre DFS potrebbe non restituire il percorso più breve.
Implementazione: BFS può essere implementata utilizzando una coda, mentre DFS può essere implementata utilizzando uno stack o ricorsione.
Scegliere tra BFS e DFS dipende dall’obiettivo specifico dell’applicazione e dalla struttura del grafo o dell’albero su cui si lavora.
Parlare di linguaggi di programmazione “brutti” può essere un po’ soggettivo, poiché le preferenze personali e le esigenze del progetto possono influenzare le opinioni sugli strumenti di sviluppo. Tuttavia, ci sono alcuni linguaggi che sono spesso considerati meno attraenti o più problematici per varie ragioni. Ecco alcuni esempi di quelli che provocano volutamente il mal di testa.
Whitespace
Si tratta di un linguaggio di programmazione in cui solo spazi, tabulazioni e ritorni a capo sono significativi, mentre i caratteri alfanumerici sono ignorati. La sua sintassi basata sulla formattazione del testo può essere considerata poco pratica e difficile da leggere per molte persone. Questo programma, se eseguito con un interprete Whitespace, stamperà “Hello World!” sulla console. Anche se può sembrare criptico, i diversi spazi vuoti, tabulazioni e ritorni a capo sono interpretati dall’interprete Whitespace per produrre l’output desiderato.
Ecco un programma “Hello World!” scritto in Whitespace, un linguaggio di programmazione che utilizza solo spazi vuoti, tabulazioni e ritorni a capo per la sintassi, mentre ignora completamente i caratteri alfanumerici. Se non vedete nulla, è normale (i caratteri del linguaggio non sono visibili essendo spazi, il codice originale si trova qui – con compilatore online – e qui).
Brainfuck
È un linguaggio estremamente minimale e oscuro, composto da soli otto comandi. La sua sintassi è stata progettata per essere il più possibile compatta, ma questo rende il codice difficile da scrivere, leggere e comprendere. Gli 8 comandi di Brainfuck sono:
>: Incrementa il puntatore della cella di memoria di 1.
<: Decrementa il puntatore della cella di memoria di 1.
+: Incrementa il valore della cella di memoria puntata di 1.
-: Decrementa il valore della cella di memoria puntata di 1.
[: Se il valore della cella di memoria puntata è zero, salta all’istruzione dopo la corrispondente parentesi chiusa ].
]: Torna all’istruzione dopo la corrispondente parentesi aperta [, se il valore della cella di memoria puntata non è zero.
.: Stampa il valore ASCII della cella di memoria puntata.
,: Legge un carattere dalla tastiera e lo memorizza nella cella di memoria puntata come valore ASCII.
Scrivere una calcolatrice per la radice quadrata in Brainfuck può essere piuttosto complesso, dato che il linguaggio è molto limitato e non offre operazioni matematiche avanzate come la radice quadrata. Tuttavia, possiamo implementare una semplice approssimazione della radice quadrata utilizzando metodi come il metodo di Newton-Raphson. Ecco un’implementazione di base:
Esempio di calcolo della radice quadrata in brainfuck:
Creato per essere il più ostico possibile, INTERCAL è noto per la sua sintassi bizzarra e poco intuitiva. È stato concepito come uno scherzo e non è mai stato adottato per scopi pratici. L’obiettivo di INTERCAL era più un esperimento umoristico che un linguaggio di programmazione pratico. Questo codice:
PLEASE READ OUT #13
DO ,1 <- #238
PLEASE DO ,1 SUB #1 <- #13
DO ,1 SUB #2 <- #0
DO ,1 SUB #3 <- #13
DO ,1 SUB #4 <- #194
DO ,1 SUB #5 <- #15
DO ,1 SUB #6 <- #225
PLEASE DO ,1 SUB #7 <- #248
PLEASE DO ,1 SUB #8 <- #13
DO ,1 SUB #9 <- #10
PLEASE DO ,1 SUB #10 <- #255
DO ,1 SUB #11 <- #220
DO ,1 SUB #12 <- #194
DO READ OUT ,1
PLEASE GIVE UP
restituisce :
ICL099I PROGRAMMER IS OVERLY POLITE ON THE WAY TO 18 CORRECT SOURCE AND RESUBNIT
e non sappiamo perchè.
Malbolge
Proclamato come il “linguaggio di programmazione più difficile da usare”, Malbolge è stato progettato appositamente per essere estremamente complicato da comprendere e scrivere. La sua sintassi è estremamente contorta e il suo funzionamento è quasi insensato.
Malbolge è un linguaggio di programmazione estremamente difficile da comprendere e utilizzare. È progettato per essere il più ostico possibile ed è noto per essere quasi impossibile da scrivere manualmente. Tuttavia, ecco un esempio di programma “Hello World!” in Malbolge:
Questo codice Malbolge stampa “Hello World!” quando viene eseguito nell’interprete Malbolge. Tuttavia, capire come funziona questo codice e come sia stato generato richiederebbe una conoscenza approfondita del linguaggio e delle sue peculiarità. Malbolge è stato creato appositamente per essere estremamente difficile da comprendere e scrivere, quindi programmi anche relativamente semplici possono sembrare completamente incomprensibili.
PHP (si scherza)
Sebbene PHP sia stato ampiamente utilizzato per lo sviluppo web, è spesso criticato per la sua sintassi incostante, la mancanza di coerenza e alcune caratteristiche considerate obsolette o poco sicure. Nonostante ciò, ha una vasta base di utenti e continua a essere uno dei linguaggi principali per la creazione di siti web dinamici.
Scrivere un codice orribile in PHP va contro i principi della buona programmazione e della manutenibilità del codice. Tuttavia, posso fornirti un esempio di codice che viola molte best practice e che potrebbe essere considerato “orribile” in quanto difficile da leggere, comprendere e mantenere. Si prega di notare che non promuovo questo tipo di pratica di programmazione:
<?php
// Codice orribile in PHP
class A {
public $name;
private $age;
public function __construct($n, $a) {
$this->name = $n;
$this->setAge($a);
}
public function setAge($a) {
if (is_numeric($a) && $a > 0 && $a < 150) {
$this->age = $a;
} else {
throw new Exception("Invalid age!");
}
}
public function getAge() {
return $this->age;
}
}
function prntA($a) {
echo "<br><b>Name: </b>" . $a->name . "<br>";
echo "<b>Age: </b>" . $a->getAge() . "<br>";
}
$person1 = new A("John", 25);
$person2 = new A("Jane", 30);
$person3 = new A("Jack", -5); // Età negativa
prntA($person1);
prntA($person2);
prntA($person3);
?>
Questo codice presenta diversi problemi, tra cui:
Mancanza di separazione tra logica e presentazione.
Gestione degli errori poco chiara.
Assenza di validazione approfondita dei dati di input.
Utilizzo di eccezioni per gestire scenari comuni come età negativa.
Mancanza di coerenza nello stile di codifica.
Mancanza di commenti e documentazione.
Potenziali vulnerabilità di sicurezza.
Questi sono solo alcuni esempi di ciò che potrebbe rendere un codice PHP orribile. È importante scrivere codice che sia chiaro, coerente, sicuro e manutenibile.
Conclusioni
Questi sono solo alcuni esempi di linguaggi che potrebbero essere considerati “brutti” per vari motivi. Tuttavia, è importante ricordare che ogni linguaggio ha le sue peculiarità e che quello che potrebbe sembrare poco attraente per alcuni potrebbe essere perfettamente adatto per altri, a seconda delle esigenze del progetto e delle preferenze personali.
Ogni linguaggio di programmazione ha i suoi punti di forza e le sue debolezze, ed è importante scegliere quello più adatto alle esigenze specifiche del progetto e alle preferenze personali del programmatore. Quindi, mentre possiamo scherzare sulle peculiarità di alcuni linguaggi, è importante rispettare la diversità e la flessibilità offerta dalla vasta gamma di linguaggi disponibili. Alla fine, ciò che conta davvero è la capacità di utilizzare gli strumenti a nostra disposizione per creare software funzionale e di qualità. E ad un certo punto il linguaggio potrebbe anche rispondere “sarai bello tu!“.
Un “check” è un termine che può assumere diversi significati a seconda del contesto in cui viene utilizzato. Ecco alcune delle sue possibili interpretazioni più comuni:
Controllo o verifica: In contesti generali, un “check” si riferisce a un controllo o a una verifica. Ad esempio, “fare un check” su qualcosa significa controllare o verificare la sua condizione o la sua accuratezza.
Assegno: In ambito finanziario, un “check” è un assegno, un documento scritto da una persona o da un’azienda a favore di un’altra persona o azienda, che rappresenta una promessa di pagamento.
Controllo dei bagagli: In aeroporto o in altri luoghi di viaggio, un “check” può fare riferimento al processo di controllo dei bagagli da parte della sicurezza o al punto in cui si consegnano i bagagli per essere caricati sull’aereo.
Segno di spunta o spuntino: In informatica o nelle applicazioni di gestione delle attività, un “check” è spesso rappresentato da un segno di spunta (✔) e indica che un’attività o un elemento è stato completato o verificato con successo.
Controllo delle prestazioni di un veicolo: In ambito automobilistico, un “check” può riferirsi al controllo delle prestazioni di un veicolo o alla verifica dei suoi componenti, come ad esempio il “check del motore” o il “check degli pneumatici”.
La definizione esatta di “check” dipende dal contesto in cui viene utilizzato, quindi è importante considerare il contesto per comprendere il suo significato specifico in una data situazione.
Check nella programmazione
In programmazione, un “check” è spesso associato a un controllo o a una verifica condizionale, utilizzato per assicurarsi che una certa condizione sia soddisfatta prima di procedere con un’azione specifica. Ecco un esempio di un “check” in un programma Python:
Supponiamo di avere un programma che richiede all’utente di inserire un numero e vogliamo verificare se il numero inserito è maggiore di 10. In questo caso, stiamo facendo un “check” sulla condizione della variabile numero inserita dall’utente:
python
numero = int(input("Inserisci un numero: "))
# Facciamo un check sulla condizione: è il numero maggiore di 10? if numero > 10: print("Il numero è maggiore di 10.") else: print("Il numero non è maggiore di 10.")
Nel codice sopra, la parte chiave è l’istruzione if, che esegue un “check” sulla condizione numero > 10. Se la condizione è vera (cioè il numero inserito è maggiore di 10), verrà eseguito il blocco di istruzioni nel primo ramo dell’if. Altrimenti, se la condizione è falsa, verrà eseguito il blocco di istruzioni nell’else.
In ogni linguaggio di programmazione moderno come Java, è essenziale poter prendere decisioni, interagire con l’utente e organizzare il codice in blocchi riutilizzabili. In questa sezione analizzeremo tre concetti fondamentali: le strutture condizionali if/else, la lettura dell’input da tastiera, e l’uso dei metodi.
Condizioni: if, else if, else
Le strutture condizionali permettono di eseguire porzioni di codice solo se determinate condizioni logiche risultano vere. L’istruzione if verifica una condizione booleana e, se questa è vera, esegue il blocco di codice associato. L’istruzione else consente di definire un comportamento alternativo, mentre else if permette di valutare condizioni multiple in sequenza. Questo tipo di controllo è alla base della logica decisionale nei programmi, ed è indispensabile per creare comportamenti dinamici.
Input da Tastiera
L’interazione con l’utente è un aspetto chiave di molte applicazioni. In Java, l’input da tastiera può essere gestito in modo semplice ed efficiente mediante la classe Scanner, appartenente al package java.util. Questa classe permette di leggere vari tipi di dati (stringhe, numeri interi, numeri in virgola mobile, ecc.) digitati dall’utente durante l’esecuzione del programma. L’input dinamico consente di rendere le applicazioni più flessibili e personalizzabili.
Metodi
I metodi rappresentano blocchi di codice riutilizzabili che eseguono una specifica operazione. In Java, ogni metodo deve appartenere a una classe, e può essere definito per restituire un valore o per eseguire un compito (metodi void). I metodi migliorano la leggibilità, la modularità e la manutenibilità del codice, permettendo di suddividere un programma complesso in unità più semplici e specializzate. Inoltre, l’uso corretto dei metodi promuove la programmazione orientata agli oggetti e facilita il riutilizzo del codice.
1. Condizioni (if, else, else if)
Servono per eseguire qualcosa solo se una condizione è vera.
Esempi:
int età = 18;
if (età >= 18) {
System.out.println("Sei maggiorenne.");
} else {
System.out.println("Sei minorenne.");
}
Puoi aggiungere condizioni multiple:
int voto = 7;
if (voto >= 9) {
System.out.println("Ottimo");
} else if (voto >= 6) {
System.out.println("Sufficiente");
} else {
System.out.println("Insufficiente");
}
✅ Operatori comuni:
Operatore
Significato
==
uguale
!=
diverso
<, >, <=, >=
minore, maggiore…
&&
e logico
`
⌨️ 2. Input da tastiera
Serve per chiedere dati all’utente.
Esempio con Scanner:
import java.util.Scanner;
public class InputDemo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Come ti chiami? ");
String nome = input.nextLine();
System.out.print("Quanti anni hai? ");
int età = input.nextInt();
System.out.println("Ciao " + nome + ", hai " + età + " anni.");
}
}
✳️ Importante: Scanner si importa con import java.util.Scanner;
3. Funzioni (metodi)
Un metodo è un blocco di codice riutilizzabile.
Sintassi base:
public static void saluta() {
System.out.println("Ciao dal metodo!");
}
Metodo con parametri:
public static void salutaPersona(String nome) {
System.out.println("Ciao, " + nome + "!");
}
Metodo che ritorna un valore:
public static int somma(int a, int b) {
return a + b;
}
Esempio completo:
public class MetodiDemo {
public static void main(String[] args) {
saluta(); // stampa un saluto generico
salutaPersona("Luca"); // saluta qualcuno
int risultato = somma(3, 4);
System.out.println("Somma: " + risultato);
}
public static void saluta() {
System.out.println("Ciao!");
}
public static void salutaPersona(String nome) {
System.out.println("Ciao, " + nome + "!");
}
public static int somma(int a, int b) {
return a + b;
}
}
Istanza in Informatica: Definizione, Etimologia e Approfondimento
Etimologia
La parola “istanza” deriva dal latino “instantia,” che significa “presenza, insistenza,” da “instans,” che significa “che preme, che urge”. In informatica, il termine è stato adattato per indicare una singola manifestazione di un’entità o un concetto che può essere “presente” o “utilizzato” all’interno di un sistema.
Definizione “istanza” in Informatica
In informatica, un’istanza si riferisce a un’implementazione concreta o un’esecuzione specifica di una classe, un processo, o un oggetto all’interno di un programma o di un sistema informatico.
Istanza di una Classe
Nel contesto della programmazione orientata agli oggetti, una classe è un modello o un “blueprint” che definisce le proprietà e i comportamenti comuni a tutti gli oggetti di quel tipo. Un’istanza di una classe è un oggetto reale creato sulla base di quella classe. Ogni istanza ha il proprio stato, rappresentato dai valori delle proprietà (o attributi), anche se condivide il comportamento definito nella classe.
Per esempio, in un programma che modella automobili, la classe “Auto” potrebbe definire attributi come “marca,” “modello,” e “colore”. Quando crei un oggetto basato su questa classe (ad esempio, “miaAuto”), stai creando un’istanza della classe “Auto”. Ogni “miaAuto” sarà un’istanza diversa con i propri valori specifici per “marca,” “modello,” e “colore”.
Istanza di un Processo
Nel contesto dell’esecuzione di programmi, un’istanza può anche riferirsi a una singola esecuzione di un processo. Quando avvii un programma sul tuo computer, una nuova istanza di quel programma (o processo) viene creata e gestita dal sistema operativo. Ogni volta che esegui un programma, anche se è lo stesso programma, viene creata una nuova istanza.
Istanza di un Server o un Servizio
Nel cloud computing o in sistemi distribuiti, un’istanza può anche riferirsi a una singola unità di calcolo che esegue un servizio o un’applicazione. Ad esempio, in Amazon Web Services (AWS), un’istanza EC2 è una macchina virtuale con risorse computazionali dedicate, utilizzata per eseguire applicazioni o servizi specifici.
Istanza Giuridica: Definizione e Significato
In ambito giuridico, il termine “istanza” ha un significato diverso, ma conserva l’idea di “richiesta” o “azione” attivata in un determinato contesto.
Definizione Giuridica
Un’istanza giuridica è una richiesta formale presentata da una parte a un’autorità giudiziaria o amministrativa. Può riferirsi anche all’intero processo o procedimento giudiziario che si svolge davanti a un giudice o un tribunale.
Tipi di Istanza Giuridica
Istanza Processuale: Una fase o un grado di giudizio in un procedimento legale. Ad esempio, si parla di “prima istanza” per indicare il primo grado di giudizio presso un tribunale ordinario, dove viene esaminato per la prima volta il caso. Se la decisione viene impugnata, il caso può passare a una “seconda istanza” o “appello,” e così via.
Domanda o Petizione: Un’istanza può anche essere una domanda formale presentata a un giudice o a un tribunale per ottenere un provvedimento specifico. Ad esempio, una “istanza di fallimento” è una richiesta per dichiarare il fallimento di una società.
L’istanza giuridica è cruciale perché avvia, definisce e orienta il corso di un procedimento giudiziario. È il meccanismo attraverso il quale i diritti e le pretese delle parti vengono portati all’attenzione dell’autorità giudiziaria, che dovrà esaminare, giudicare e decidere.
Sia in informatica che in ambito giuridico, il termine “istanza” riflette l’idea di una manifestazione o applicazione concreta di un concetto o di un’azione formale. In informatica, si tratta di un’entità specifica derivata da un modello o un processo, mentre nel diritto, rappresenta una richiesta o un’azione formale avviata all’interno di un sistema giudiziario.
Gestisci Consenso
Per comprendere il flusso di utenti e poter garantire una migliore esperienza d'uso utilizziamo un unico cookie per l'analisi del traffico web. Ci teniamo alla tua privacy e non raccogliamo nulla che non ci interessa e non cediamo nessun dato. Per saperne di più leggi la nostra Privacy Policy.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
Cookie necessario al corretto funzionamento di Matomo Analytics per l'anali del traffico web.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.