Visualizza post

Questa sezione ti permette di visualizzare tutti i post inviati da questo utente. N.B: puoi vedere solo i post relativi alle aree dove hai l'accesso.


Topics - Trizio

Pagine: [1]
1
Base / List comprehension vs map e filter
« il: Dicembre 06, 2018, 12:44 »
Una normale list comprehension:

>>> foo = [i * 10 for i in (1, 2, 3)]


Map:

>>> foo = map(lambda i: i * 10, (1, 2, 3))


List comprehension con if test:

>>> foo = [i for i in range(1, 11) if i % 2 == 0]


Filter:

>>> foo = filter(lambda i: i % 2 == 0, range(1, 11))


In Python 3, map e filter ritornano sequenze virtuali. Ora, se ho bisogno di una lista, il problema non si pone: mi conviene usare una list comprehension.

Supponiamo invece che io abbia bisogno solo di un iterable, non mi importa se una sequenza fisica o virtuale; in questo caso, quale approccio conviene? Il Lutz non è particolarmente chiaro. La list comprehension è veloce, perché ogni iterazione è eseguita alla velocità del linguaggio C (parole del Lutz, eh). map e filter, d’altro canto, creano sequenze virtuali che occupano la memoria poco alla volta. Se io ho una sequenza di oggetti particolarmente massiccia, suppongo che map e filter siano più convenienti.

2
Base / Augmented assignment e oggetti mutabili (WTF)
« il: Novembre 15, 2018, 12:09 »
L'augmented assignment (eh, non so come si chiami in italiano) con il + su una lista non esegue una concatenation, ma il metodo extend:

>>> foo = bar = [1, 2, 3]
>>> foo += 4, 5
>>> foo
[1, 2, 3, 4, 5]
>>> bar
[1, 2, 3, 4, 5]


Già qui la cosa mi urta, ma tant'è. Dato che l'operazione è mappata su extend e non su una concatenation l'oggetto a destra può essere qualsiasi iterable (nell'esempio una tupla) e ovviamente l'operazione cambia sul posto l'oggetto, conseguenza da tenere presente in caso di shared reference (come nell'esempio sopra).

Il tutto mi sembra sbagliato. E su più livelli. Tanto per cominciare il + fa pensare ad una concatenation ed infatti sarebbe lecito aspettarsi una concatenation. Inoltre, a che diavolo serve un in-place change così insaspettato, quando si può chiamare esplicitamente extend e rendere tutto molto più chiaro?

La cosa si fa ancora più oscura quando prendiamo i set. Ora, i set hanno i vari metodi update che possono cambiarli sul posto. Constatato come funziona l'augmented assignment con le liste, ci si potrebbe aspettare che un'operazione come |= sia mappata sul metodo update. Non proprio, come si evince qui:

>>> foo = bar = {'a', 'b', 'c'}
>>> foo |= 'x', 'y', 'z'
TypeError: unsupported operand type(s) for |=: 'set' and 'tuple'

>>> foo |= {'x', 'y', 'z'}
>>> foo
{'b', 'c', 'a', 'y', 'z', 'x'}
>>> bar
{'b', 'c', 'a', 'y', 'z', 'x'}


L'operazione di sicuro non esegue una normale unione (ossia foo = foo | {'x', 'y', 'z'}) dato che cambia l'oggetto sul posto (i.e. non ne crea uno nuovo come invece fa l'unione).  Ma non è neppure mappata sul metodo update, dato che update accetta qualsiasi iterable, mentre il qui presente augmented assignment accetta solo set. Il risultato è insomma una versione azzoppata del metodo update.




3
Python-it.org cafè / L'importanza delle prestazioni
« il: Ottobre 20, 2018, 11:29 »
Salve. Premetto di essere un principiante. E non intendo perseguire la carriera di ingegnere informatico. Insomma, le mie basi non sono granché solide e sospetto di avere qualche concezione distorta dell'informatica.

La mia idea di fondo (ossia quello che ho dedotto leggendo cose scritte da altri) è che un programma deve soddisfare le esigenze di chi lo usa, compiendo il minimo sforzo possibile in termini di risorse, e garantendo a chi lo legge e modifica il minimo sforzo possibile. Per un motivo o per l'altro, la questione delle prestazioni mi lascia un po' perplesso.

Non ho dimestichezza con altri linguaggi di programmazione (a dirla tutta non è che abbia una gran dimestichezza nemmeno con Python), ma stando a quanto ho capito ci sono linguaggi di programmazione decisamente più performanti. Perché utilizzare Python, quando verosimilmente è possibile utilizzare per lo stesso scopo -- a titolo di esempio -- C?

Presumo che un programma scritto in C possa tranquillamente soddisfare le esigenze dei customer, possa essere scritto in modo tale da essere leggibile, e a quanto pare è pressoché certo che sfonderà la barriera del suono.

Eppure il successo di Python sembrerebbe in qualche modo sbugiardare questo modello. Okay, un programma scritto in Python è verosimilmente più facile da mantenere rispetto ad un equivalente scritto  in C, ma le sue prestazioni dovrebbero essere comunque inferiori. Di poco, di molto...? Immagino dipenda da cosa faccia effettivamente quel programma.

Nel mondo vero, quanto sono davvero importanti le prestazioni?

P.S. Okay, sono ancora ingenuo, ma non del tutto sprovveduto. Immagino che nel "mondo vero" (ambito business, scientifico, ecc.) entri in gioco anche il fattore tempo, e che progettare un software in C sia più dispendioso in termini di tempo (e il tempo è denaro) rispetto a uno in Python. Poi, ovvio, è relativo...

4
Base / String formatting
« il: Ottobre 15, 2018, 13:42 »
Salve. Domande banali. Python 3.5.

Quando nell'espressione passo un float al type code 'd', nella stringa risultante ottengo un intero (i.e. quello che mi aspetto):

>>> '%d' % 1.0
'1'


Quando faccio la stessa cosa con format ottengo un errore:

>>> '{:d}'.format(1.0)
ValueError: Unknown format code 'd' for object of type 'float'


I type code 'f', 'e' e 'g' accettano interi, sia con l'espressione che con format:

>>> '%f' % 1
'1.000000'

>>> '{:f}'.format(1)
'1.000000'


1) Devo per forza eseguire la conversione manualmente?

>>> '{:d}'.format(int(1.0))
'1'


2) Perché? A che diavolo serve il type code 'd' se non converte una cippa lippa?

