Traccia della lezione Lez02 Lucido 2: Supponiamo ora di voler stampare un banner analogo al contenuto del file HELLO.TXT. Come ci organizziamo? Si potrebbe stampare: - un carattere alla volta, come fa il codice HELLOWRONG1.C - tutto in una volta, come fa il codice HELLOWRONG2.C - una stringa alla volta, dove "stringa" sta per qualsiasi brano del testo da stampare, come fa il codice HELLOWRONG3.C Sono tre scomposizioni "sbagliate" (la seconda non è neppure una scomposizione), perché i sottoproblemi considerati non hanno un significato intuitivo. Se si compilano i tre codici, si vedrà che funzionano correttamente, ma questo non vuol dire che siano programmi "accettabili". Lucido 5: I "requisiti" di un problema sono la relazione che si vuole ottenere fra i dati e i risultati, fra l'istanza e la soluzione del problema. Lucido 7: Si noti quanto frequenti siano le ripetizioni di sottocompiti, appartenenti a compiti diversi o allo stesso compito. * scrivere HELLO - scrivere H + scrivere * * (6 volte in complesso) + scrivere ***** - scrivere E + scrivere ***** (3 volte in complesso) + scrivere * (4 volte in complesso) - scrivere L (2 volte) + scrivere * (6 volte in complesso) + scrivere ***** - scrivere O + scrivere ***** (2 volte in complesso) + scrivere * * (5 volte in complesso) Lucido 8: Un approccio puramente BOTTOM-UP funzionerebbe così: sapendo già stampare sequenze di caratteri uguali -> si combinano le sequenze in righe più complesse -> si combinano le righe in griglie rettangolari (lettere) -> si combinano le lettere in parole Lucido 9: Si proceda a realizzare in maniera top-down il codice HELLO-TOPDOWN.C partendo dal file HELLO.C, che contiene solo alcune direttive #define, l'inclusione delle librerie standard (STDIO.H e STDLIB.H) e della libreria ausiliaria per l'input-output (IOCREMA.H e IOCREMA.C) che ci fornirà le funzioni ACapo, StampaCarattere, e StampaStringa, e un main vuoto. Ci vuole poco più di mezz'ora: si noti che le singole righe del codice sono pesantemente riutilizzate, tanto che dopo un po' non occorre quasi piu' scendere al livello basso perché l'approccio bottom-up consente di fermarsi prima: - inserire nel main una funzione StampaHELLO(), dichiararne il prototipo sopra il main e definirla come funzione vuota sotto il main - definire StampaHELLO() usando nuove funzioni StampaH(), StampaE(), StampaL() e StampaO(), per ciascuna delle quali si dichiara un prototipo sopra il main e si dà una definizione vuota sotto il main - definire StampaH() usando funzioni StampaI___I() e StampaIIIII() per ciascuna delle quali si dichiara un prototipo sopra il main e si dà una definizione vuota sotto il main - soffermarsi sull'importanza di scegliere per ogni funzione un nome immediatamente chiaro - definire StampaI___I() usando StampaCarattere() e ACapo() - fare lo stesso per StampaIIIII() - definire StampaE()... - eccetera... (si noti il modo in cui si procede lungo l'albero delle funzioni, riportato nel lucido 9) - verificare il risultato * redirezione dell'output * FC per confrontare con l'output atteso E' fondamentale acquisire manualità nel procedimento: - inserire una chiamata a funzione nel main o in una procedura - se la funzione è nuova, inserire il prototipo in alto - se la funzione è nuova, inserire la definizione vuota in basso e procedere a riempirla, ripetendo il meccanismo Seguendo questo metodo, il codice è perfettamente compilabile senza errori ad ogni passo. Questo è utilissimo, perché permette di scoprire al più presto molti errori (ovviamente, non tutti). Domanda: qual è il limite oltre il quale diventa folle decomporre le operazioni? Dipende da quanto si prevede di riusare le procedure che si stanno scrivendo. Lucido 10: La programmazione strutturata è un'estensione naturale dell'approccio top-down ma anche dell'approccio bottom-up, se correttamente inteso. In C, "blocco" è tutto ciò che è racchiuso fra parentesi graffe. Tutte le funzioni sono blocchi, ma vi sono blocchi che non sono funzioni. Si noti che i commenti sono necessari, ma sono l'ultima risorsa: il miglior codice è quello che non ha bisogno di commenti perché si capisce leggendo. Anche in questo caso, spesso i manuali aziendali di stesura del software richiedono di aggiungere commenti. Lucido 12: Negli schemi a blocchi le attività di processo sono i blocchi rettangolari, alcune attività di gestione sono le frecce e i blocchi rombici (allocazione e deallocazione non compaiono negli schemi a blocchi) Lucido 13: Sequenza La soluzione è corretta se le azioni sono eseguite in una sequenza data. Se scambiamo alcuni passi la soluzione potrebbe non essere corretta. Esempio: StampaHELLO richiede un ordine ben preciso fra le lettere Esempio: IL BANCOMAT. 1. Inserire la tessera 2. Digitare il PIN 3. Recuperare il contante 4. Recuperare la tessera Negli schemi a blocchi, un blocco sequenziale è un blocco rettangolare, legato da frecce al precedente e al successivo. In C, un blocco è un insieme di istruzioni racchiuse fra {}. Selezione: Negli schemi a blocchi, il costrutto di selezione è un blocco rombico con due o più uscite dirette ai blocchi che rappresentano le azioni alternative. In C avremo costrutti "if", "if else", ecc... Esempio: Se PIN immesso dall’utente è uguale al PIN registrato in banca: - allora il BANCOMAT eroga i soldi; - altrimenti all’utente è richiesto di digitare un nuovo PIN. L'espressione logica è: "il PIN immesso dall’utente è uguale al PIN registrato in banca" Essa può essere solo o vera o falsa. Esempio: Stampa il massimo fra due numeri Iterazione Negli schemi a blocchi, il costrutto di iterazione è un blocco rombico con due uscite, una delle quali prosegue, mentre l'altra è diretta al blocco da eseguire, dal quale a sua volta esce una freccia che torna all'ingresso del blocco iterativo. In C avremo costrutti "while", "do while", "for", ecc... Esempio: 1. Inserire la tessera 2. Digitare il PIN 3. Controllare il PIN: - finché il codice non è corretto, cancellarlo e tornare al punto 2 4. Recuperare il contante 5. Recuperare la tessera L'espressione logica che impone la ripetizione è: "il codice non è corretto" Le strutture si possono collegare fra loro in sequenza, oppure annidare una dentro l'altra.