3) Perché l'espressione e format devono convivere? Retrocompatibilità? Ha un senso per python2, non per python3.

5
Salve. Ultimamente mi sono un po' scocciato della pubblicità online e ho cercato un metodo alternativo ai vari ad-blocker di Firefox. Ho dunque provato ad usare il browser Links, per vedere se potevo rimpiazzare direttamente Firefox. Come soluzione al mio problema non è un granché  e alla fine ho optato per pi-hole installato sul mio raspberry.

Il fatto è che incidentalmente ho scoperto che Links sembrerebbe aggirare il paywall del Washington Post. È il caso di avvisarli?

6
Videogame / pygame e snake (tanto per cambiare)
« il: Ottobre 15, 2017, 14:22 »
In questi giorni ho cercato di applicare le nozioni fin qui apprese, creando il solito clone di snake. Per fare questo ho dato un'occhiata a vari programmi scritti da altri, cercando di capire la logica da implementare e soprattutto le varie funzioni di pygame che mi servivano. Ne è uscito qualcosa di interessante, non particolarmente originale, ma con qualche illuminazione su cui metto il mio copyright (joke).

Il gioco doveva seguire una particolare sequenza di azioni:
  • il serpente appare e scompare per tre secondi per far capire al giocatore che il gioco sta per cominciare;
  • main loop del gioco;
  • game over, con il serpente e la mela che diventano bianchi per tre secondi per far capire al giocatore che il gioco è finito.
Dopodiché il tutto si ripete fino a quando il giocatore non decide di chiudere il gioco premendo ESC o chiudendo la finestra normalmente. Niente menu e niente punteggio. Solo gameplay.

Ora, ho cercato di dividere la logica del programma in vari moduli e funzioni, così come si dovrebbe fare. Quello che vi chiedo non è di leggere TUTTO il programma (ci mancherebbe), bensì di darci una rapida occhiata per stabilire se è scritto bene o se invece è il caso di fare alcune modifiche (es. alcune funzioni possono  essere fattorizzate, ecc. ecc.)... in breve, se il programma è leggibile.

Se volete provare il gioco, potete semplicemente copiare i seguenti moduli e incollarli nella stessa directory. Per muovere il serpente bisogna usare i tasti WASD, come negli FPS.
Non ci sono file esterni. Inoltre, il tutto dovrebbe essere esente da bug -  fino a prova contraria - ho lavorato ai vari casi limite.

cnst.py
Questo modulo contiene le costanti utilizzate dal gioco. L'idea di piazzarle in un modulo a sé stante l'ho copiata da un platform scritto con pygame. Una buona idea, devo dire, rende il tutto molto più leggibile.

import pygame

WINDOWWIDTH = 320
WINDOWHEIGHT = 240
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))

CELLSIZE = 10
CELLWIDTH = WINDOWWIDTH / CELLSIZE
CELLHEIGHT = WINDOWHEIGHT / CELLSIZE
ALL_COORDS = []
for x in range(0, CELLWIDTH):
    for y in range(0, CELLHEIGHT):
        ALL_COORDS.append((x, y))

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED   = (255, 0, 0)

FPS = 15
CLOCK = pygame.time.Clock()



snake.py

def get_new_head(direction, old_head_coordinates):
    """Given a string which specify the snake's direction and a tuple
    containing the old head coordinates, return a tuple containing the
    new head coordinates.
    """
    x, y = old_head_coordinates
    if direction == 'up':
        return (x, y-1)
    elif direction == 'down':
        return (x, y+1)
    elif direction == 'right':
        return (x+1, y)
    elif direction == 'left':
        return (x-1, y)



apple.py

import random
from cnst import ALL_COORDS


def get_random_coords(snake_coords):
    """Given a list containing all the coordinates occupied by the snake,
    return a tuple of free coordinates.
    """
    all_free = [coords for coords in ALL_COORDS if coords not in snake_coords]
    return random.choice(all_free)



game.py
Il modulo principale.

import sys, pygame, snake, apple
from cnst import *
from pygame.locals import *


def get_cell_rect(coords):
    """Given a tuple containing a pair of coordinates, return a rect
    object.
    """
    x, y = coords
    return pygame.Rect(x*CELLSIZE+1, y*CELLSIZE+1, CELLSIZE-1, CELLSIZE-1)


def draw_cell_square(rect, color):
    """Given a rect object and a color, draw a square on the rect."""
    pygame.draw.rect(DISPLAYSURF, color, rect)


def initialize_game():
    # initialize snake's starting coordinates and direction
    snake_coords = [(5, 11), (4, 11), (3, 11)]
    snake_direction = 'right'
   
    # initialize first apple coordinates
    apple_coords = apple.get_random_coords(snake_coords)

    # create the needed rects
    snake_rects = [get_cell_rect(coords) for coords in snake_coords]

    # blink the snake for three seconds while checking if player wants to quit
    for half_sec in range(1, 7):
        for event in pygame.event.get():
            if event.type == QUIT:
                terminate()
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    terminate()
        else:
            for rect in snake_rects:
                if half_sec % 2 == 0:
                    draw_cell_square(rect, GREEN)
                else:
                    DISPLAYSURF.fill(BLACK)
            pygame.display.update(snake_rects)
            pygame.time.delay(500)
    return snake_coords, snake_direction, apple_coords


def run_game(snake_coords, snake_direction, apple_coords):
    while True:
        # manage pygame event queue
        event = pygame.event.poll()
        if event.type == QUIT:
            terminate()
        elif event.type == KEYDOWN:
            if event.key == K_a and snake_direction != 'right':
                snake_direction = 'left'
            elif event.key == K_d and snake_direction != 'left':
                snake_direction = 'right'
            elif event.key == K_w and snake_direction != 'down':
                snake_direction = 'up'
            elif event.key == K_s and snake_direction != 'up':                 
                snake_direction = 'down'
            elif event.key == K_ESCAPE:
                terminate()

        # add snake's new head inside its data structure
        new_head_coords = snake.get_new_head(snake_direction, snake_coords[0])
        snake_coords.insert(0, new_head_coords)

        # check if snake's new head has hit an edge
        x, y = new_head_coords
        if x == -1 or x == CELLWIDTH:
            return snake_coords, apple_coords
        if y == -1 or y == CELLHEIGHT:
            return snake_coords, apple_coords
     
        # check if snake's new head has hit its body
        for body_coords in snake_coords[1:]:
            if new_head_coords == body_coords:
                return snake_coords, apple_coords

        # check if snake's new head has eaten the apple
        if new_head_coords == apple_coords:
            apple_coords = apple.get_random_coords(snake_coords)
            tail_coords = snake_coords[-1]  # previous iteration tail
        else:
            tail_coords = snake_coords.pop()
       
        # create the needed rects
        head_rect = get_cell_rect(new_head_coords)
        tail_rect = get_cell_rect(tail_coords)
        apple_rect = get_cell_rect(apple_coords)

        # draw background, snake's head and apple
        DISPLAYSURF.fill(BLACK)
        draw_cell_square(head_rect, GREEN)
        draw_cell_square(apple_rect, RED)
       
        # update selected display rects
        rects = [head_rect, tail_rect, apple_rect]
        pygame.display.update(rects)
        CLOCK.tick_busy_loop(FPS)


def game_over(snake_coords, apple_coords):
    # create the needed rects
    snake_rects = [get_cell_rect(coords) for coords in snake_coords]
    apple_rect = get_cell_rect(apple_coords)

    # draw snake's body and apple
    for rect in snake_rects:
        draw_cell_square(rect, WHITE)
    draw_cell_square(apple_rect, WHITE)
   
    # update selected display rects
    rects = snake_rects[:]
    rects.append(apple_rect)
    pygame.display.update(rects)
   
    # freeze the game for three seconds but check if player wants to quit
    for half_sec in range(1, 7):
        for event in pygame.event.get():
            if event.type == QUIT:
                terminate()
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    terminate()
        else:
            pygame.time.delay(500)
   
    # clear the screen for next iteration
    DISPLAYSURF.fill(BLACK)
    pygame.display.update(rects)
    return


def terminate():
    pygame.quit()
    sys.exit()


def main():
    pygame.init()
    pygame.display.set_caption('Snake')

    # block unwanted events
    blocked_events = [MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN]
    pygame.event.set_blocked(blocked_events)

    while True:
        init_args = initialize_game()
        final_args = run_game(*init_args)
        game_over(*final_args)


if __name__ == '__main__':
    main()


L'"idea rivoluzionaria" di cui vi parlavo consiste nel fatto che ad ogni frame disegno solo la testa e la mela. Il corpo del serpente è solo un'"eco" della testa dei precedenti frame. Questo dovrebbe essere molto più efficiente e non l'ho visto fare da nessun altro. Già, niente di trascendentale, ma mi sembrava un'idea eccellente.

Comunque, le prime due funzioni in quest'ultimo modulo sono delle... ehm... utility. Probabilmente andrebbero scritte in un modulo a parte, ma non sapevo come chiamarlo. Servono rispettivamente a creare un oggetto rect e a disegnare un quadrato sullo stesso. La mela e il corpo del serpente sono uguali, cambia solo il colore, per cui le due funzioni servono per entrambi.

Per il resto, ho diviso le tre sequenze di gioco in tre funzioni distinte che si "passano" gli argomenti di cui hanno bisogno.

Che ne pensate? Qualcosa da migliorare? Il programma è scritto come farebbe un... ehm, professionista? È leggibile?

Ah, già, quasi me ne dimenticavo. Nelle funzioni initialize_game e game_over ho trovato un espediente per fermare l'immagine per tre secondi. Probabilmente non è granché, e di sicuro c'è un modo migliore, ma non ho trovato niente di meglio cercando (rapidamente) nella libreria di pygame. Praticamente metto il pausa il gioco ogni mezzo secondo e controllo che l'utente non abbia voglia di chiudere il gioco.

7
Base / Ordinare una lista di tuple
« il: Ottobre 01, 2017, 14:58 »
Data questa lista:

foo = [('spam', 3), ('ham', 1), ('egg', 2)]


Vogliamo ordinarla in base al secondo campo di ciascuna tupla. La cosa più semplice da fare dovrebbe essere questa:
>>> foo.sort(key=lambda i: i[1])
>>> foo
[('ham', 1), ('egg', 2), ('spam', 3)]


Qui, però, Guido van Rossum--ammesso che sia davvero lui--dice che:

Citazione
The sort method for lists takes an optional comparison function as an argument that can be used to change the sorting behavior. This is quite convenient, though it can really slow down your sorts.

E propone questo metodo:
>>> bar = [(x[1], x) for x in foo]
>>> bar.sort()
>>> foo = [y for (x, y) in bar]
>>> foo
[('ham', 1), ('egg', 2), ('spam', 3)]


Come può essere più efficiente se mi costringe a creare due nuove liste?

8
Esercizi / Soluzioni ad alcuni esercizi proposti da un utente
« il: Settembre 26, 2017, 16:22 »
Premessa
Salve! In questa discussione, un utente ha proposto degli esercizi ad un altro. Tuttavia, questi esercizi non sono stati risolti e, di conseguenza, la discussione si è inabissata. Mi sono permesso di risolverli, dato che anch'io sono alle prime armi, e gradirei che qualcuno me li correggesse e rispondesse ad alcune mie domande.


Esercizio 1
1. Scrivere una funzione che prende come parametri 3 numeri e ritorna la media.

def average(x, y, z):
    """Return the average of three numbers."""
    return (x + y + z) / 3.0


Dal poco codice che ho avuto modo di leggere finora, ho notato che non tutti sono soliti usare le docstring. Capisco quando si ha a che fare con funzioni semplici come questa, ma ho visto dei moduli molto più complessi, presumibilmente scritti da professionisti (o comunque gente più esperta di me), senza la benché minima presenza di commenti. È molto più agevole interpretare il codice di una funzione, se questa è introdotta da una breve docstring che ne riassuma lo scopo, pertanto credo che sia una buona  idea quella di imparare a scriverle sin da subito. Se avete consigli da dare in proposito o se avete qualcosa da ridire sulle mie docstring, non esitate a dire la vostra.

Quanto al codice, il divisore è 3.0 per via di Python 2.7.


Esercizio 2
2. [...]scrivere una funzione che prende come parametro una lista di numeri e un altro numero. Se la lista e' vuota, ritorna questo numero, altrimenti, ritorna la media dei numeri nella lista.

def seq_average(seq, default):
    """Return the average of a sequence of numbers; if the sequence is
    empty, return default.
    """
    if seq:
        return sum(seq) / float(len(seq))
    else:
        return default



Esercizio 3
3. Scrivere una funzione che conta le linee di "The Picture of Dorian Gray" (http://www.gutenberg.org/cache/epub/174/pg174.txt)
3a) Scrivere una funzione che ne conta le linee non vuote
3b) Scrivere una funzione che conta i caratteri.
3c) Scrivere una funzione che conta i capitoli.

Le funzioni prendono come input un'oggetto file precedentemente aperto (e.g.,

[codice]with open('pg174.txt') as fh:
    print count_lines(fh)[/codice]

def count_lines(input_file):
    """Return the number of lines in a text file."""
    lines_counter = 0
    for line in input_file: lines_counter += 1
    return lines_counter


def count_real_lines(input_file):
    """Return the number of non-blank lines in a text file."""
    lines_counter = 0
    for line in input_file:
        if not line.isspace(): lines_counter += 1
    return lines_counter


def count_chars(input_file):
    """Return the number of characters in a text file."""
    chars_counter = 0
    for line in input_file: chars_counter += len(line)
    return chars_counter


def count_chapters(input_file):
    """Return the number of chapters in... well, 174.txt"""
    chapters_counter = 0
    for line in input_file:
        if 'CHAPTER' in line: chapters_counter += 1
    return chapters_counter


L'ultima funzione, count_chapters,  può funzionare solo col file 174.txt o, al limite, con file che condividano la stessa impostazione. D'altro canto, non vedo proprio come sia possibile scrivere una funzione che funzioni sempre.


Esercizio 4
4. Scrivere una funzione che prende il solito oggetto file e ritorna un dizionario parola -> numero di occorrenze nel file. [...]  Nota, devi anche rimuovere la punteggiatura: "Yes," e "Yes!" sono la stessa parola. Non importa il case (puoi metterle tutte minuscole o maiuscole, come preferisci, ma i miei occhi preferiscono se sono tutte minuscole, visto che non sono un programmatore Cobol). Nota... "don't" per i nostri scopi conta come una parola.
4a. Come prima, ma saltiamo le parole di meno di 3 caratteri.
4b.Scrivere una funzione come quella in 4, che prende in aggiunta un set contenente delle stringhe. Queste parole saranno ignorate.

def count_words_occurrences(input_file):
    """Read a text file and return a dictionary containing all the words
    in the file as keys and the numbers of occurrences of these words as
    values.
    """
    punct = '!"#$%&'()*+,-./:;<=>?@[]^_`{|}~'
    words_occurrences = {}
    for line in input_file:
        for word in line.lower().replace('--', ' ').split():
            cleaned_word = word.strip(punct)
            if cleaned_word in words_occurrences:
                words_occurrences[cleaned_word] += 1
            else:
                if not cleaned_word: continue        # get rid of empty strings
                words_occurrences[cleaned_word] = 1
    return words_occurrences


def count_words_occurrences2(input_file):
    """Read a text file and return a dictionary containing all the words
    in the file with more that three characters as keys and the numbers
    of occurrences of these words as values.
    """
    punct = '!"#$%&'()*+,-./:;<=>?@[]^_`{|}~'
    words_occurrences = {}
    for line in input_file:
        for word in line.lower().replace('--', ' ').split():
            cleaned_word = word.strip(punct)
            if cleaned_word in words_occurrences:
                words_occurrences[cleaned_word] += 1
            else:
                if len(cleaned_word) <= 3: continue
                words_occurrences[cleaned_word] = 1
    return words_occurrences


def count_words_occurrences3(input_file, skip=set()):
    """Read a text file and return a dictionary containing all the words
    in the file but not in skip (if given) as keys and the numbers of
    occurrences of these words as values.
    """
    punct = '!"#$%&'()*+,-./:;<=>?@[]^_`{|}~'
    words_occurrences = {}
    for line in input_file:
        for word in line.lower().replace('--', ' ').split():
            cleaned_word = word.strip(punct)
            if cleaned_word in words_occurrences:
                words_occurrences[cleaned_word] += 1
            else:
                if not cleaned_word or cleaned_word in skip: continue
                words_occurrences[cleaned_word] = 1
    return words_occurrences


Questo esercizio mi ha fatto sudare per ore. Ho scritto almeno una mezza dozzina di versioni della prima funzione, count_words_occurrences, e mi auguro vivamente che ne sia valsa la pena.

La riga 6 contiene una stringa con tutti i caratteri di punteggiatura. In altre parole, è quello che restituisce la costante string.punctuation nel modulo string. Importare un modulo per una stringa mi sembrava poco efficiente, pertanto ho deciso di "assemblarmi" la stringa da me. P.S. Il sito sembrerebbe interpretare i caratteri backslash all'interno della stringa, modificandola.

Alla riga 9,  replace serve a separare le parole che nel testo sono unite da un doppio trattino (il libro ne è pieno). Al posto del doppio trattino, inserisce uno spazio, così che il successivo metodo split riesca a creare una lista con tutte le parole presenti sulla riga. In assenza di istruzioni in merito, le parole unite da un trattino (per esempio, "false-jewelled") le ho considerate come una singola parola; in caso contrario, è sufficiente inserire un altro replace sulla stessa riga (ovviamente prima di split).

Ho notato che l'operazione di rimozione della punteggiatura (riga 10), in combinazione con le operazioni della riga precedente, creava delle stringhe vuote quando processava le seguenti "parole":

>>> for line in open('174.txt'):
...     for word in line.lower().replace('--', ' ').split():
...         if len(word.strip('!"#$%&'()*+,-./:;<=>?@[]^_`{|}~')) == 0: print word
...
***
***
"
"
...
...
...
...
...
"
..."
...
...?"
...
...
...
"
...
?
"
"
...?"
...
***
***
*****
*****
***
***
-
-
-
-
-
-
-


Si tratta dunque di voci che posso evitare di inserire nel dizionario, dal momento che non si tratta di vere parole. È quello che faccio con quello strano test alla riga 14. Originariamente lo avevo scritto in questo modo:

[...]
cleaned_word = word.strip(punct)
if not cleaned_word: continue        # get rid of empty strings     
if cleaned_word in words_occurrences:
    words_occurrences[cleaned_word] += 1
else:
    words_occurrences[cleaned_word] = 1
[...]


Ma poi ho pensato che controllare tutte le parole era inutile: bastava controllare quelle che inserivo nel dizionario. In questo modo la funzione dovrebbe essere più efficiente, no?

Per quanto riguarda le altre due funzioni, beh, praticamente la logica è la stessa. Nella seconda, count_words_occurrences2, ho cambiato unicamente il suddetto test; dato che dovevo eliminare le parole composte da meno di tre caratteri, quel len si dovrebbe occupare anche delle stringhe vuote. Nella terza, ho fatto sì che il secondo argomento fosse di default un set vuoto, cosicché l'utente può decidere se inserire o meno un set con parole da saltare.

Per ora ho fatto questi esercizi. Ne mancano due, ma sono troppo stanco per affrontarli adesso. Se avete critiche da fare, suggerimenti, insulti...--perché no?--scrivete pure. In ogni caso, grazie per essere arrivati alla fine di questo lungo post (ammesso che l'abbiate letto tutto).

Pagine: [1]