-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcomp202-notes.html
More file actions
1 lines (1 loc) · 231 KB
/
comp202-notes.html
File metadata and controls
1 lines (1 loc) · 231 KB
1
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>COMP-202: Foundations of Programming</title><meta name="next-head-count" content="3"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hack-font@3/build/web/hack-subset.css"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css"/><base href="/comp202-notes/"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /><link rel="preload" href="/_next/static/css/ddaa7ed3a1cb9943.css" as="style"/><link rel="stylesheet" href="/_next/static/css/ddaa7ed3a1cb9943.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js"></script><script src="/_next/static/chunks/webpack-a0deeec5c85c92d3.js" defer=""></script><script src="/_next/static/chunks/framework-ffee79c6390da51e.js" defer=""></script><script src="/_next/static/chunks/main-b714c06b389f6489.js" defer=""></script><script src="/_next/static/chunks/pages/_app-7ea878ea5ec92829.js" defer=""></script><script src="/_next/static/chunks/pages/comp202-notes-3aba4b62b9cbd94d.js" defer=""></script><script src="/_next/static/tDjRbwhe6GKkHs3U2EVUf/_buildManifest.js" defer=""></script><script src="/_next/static/tDjRbwhe6GKkHs3U2EVUf/_ssgManifest.js" defer=""></script><style data-href="https://fonts.googleapis.com/css2?family=Encode+Sans+SC:wght@300&display=swap">@font-face{font-family:'Encode Sans SC';font-style:normal;font-weight:300;font-stretch:normal;font-display:swap;src:url(https://fonts.gstatic.com/s/encodesanssc/v9/jVyp7nLwCGzQ9zE7ZyRg0QRXHPZc_uUA6Kb3VJWLE_Pdtm7lcD6qvXT1HCZm8cw.woff) format('woff')}@font-face{font-family:'Encode Sans SC';font-style:normal;font-weight:300;font-stretch:100%;font-display:swap;src:url(https://fonts.gstatic.com/s/encodesanssc/v9/jVyp7nLwCGzQ9zE7ZyRg0QRXHPZc_uUA6Kb3VJWLE_Pdtm7lcD6qvXT1HCZmwcdHP2MHhIbVOo20.woff) format('woff');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Encode Sans SC';font-style:normal;font-weight:300;font-stretch:100%;font-display:swap;src:url(https://fonts.gstatic.com/s/encodesanssc/v9/jVyp7nLwCGzQ9zE7ZyRg0QRXHPZc_uUA6Kb3VJWLE_Pdtm7lcD6qvXT1HCZmwcZHP2MHhIbVOo20.woff) format('woff');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Encode Sans SC';font-style:normal;font-weight:300;font-stretch:100%;font-display:swap;src:url(https://fonts.gstatic.com/s/encodesanssc/v9/jVyp7nLwCGzQ9zE7ZyRg0QRXHPZc_uUA6Kb3VJWLE_Pdtm7lcD6qvXT1HCZmwchHP2MHhIbVOg.woff) format('woff');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style><style data-href="https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,350;0,430;1,350;1,430&display=swap">@font-face{font-family:'Work Sans';font-style:italic;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY9z_wNahGAdqQ43Rh_ebrnlwyYfEPxPoGUgGsJoA.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY9z_wNahGAdqQ43Rh_ebrnlwyYfEPxPoGU3msJoA.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY9z_wNahGAdqQ43Rh_ebrnlwyYfEPxPoGU7GsJoA.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY_z_wNahGAdqQ43RhVcIgYT2Xz5u32KxfXNis.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY_z_wNahGAdqQ43RhVcIgYT2Xz5u32K0nXNis.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGY_z_wNahGAdqQ43RhVcIgYT2Xz5u32K3vXNis.woff) format('woff')}@font-face{font-family:'Work Sans';font-style:italic;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDkv_1i4_D2E4A.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Work Sans';font-style:italic;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDlv_1i4_D2E4A.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Work Sans';font-style:italic;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDrv_1i4_D2.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Work Sans';font-style:italic;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDkv_1i4_D2E4A.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Work Sans';font-style:italic;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDlv_1i4_D2E4A.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Work Sans';font-style:italic;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYqz_wNahGAdqQ43Rh_eZDrv_1i4_D2.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Work Sans';font-style:normal;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_c6DptfpA4cD3.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Work Sans';font-style:normal;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_cqDptfpA4cD3.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Work Sans';font-style:normal;font-weight:350;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_fKDptfpA4Q.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Work Sans';font-style:normal;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_c6DptfpA4cD3.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Work Sans';font-style:normal;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_cqDptfpA4cD3.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Work Sans';font-style:normal;font-weight:430;font-display:swap;src:url(https://fonts.gstatic.com/s/worksans/v19/QGYsz_wNahGAdqQ43Rh_fKDptfpA4Q.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style><style data-href="https://fonts.googleapis.com/css2?family=Alegreya+SC:wght@400;500&family=Alegreya:ital,wght@0,400;0,500;1,400;1,500&family=Lato:ital,wght@0,300;0,400;1,300;1,400&display=swap">@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaSrEBBsBhlBjvfkSLk3abBFkvpkARTPlbgv6ql.woff) format('woff')}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaSrEBBsBhlBjvfkSLk3abBFkvpkARTPlbSv6ql.woff) format('woff')}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UacrEBBsBhlBjvfkQjt71kZfyBzPgNG9hUI_w.woff) format('woff')}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UacrEBBsBhlBjvfkQjt71kZfyBzPgNGxBUI_w.woff) format('woff')}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-a6r.woff) format('woff')}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-rUy.woff) format('woff')}@font-face{font-family:'Lato';font-style:italic;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u_w4BMUTPHjxsI9w2PHw.woff) format('woff')}@font-face{font-family:'Lato';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u8w4BMUTPHjxswWA.woff) format('woff')}@font-face{font-family:'Lato';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh7USeww.woff) format('woff')}@font-face{font-family:'Lato';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHvxo.woff) format('woff')}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96fp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk967p57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96bp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96np57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96Xp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96Tp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96rp57F2IwM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96fp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk967p57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96bp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96np57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96Xp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96Tp57F2IwN-Pw.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaHrEBBsBhlBjvfkSLk96rp57F2IwM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLsx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLlx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLtx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLix6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLux6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLvx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLhx6jj4JN0.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLsx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLlx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLtx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLix6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLux6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLvx6jj4JN0EwI.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreya/v35/4UaBrEBBsBhlBjvfkSLhx6jj4JN0.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6i2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6r2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6j2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6s2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6g2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6h2ZAbaqe-LGs.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiOGmRtCJ62-O0HhNEa-Z6v2ZAbaqe-.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU7SKqUFmKCJz4.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oUySKqUFmKCJz4.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU6SKqUFmKCJz4.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU1SKqUFmKCJz4.woff2) format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU5SKqUFmKCJz4.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU4SKqUFmKCJz4.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Alegreya SC';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/alegreyasc/v25/taiTGmRtCJ62-O0HhNEa-ZZc-oU2SKqUFmKC.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Lato';font-style:italic;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u_w4BMUTPHjxsI9w2_FQftx9897sxZ.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Lato';font-style:italic;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u_w4BMUTPHjxsI9w2_Gwftx9897g.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Lato';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u8w4BMUTPHjxsAUi-qNiXg7eU0.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Lato';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u8w4BMUTPHjxsAXC-qNiXg7Q.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Lato';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh7USSwaPGQ3q5d0N7w.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Lato';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh7USSwiPGQ3q5d0.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Lato';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHjxAwXiWtFCfQ7A.woff2) format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Lato';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/lato/v24/S6uyw4BMUTPHjx4wXiWtFCc.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style></head><body><div id="__next"><style data-emotion="css 1gas0fl">.css-1gas0fl{margin:5em 25%;width:70rem;}</style><div class="css-1gas0fl"><style data-emotion="css 12putv1">.css-12putv1{margin:0.5em 0;}</style><div class="css-12putv1"><a href="Lecture-1.1.html">1.1 — Programming Basics</a></div><div class="css-12putv1"><a href="Lecture-1.2.html">1.2 — Variables, Arithmetic & String operations</a></div><div class="css-12putv1"><a href="Lecture-2.1.html">2.1 — Function calls, Built-in functions, Expressions</a></div><div class="css-12putv1"><a href="Lecture-2.2.html">2.2 — Defining functions, Comparing Values, Control flow statements</a></div><div class="css-12putv1"><a href="Lecture-2.3.html">2.3 — return vs. print, while statement</a></div><div class="css-12putv1"><a href="Lecture-3.1.html">3.1 — Modules, String methods, break & continue statements</a></div><div class="css-12putv1"><a href="Lecture-3.2.html">3.2 — Controlling print(), Nested Loops</a></div><div class="css-12putv1"><a href="Lecture-3.3.html">3.3 — Scope of variables, Lists</a></div><div class="css-12putv1"><a href="Lecture-4.1.html">4.1 — List methods, Tuples, Immutable objects</a></div><div class="css-12putv1"><a href="Lecture-4.2.html">4.2 — Sets, Dictionaries</a></div><div class="css-12putv1"><a href="Lecture-5.1.html">5.1 — Iterables, More dict & list operations, Nested lists</a></div><div class="css-12putv1"><a href="Lecture-5.2.html">5.2 — Nested data structures, Comprehensions</a></div><div class="css-12putv1"><a href="Lecture-5.3.html">5.3 — Sorting, Modules, Reading & Writing Files</a></div><div class="css-12putv1"><a href="Lecture-6.1.html">6.1 — Files, Shallow vs. deep copy</a></div><div class="css-12putv1"><a href="Lecture-6.2.html">6.2 — Handling exceptions, Object Oriented Programming (OOP)</a></div><div class="css-12putv1"><a href="Lecture-7.1.html">7.1 — Object Oriented Programming (OOP)</a></div><div class="css-12putv1"><a href="Lecture-7.2.html">7.2 — More OOP, Plotting using Matplotlib</a></div><div class="css-12putv1"><a href="Lecture-7.3.html">7.3 — NumPy & Misc. topics</a></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"allPosts":[{"slug":"Lecture-1.1.md","content":"\n:::blockquote{.notes-only}\nIn the beginning was the Tao. The Tao gave birth to Space and Time.\nTherefore Space and Time are the Yin and Yang of programming.\n:cite[— [Tao of Programming](https://www.mit.edu/~xela/tao.html)]\n:::\n\n## Binary Numbers \n\nIn decimal system, a number is expressed as a sequence of digits $0$ to $9$.\n\nFor example, `Two thousand twenty one ⇔ 2021`\n\n##\n\nIn binary number system the set of digits, is called binary digits or :sc[bits]: $\\{0, 1\\}$. \n\nA binary number is expressed as a sequence of bits. \n\nFor example, $183$ in binary is $10110111$.\n\n## Converting from decimal to binary\n\n::decimal-binary\n\n## Converting from binary to decimal\n\n::binary-decimal\n\n## Groups of bits\n\nA group of $8$ bits is called a :i[byte] e.g. $11010111$\n- $1$ kilobyte (kB) = $1000$ bytes\n- $1$ megabyte (MB) = $10^6$ (million) bytes\n- $1$ gigabyte (GB) = $10^9$ (billion) bytes\n- $1$ terabyte (TB) = $10^{12}$ bytes (1000 billion)\n\n## What is programming? \n\n:::div{.gap1}\nProgramming is the process of creating a set of instructions — a program — to tell a computer how to perform a task.\n\nPrograms take input data, perform some computation — numerical or symbolic (text) — and produce output data.\n:::\n\n##\n\n:::div{.gap1}\nComputers can perform only basic binary operations (such as add or multiply two numbers)\n\nHow do we communicate complex instructions to computers? — Use a programming language!\n:::\n\n## Levels of programming languages\n\n:::div{.my2}\n| Low-level languages | High-level languages |\n|--------------------------------------------------------|---------------------------------------------------|\n| Closer to machine, difficult for humans | Closer to humans, easier for humans to work with |\n| | |\n| Less portable, provide less abstraction over hardware | More portable, more abstraction over hardware |\n| Examples: Assembly Language | Examples: Java, Python |\n:::\n\n\n## How do computers understand high-level languages?\n\n:::div{.gap1}\nHigh-level languages are translated into machine code (for CPU). \n\nProgramming languages come in two main flavors — :i[compiled] languages or :i[interpreted] languages.\n\nCompilers and interpreters are software tools responsible to translate source code into machine code.\n:::\n\n##\n\n:::div{style=\"margin-bottom: 1em;\"}\n:sc[Compiled] languages (e.g. C/C++, Java) \n\n- High-level program (source code) ➞ :sc[Compiler] ➞ Binary executable (e.g. .exe or .dmg)\n- Once compiled, the binary program can be executed without compiler.\n:::\n\n:::div\n:sc[Interpreted] (e.g. Python, Ruby)\n\n- High-level program (source code) ➞ Executed directly by an :sc[Interpreter]\n- The interpreter is required on the machine where the program is executed.\n:::\n\n::divider\n\n## Data in binary\n\nComputers can understand only binary numbers \n\nHow can we encode data in the real world into binary numbers?\n\n::img{src=\"module-1/data-in-binary.svg\" style=\"margin: 2rem auto; width:100%;\" }\n\n## Integers in binary\n\nWe already saw how to represent positive integers in binary e.g. \n:div[$109 = 1101101_2$]{style=\"text-align: center;\"}\n\nFor signed integers (to differentiate negative and positive), an extra leftmost bit is used for sign only, e.g. \n :div[$-109 = \\colorbox{lightblue}{1}1101101_2$]{style=\"text-align: center;\"} \n :div[$+109 = \\colorbox{lightblue}{0}1101101_2$]{style=\"text-align: center;\"}\n\n::div[(For more info: https://en.wikipedia.org/wiki/Signed_number_representations)]{.smaller}\n\n## Real numbers in binary\n\n64-bit :sc[Floating point] format is used to represent numbers with decimal point, e.g. \n:div[$\\colorbox{lightblue}{0}\\colorbox{lightpink}{10000000000}\\colorbox{lightgreen}{1001001000011111101101010100010001000010110100011000} = 3.141592653589793$]{style=\"text-align: center;\"}\n\n::div[(For more info: https://en.wikipedia.org/wiki/Double-precision_floating-point_format)]{.smaller}\n\n##\n\nFloating point format has a :i[finite precision], but digits of $\\pi$ run forever: :div[$3.1415926535897932384626433832795028841...$]{style=\"text-align: center;\"}\n\nWith only 64-bits, we can only have precision up to a fixed digits after decimal point: $3.141592653589793$\n\n\n\n## Text in binary\n\n::p[Letters and punctuations in human languages are encoded in binary using a :i[Character Encoding] such as ASCII or UTF-8 (Unicode).]{.ppt-m-3}\n\n::image{style=\"margin: 0 auto; width: 80%;\" src=\"module-1/ASCII-Table.png\" .ppt-img50} \n::div[(source: https://simple.wikipedia.org/wiki/ASCII)]{style=\"font-size: 0.6em; margin: 0 auto;\"}\n\n## Images, audio \u0026 video in binary\n\nBinary data is stored in a file using a specific format.\n\nPrograms know what to do (play music, show image, etc) based on the format. \n\nWe already know some of these formats:\n- Images: jpeg, png\n- Audio: mp3, m4a, wma\n- Video: mp4, avi, wmv\n\n\n::divider","title":"1.1 — Programming Basics","date":"2024-05-01","published":true},{"slug":"Lecture-1.2.md","content":"\n\n## Thonny Demo — Editor vs Shell\n\nPython interpreter works in two modes:\n- An interactive :sc[Shell] mode (with the prompt `\u003e\u003e\u003e`)\n - Line(s) of code is executed immediately as soon entered and output is visible immediately\n- :sc[Script] mode\n - Executes a Python file (`.py`) as a program.\n\nThonny allows us to use both modes in one graphical interface.\n\n## Comments\n\nComments are annotations we add to our program and are ignored by the Python interpreter.\n\nIn Python, we start a comment using `#`.\n\n```python\n# Author: Deven\n# My first program\n\n# This is a comment on its own line \u0026 it will be ignored\nprint(\"Hello, world!\") # str\nprint(123) # int\nprint(1.614) # float \n```\n\n## \nWe use comments to:\n- Make the code easier to read and understand by explaining how it works.\n- Indicate authorship and license.\n- Disable some code (prevent it from executing) but still keeping it in the file.\n\n\nIn Thonny, we can use `Edit menu -\u003e Toggle comment` to comment/uncomment the selected lines.\n\n\n## Objects and Data Types\n\nAll data in a Python program is represented by :sc[objects]. \n\nAn object always has a :sc[type] (or :sc[class]) associated with it.\n\nWe can use `type()` function to know the type of an object.\n\n:::hgrid{gap=\"7em\"}\n```python lineno=false\n\u003e\u003e\u003e type(5)\n\u003cclass 'int'\u003e\n```\n\n::image{style=\"margin: 0 auto; width: 40%;\" src=\"module-1/int.svg\" .ppt-img100} \n:::\n\n##\n\n::::div{style=\"display: grid; gap: 3em;\"}\n:::hgrid{gap=\"7em\"}\n```python lineno=false\n\u003e\u003e\u003e type(3.1415)\n\u003cclass 'float'\u003e\n```\n\n::image{style=\"margin: 0 auto; width: 40%;\" src=\"module-1/float.svg\" .ppt-img100} \n:::\n\n:::hgrid{gap=\"7em\"}\n```python lineno=false\n\u003e\u003e\u003e type(\"Hello\")\n\u003cclass 'str'\u003e\n```\n\n::image{style=\"margin: 0 auto; width: 40%;\" src=\"module-1/str.svg\" .ppt-img100} \n:::\n::::\n\n##\n\nAn object's type determines the operations that the object supports:\n\n```python\n# objects of int type can be added using +\n\u003e\u003e\u003e 10 + 5\n15\n\n# But an object of type str cannot be added to an int using +\n\u003e\u003e\u003e \"Hello\" + 5\nTraceback (most recent call last):\n File \"\u003cpyshell\u003e\", line 1, in \u003cmodule\u003e\nTypeError: can only concatenate str (not \"int\") to str\n```\n\n## Summary\n \nWe saw the three basic data types in Python:\n- `int`: Integers such as $..., -1, 0, 1, 2, ...$\n- `float`: Floating-point numbers such as $-1.2, 3.14,$ etc.\n- `str`: Text data (a sequence of characters) such as \"hello world\", \"Python\", etc.\n\nThe terms :sc[Object] and :sc[Value] are used interchangeably. \nSo are the terms :sc[Class] and :sc[Type].\n\n\n## Variables\n\nIn Python, a :sc[Variable] is a name that refers to an object in computer memory. \nA variable can be created using :sc[Assignment Statement]: \n\n:::div{.center .code .my2}\n:span[variable_name = value]{.bgblue .p1 .br5}\n:::\n\n`=` is known as the :sc[assignment operator].\n\nAssignment statement does the following:\n\n1. Value/Object on right of `=` is created.\n2. The name `variable_name` is created if it does not exist.\n3. `variable_name` is bound (associated) to the value/object.\n\n##\n\n```python\n# create a variable and assign it value 20\ntemperature = 20 \n\n# variable temperature refers to 20 which is displayed\nprint(\"Today's temperature is\", temperature)\n\n# show type of the variable\nprint(\"Type of temperature variable is\", type(temperature))\n```\n\n```output\nToday's temperature is 20\nType of temperature variable is \u003cclass 'int'\u003e\n```\n\n## Arithmetic with numbers\nCalculations with numbers can be done using :i[arithmetic operators].\n\n::::div{.hgrid}\n:::div\n```python\n# Addition\nprint(1.5 + 1.5) # 3.0\n\n# Subtraction\nprint(10 - 20) # -10\n```\n:::\n:::div\n```python\n# Multiplication\nprint(42 * 42) # 1764\n\n# Division\nprint(1 / 5) # 0.2\n\n# Exponentiation (x to the power of y)\nprint(2 ** 16) # 65536\n```\n:::\n::::\n\n##\n\n```python\ntemperature = 20\n# Unary minus operator\nprint(-temperature) # -20\n\n\n# Computing rest mass energy of an electron\nrest_mass = 9.109e-31 # Using scientific notation\nspeed_of_light = 3e8\n\nrest_mass_energy = rest_mass * (speed_of_light ** 2) # E = mc^2\nprint(rest_mass_energy) # 8.198099999999999e-14\n```\n\n## Floor division and remainder\n\n::img{src=\"module-2/divmod.svg\" style=\"margin:2em;\"}\n\n\n```python\n# floor division\nprint(20 // 3) # 6\n\n# remainder\nprint(20 % 3) # 2\n```\n\n## \n\n```python\n# Converting seconds to minutes\n\nduration = 320\nprint(duration, \"seconds equal\", duration / 60, \"minutes.\")\n# 320 seconds equal 5.333333333333333 minutes.\n\n\n# Alternative approach:\nminutes = duration // 60\nseconds = duration % 60\nprint(duration, \"seconds equal\", minutes, \"minutes and\", \n seconds, \"seconds.\")\n# 320 seconds equal 5 minutes and 20 seconds.\n```\n\n## Result type of arithmetic operations\n\n::image{style=\"margin: 0 auto; width: 65%;\" src=\"module-2/result_type.png\" .ppt-img80} \n\n##\n```python\nx = 2 + 1\nprint(x, type(x)) # 3 \u003cclass 'int'\u003e\n\nx = 2 + 1.0\nprint(x, type(x)) # 3.0 \u003cclass 'float'\u003e\n\n# Classic division always results in float\nx = 1 / 2 \nprint(x, type(x)) # 0.5 \u003cclass 'float'\u003e\n```\n\n:::div{.px2 .py1 .my2 style=\"border: solid 1px lightgray; border-radius:5px; background-color: #d6efd6;\"}\n:b[Try the above examples with other operators!]{.p0}\n:::\n\n##\n\nTry the problem \"Distance between two points\" on Ed.\n\n## Basic string operations\n\nStrings are sequences of zero or more characters.\n\nIn Python, strings are enclosed by either single or double quotes.\n \n```python\n\"Hello\"\n'everyone!'\n\"I'm Batman.\" # single quote allowed inside double quotes,\n'You can call me \"Bruce\".' # and vice versa.\n'123' # this is a string, not a number!\n\"\" # this is an empty string\n\" \" # this is a string with just one space\n```\n\n##\n```python\n# a multi-line string using triple quotes\nlines = \"\"\"The woods are lovely, dark and deep, \nBut I have promises to keep, \nAnd miles to go before I sleep, \nAnd miles to go before I sleep.\n\"\"\"\nprint(lines)\n\n# We can also use single quotes for multi-line strings\nprint(\n'''I hold it true, whate'er befall;\nI feel it when I sorrow most;\n'Tis better to have loved and lost\nThan never to have loved at all.\n''')\n```\n\n## String concatenation (joining) using `+` operator\n\n```python\nmessage = \"Hello\" + \"everyone\"\nprint(message) # Helloeveryone\n\nname = \"Alice\"\nmessage = \"Hello \" + name\nprint(message) # Hello Alice\n\nstring = \"1\" + \"2\" + \"3\"\nprint(string) # 123 and not the number 6\n\nprice = 100\nprint(price + \" USD\")\n# TypeError: unsupported operand type(s) for +: 'int' and 'str'\n```\n\n## String repetition\nString can be repeated multiple times using `*` operator.\n\n```python\nprint(\"Welcome! \" * 3) # 'Welcome! Welcome! Welcome! '\n\nprint(4 * \"ha\") # 'hahahaha'\n\n```\n\n## String length\nThe function `len()` returns length of its argument string.\n\n```python\npassword = \"xyz1234\"\nprint(\"Password length:\", len(password))\n# Password length: 7\n\nprint(len(1234))\n# TypeError: object of type 'int' has no len()\n\n```\n\n## Order of Expression Evaluation\n\nWhen we have multiple operators in the same expression, which operator should apply first? \n\nAll Python operators have a :sc[precedence] and :sc[associativity]:\n- Precedence — for two different kinds of operators, which should be applied first?\n- Associativity — how operators of the same precedence are grouped in the absence of parentheses? \n - Operations are grouped from the left or from the right?\n\n##\nTable below show operators from higher precedence to lower.\n\n:::div{.my2 .hgrid}\n| Operator | Associativity |\n|---------------------|---------------|\n| `()` (parentheses) | - |\n| `**` | Right |\n| Unary `-` | - |\n| `*`, `/`, `//`, `%` | Left |\n| Binary `+`, `-` | Left |\n| `=` (assignment) | Right |\n:::\n\n\n##\n```python\nx = 3\ny = 5\n# Multiplication has higher precedence than addition\nz = x + 2 * y + 1 \nprint(z) # 14\n\n# Need to use parentheses to enforce the order we want\nz = (x + 2) * (y + 1)\nprint(z) # 30\n\n# Same precedence so left to right\nz = x * y / 100 \nprint(z) # 0.15\n```\n\n## \n```python\n# Same as 2 ** (3 ** 2) because \"**\" goes right to left\nz = 2 ** 3 ** 2\nprint(z) # 512\n\n# Using parentheses to enforce the order we want\nz = (2 ** 3) ** 2 \nprint(z) # 64\n\nx = 5\nx = x + 1 # addition happens first and then assignment\nprint(x) # 6\n\n```\n\n## More on Variables\n\nLet us write code that implements the following formula to convert fahrenheit to celsius:\n\n$$c = \\frac{5(f-32)}{9}$$\n\n\n```python\nprint(\"10 F in C is\", 5 * (10 - 32) / 9)\n```\n\n## :span[Variables allow \"saving\" intermediate results of a computation]{style=\"font-size: 0.83em;\"}\n\nWe can use variable to store the result so that we can reuse it in the program later. \n\n```python\nfahrenheit = 10\n\n# Store the result of the expression\ncelsius = 5 * (fahrenheit - 32) / 9\n\nprint(fahrenheit, \"F in C is\", celsius)\n\n# Use variable celsius for more calculations\nprint(\"Adding 10 degrees today:\", celsius + 10)\n```\n\n## :span[Variables can be reassigned new values]{style=\"font-size: 0.85em;\"}\n```python\n# Create variable name \"number\" and assign a value to it\nnumber = 123 \nprint(number) # displays 123\n\n# Assign new value to existing variable \"number\"\nnumber = -50\n\nprint(number) # displays -50\n\n# add 10 and assign the result value to existing variable \"number\"\nnumber = number + 10\n\nprint(number) # displays -40\n```\n\n##\n\nNew values can be of different type. \n\n\u003cdiv style=\"margin-top: 2em; margin-bottom: 2em; width: 800;\"\u003e\n\u003ciframe width=\"800\" height=\"500\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=number%20%3D%20123%20%20%23%20an%20int%20value%0Amessage%20%3D%20%22hello%22%20%20%23%20a%20string%0A%0Aprint%28number,%20type%28number%29%29%0Aprint%28message,%20type%28message%29%29%0A%0A%23%20Now%20variable%20number%20refers%20to%20the%20string%20%22hello%22%0Anumber%20%3D%20message%0A%0Aprint%28number,%20type%28number%29%29%0Aprint%28message,%20type%28message%29%29%0A\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n\u003c/div\u003e\n\n## \nHowever, variables should be changed with caution as it can produce errors or strange results.\n```python\nnumber = 123 # an int value\nmessage = \"hello\" # a string\n\n# Now variable number refers to the string \"hello\"\nnumber = message\nprint(number * 2) # String repetition!\nprint(number - 10) # minus won't work with string.\n```\n\n```output\nhellohello\nTraceback (most recent call last):\n print(number - 10)\nTypeError: unsupported operand type(s) for -: 'str' and 'int'\n```\n\n## Example: Swapping values\nSometimes we need to swap (interchange) values of two variables.\n\nA naive attempt (does not work):\n\n```python\nx = 137\ny = 42\n\n# Try swapping\nx = y\ny = x\n\nprint(x, y) # 42 42\n```\n\n##\n\nThe following will work:\n\n:::solution\n```python\nx = 137\ny = 42\n\n# Correct way to swap\ntemp = x\nx = y\ny = temp\n\nprint(x, y) # 42 137\n```\n:::\n\n\n\n##\n\nTry the problem \"Textbox\" on Ed.\n\n\n## Rules for variable names\n\n- A variable name can only contain alpha-numeric characters and underscores `A-Z, a-z, 0-9, _`\n- A variable name cannot start with a number\n- Variable names are case-sensitive\n - (`cat`, `Cat`, and `CAT` are three different variables)\n- They cannot be keywords.\n - Python has 33 reserved keywords, you can see a list of them by typing `help(\"keywords\")` in the Python shell.\n\n:::div{.px2 .my2 style=\"border: solid 1px lightgray; border-radius:5px; background-color: #d6dcef;\"}\nPython filenames must follow the same rules as above.\n:::\n\n## Good practice for naming variables\n\n- Name your variable something descriptive of its purpose or content.\n- If the variable is one word, all letters should be lowercase. Eg: `hour`, `day`.\n- If the variable contains more than one word, then they should all be lowercase and each separated by an underscore. This is called :i[snake case]. \n e.g. `is_sunny`, `cat_name`\n- Good variable names: `hour`, `is_open`, `number_of_books`, `course_code`\n- Bad variable names: `asfdstow`, `nounderscoreever`, `ur_stupid`, `CaPiTAlsANyWHErE`\n\n\n::divider\n","title":"1.2 — Variables, Arithmetic \u0026 String operations","date":"2024-05-02","published":true},{"slug":"Lecture-2.1.md","content":"\n\n## Function calls\nFunction take zero or more input values, perform an action or computation, and return the result value. \n\nInput values passed to a function are called :sc[arguments]. \n\nA :sc[Function Call] is an expression that looks like below:\n\n\n:::div{.center .code .my3}\n:span[function_name(argument1, argument2, ..., argumentN)]{.bgblue .p1 .br5}\n:::\n \nHow do we say it? — function \"takes\" argument(s) and \"returns\" a result. The result is also called the :sc[return value].\n\n## \nThe number of arguments required by a function depends on how that function is defined. \n\nFollowing are some built-in functions available in Python:\n```python\n# min() function takes 2 or more numbers and returns the minimum \nx = min(1, -4, 6)\nprint(x) # -4\n\n# abs() function takes a number and returns absolute value of the number\ny = abs(-6)\nprint(y) # 6\n\n# Gives an error if we do not give exactly one number\nz = abs(-1, 4)\n# TypeError: abs() takes exactly one argument (2 given)\n```\n\n## Expressions vs Statements\n\nAn :sc[Expression] is any valid combination of values, variables, operators, function calls. \n\nWhen executed, it always evaluates to a single object.\n\n```python\nx = 3\ny = 4\nz = x ** 2 + y ** 2 # this expression evaluates to an int object\nprint(z) # 25\n\ns = \"hello\"\ns2 = s * len(s) # this expression evaluates to str value\nprint(s2) # hellohellohellohellohello\n\n```\n\n## \n\nA statement is one or more lines of code that performs a task but does not evaluate to any value. \n\nSo, statements cannot be used as a part of an expression.\n\n```python\n\u003e\u003e\u003e x = 123 # Does not evaluate to anything so nothing shows below\n\u003e\u003e\u003e x # This is an trivially an expression\n123\n\u003e\u003e\u003e 10 + (x = 123) # Trying to use assignment statement in an expression\n 10 + (x = 123)\n ^\nSyntaxError: invalid syntax\n```\n\n## Function composition\n\nFunction composition is calling a function with the result(s) of another function(s).\n\nIt is a very useful thing to do especially when we do not need to store intermediate results.\n\n::::div{.hgrid}\n:::div\n::div[Using intermediate variables]{.b}\n```python\nx = -5\ny = -8\na = abs(x)\nb = abs(y)\nz = min(a, b)\nprint(x, y, z)\n```\n:::\n\n:::div\n::div[Using composition]{.b}\n```python\nx = -5\ny = -8\nz = min(abs(x), abs(y))\nprint(x, y, z)\n```\n:::\n::::\n\n## \n\nCheck :i[Built-in Functions] and problems on Ed Lessons.\n\n\n## `print()` displays a space between arguments\n\n```python place=\"start\"\nnum = 1.5e3\ncity = \"New York City\"\nyear = 2023\nprint(num, city, year)\n```\n\nFirst, the arguments of `print()` are evaluated as:\n\n```python place=\"start\"\nprint(1500.0, \"New York City\", 2023)\n```\n\nThen, `print()` function is executed, which would display:\n\n:::div{.code style=\"background-color: white; border-radius:5px; padding: 0.25rem 1rem;\"}\n1500.0:span[\u0026nbsp;]{style=\"background-color: red;\"}New York City:span[\u0026nbsp;]{style=\"background-color: red;\"}2023\n:::\n\n::div[(Highlighted in red are spaces added by `print()`)]{.ppt-f90 style=\"margin-top: 0.5em;\"}\n\n\n##\n\n```python place=\"start\"\nx1 = 1.5\ny1 = 2\n\nprint(\"Point:\", \"(\", x1, \",\", y1, \")\")\n```\n\nFirst, the arguments of `print()` are evaluated as:\n\n```python place=\"start\"\nprint(\"Point:\", \"(\", 1.5, \",\", 2, \")\")\n```\n\nThen, `print()` function is executed, which would display:\n\n:::div{.code style=\"background-color: white; border-radius:5px; padding: 0.25rem 1rem;\"}\n:span[Point:]:span[\u0026nbsp;]{style=\"background-color: red;\"}(:span[\u0026nbsp;]{style=\"background-color: red;\"}1.5:span[\u0026nbsp;]{style=\"background-color: red;\"},:span[\u0026nbsp;]{style=\"background-color: red;\"}2:span[\u0026nbsp;]{style=\"background-color: red;\"})\n:::\n\n::div[(Highlighted in red are spaces added by `print()`)]{.ppt-f90 style=\"margin-top: 0.5em;\"}\n\n## \n\n```python place=\"start\"\nx1 = 1.5\ny1 = 2\n\npoint1 = \"(\" + str(x1) + \", \" + str(y1) + \")\" \nprint(\"Point:\", point1)\n```\n\nFirst, the arguments of `print()` are evaluated as:\n\n```python place=\"start\"\nprint(\"Point:\", \"(1.5, 2)\")\n```\n\nThen, `print()` function is executed, which would display:\n\n:::div{.code style=\"background-color: white; border-radius:5px; padding: 0.25rem 1rem;\"}\n:span[Point:]:span[\u0026nbsp;]{style=\"background-color: red;\"}(1.5, 2)\n:::\n\n::div[(Highlighted in red are spaces added by `print()`)]{.ppt-f90 style=\"margin-top: 0.5em;\"}\n\n\n## :span[Debugging in Thonny to understand expression evaluation]{.ppt-f80}\n\n:::div{.ppt-f87}\nIn Thonny, we can use debugging features to understand how expressions are evaluated:\n\n- To show variables and their values, go to menu \"View -\u003e Variables\"\n- First, run program in :i[debug mode] by clicking the \"Debug current script\" button (located next to the \"Run current script\" button and looks like a bug)\n- Then, we have two options:\n - Run the program line-by-line using \"Step over\" button next to the \"Debug\" button\n - Run program going inside each expression using \"Step into\" button (located next to \"Step over\" button)\n:::\n\n## \n\nTry the following examples in Thonny and use debug:\n\n```python\nx = 7\n\n# Increment value of variable x by 1\nx = x + 1\n\n#\ny = x * x + 2 * (x + 1) + max(x + 1, 5)\n\n# Calling print() with 4 arguments\nprint(\"x =\", x, \"y =\", y)\n```\n\n## \n\n```python\nx1 = 1.5\ny1 = 2\n\nprint(\"Given points:\", \"(\", x1, \",\", y1, \")\")\n\npoint1 = \"(\" + str(x1) + \", \" + str(y1) + \")\" \nprint(\"Given points:\", point1)\n```\n\n\n::divider\n","title":"2.1 — Function calls, Built-in functions, Expressions","date":"2024-05-05","published":true},{"slug":"Lecture-2.2.md","content":"\n## Types of Errors\n\n:b[Syntax Errors]: When syntax is incorrect such as wrong punctuations, invalid characters, missing quotes or parentheses etc. \nProgram does not run at all in the case of syntax errors.\n```python\n# The following code has Syntax error due to missing double-quotes:\nx = 5\nprint(\"Square of x is)\nprint(x ** 2)\n```\n\n##\n\n:::div{.ppt-f94}\n:b[Runtime Errors], also called :sc[Exceptions], occur when there is a problem in the program during execution. \nAll code executes until an exception occurs.\n```python\n# The following code produces NameError because \n# variable y was not created before it is used.\nx = 5\nprint(\"Value of x is\", x)\nprint(\"Square of x is\", y ** 2)\n```\n\n:b[Semantic] or Logic errors are said to occur when a program executes without a problem but does not produce correct output as expected.\n\n:sc[Debugging] is the process of finding and removing errors in a program.\n:::\n\n\n## Defining a function\n\nA function is a :i[named] block of code that performs a task.\n\nSo far we have been using (calling) functions to do specific tasks — `print()`, `input()`, etc.\n\nWe can also define/create our own function.\n\n\n## Defining a function that takes no arguments\n\n:span[Such functions always do the same thing each time they are executed.]{.ppt-f94}\n\n::::div{.hgrid}\n:::div\n```python\n# Function definition\ndef display_greeting():\n print(\"+------------+\")\n print(\"| Welcome! |\")\n print(\"+------------+\")\n\n# Function call\ndisplay_greeting()\n\n# Call it again\ndisplay_greeting()\n```\n:::\n\n:::div\n```output\n+------------+\n| Welcome! |\n+------------+\n+------------+\n| Welcome! |\n+------------+\n```\n:::\n::::\n\n\n## :span[Functions with arguments and return value]{.ppt-f90}\nA function can return a value using `return` statement.\n\n::::div{.hgrid}\n:::div\n```python\ndef f(x): # one parameter x\n result = x * x - x - 1\n return result\n # OR: return x * x - x - 1\n\n# Call the function f\ny = f(5)\nprint(y) # 19\n\n# Call again\ny = f(10)\nprint(y) # 89\n```\n:::\n:::div\n```python\n# two parameters x and y\ndef mean(x, y):\n return (x + y) / 2\n\n\nprint(mean(3, 4)) # 3.5\n```\n:::\n::::\n\n##\n\n:::div{.bgred .px2 .py025 .br5}\nParentheses `()` are required to call a function. Omitting them is a common mistake.\n\nWhen a function is called, correct number of arguments must be passed. It is an error to pass too many or too few arguments than what a function definition expects.\n:::\n\n## Creating a function — general form/syntax\n\n```python\ndef function_name(param1, param2, ..., paramN): # function header\n # function body\n statement1\n statement2\n .\n .\n statementN\n```\n\n- `def` is a Python keyword used to define functions\n- Notice how statements are indented by spaces, typically 4 spaces. In Thonny, we can just use tab key once to indent by 4 spaces.\n\n##\n\n- When we define a function using `def` keyword:\n - it is not executed. \n - Only the function name is created, which refers to the code block inside the function.\n- When we call a function, the code block inside the function is :i[actually] executed.\n\n\n## Why create our own functions?\n\n- Functions allow code re-use; duplication of code can be avoided.\n- They help organize code into sections, which makes programs easier to read and understand.\n- They make programs easier to fix.\n\n\n## Docstrings\n\nA :sc[docstring] (documentation string) is a multiline (triple-quoted) string that we write after the header of a function to explain how the function works.\n\nIt is an important part of programming to write such documentation. \nYou will be expected do so in your assignments.\n\n##\n\n```python\ndef euclidean_distance(x1, y1, x2, y2):\n \"\"\"\n Computes Euclidean distance between two 2D points.\n\n Parameters:\n x1 (float): x-coordinate of first point \n y1 (float): y-coordinate of first point\n x2 (float): x-coordinate of second point\n y2 (float): y-coordinate of second point\n\n Returns: the euclidean distance as a float\n \"\"\"\n d = (x1 - x2) ** 2 + (y1 - y2) ** 2\n return d ** 0.5\n```\n\n\n## Boolean Values\n\nPython has two values `True` and `False` of type `bool`, which are useful for expressing and storing yes/no or true/false kind of data.\n\n```python\n\u003e\u003e\u003e True\nTrue\n\u003e\u003e\u003e False\nFalse\n\u003e\u003e\u003e type(True)\n\u003cclass 'bool'\u003e\n\u003e\u003e\u003e type(False)\n\u003cclass 'bool'\u003e\n```\n\n## Comparison Operators\n\n:sc[comparison operators], also known as :i[relational operators], are used to compare two values, such as numbers or string. \nThe result of such comparison is always a `bool` value i.e. `True` or `False`.\n\n:::div{.hgrid}\n```python\n# are these numbers equal?\n\u003e\u003e\u003e 10 == 10 \nTrue\n\u003e\u003e\u003e 10 == 20\nFalse\n```\n```python\n\u003e\u003e\u003e x = 5\n\u003e\u003e\u003e y = 10\n\u003e\u003e\u003e x == y\nFalse\n\u003e\u003e\u003e x \u003c y\nTrue\n\u003e\u003e\u003e x \u003e y\nFalse\n```\n:::\n\n##\n\n```python\n# A variable can store the result of a boolean expression \n# (just like we did for arithmetic expressions)\n\u003e\u003e\u003e x = 3\n\u003e\u003e\u003e is_positive = (x \u003e 0)\n\u003e\u003e\u003e is_positive\nTrue\n\n\u003e\u003e\u003e x = 5\n\u003e\u003e\u003e y = 5\n\u003e\u003e\u003e is_equal = (x == y)\n\u003e\u003e\u003e is_equal\nTrue\n```\n\n## Boolean Expressions\nA :sc[boolean expression] is an expression that evaluates to either `True` or `False`.\nExamples above show how boolean expressions are created using comparison operators.\n\n:::div{.p1 .br5 .bgred}\n- Common error is using `=` (single equals sign) instead of `==` (double equals sign)\n- `=` is the assignment operator, used to create variable and assign it a value\n- `==` is a comparison operator used to check for equality between two values\n:::\n\n\n## List of comparison operators\n- `x == y` — `True` if `x` is equal to `y`, otherwise `False`\n- `x != y` — `True` if `x` is not equal to `y`, otherwise `False`\n- `x \u003c y` — `True` if `x` is less than `y`, otherwise `False`\n- `x \u003e y` — `True` if `x` is greater than `y`, otherwise `False`\n- `x \u003c= y` — `True` if `x` is less than or equal to `y`, otherwise `False`\n- `x \u003e= y` — `True` if `x` is greater than or equal to `y`, otherwise `False`\n \n\n## Order of operations\n\nAll comparison operators (e.g. `==`, `!=`, etc.) have same priority and are evaluated from left to right.\n\nAll arithmetic and string operators have higher priority than comparison operators.\n\n```python\n\u003e\u003e\u003e x = 5\n# + operator will be evaluated before ==\n\u003e\u003e\u003e x + 1 == 6\nTrue\n```\n\n\n## Comparing strings for equality\n`==` and `!=` operators work for strings as well. \nTwo strings are equal when they have same length and contain exactly same characters (case-sensitive).\n\n\n:::div{.hgrid}\n```python\n\u003e\u003e\u003e \"cat\" == \"cat\"\nTrue\n\u003e\u003e\u003e \"cat\" == \"dog\"\nFalse\n\u003e\u003e\u003e \"cat\" != \"Cat\"\nTrue\n```\n\n\n```python\n# Works the same when using variables\n\u003e\u003e\u003e s1 = \"cat\"\n\u003e\u003e\u003e s2 = \"dog\"\n\u003e\u003e\u003e s1 == s2\nFalse\n```\n:::\n\n\n## Logical Operators\nLogical operators are useful to combine multiple conditions.\n\nLogical operators take boolean expressions as operands and produce a result of type `bool` when evaluated.\n\nPython has 3 boolean operators:\n- `not` — a unary operator\n- `and` — binary operator\n- `or` — binary operator\n\n##\n\nSuppose `x` is a variable of type `bool`:\n:::div{.code .p1 style=\"font-size: 0.75em;\"}\n| x | not x |\n|-------|---------|\n| False | True |\n| True | False |\n:::\n\n`not x` evaluates to the opposite value of `x`.\n\n## \n\nSuppose `x` and `y` are variables of type `bool`:\n::::div{.hgrid}\n:::div{.code .ppt-py2 .ppt-f80}\n| x | y | x and y |\n|-------|-------|---------|\n| True | True | True |\n| True | False | False |\n| False | True | False |\n| False | False | False |\n:::\n:::div{.code .ppt-py2 .ppt-f80}\n| x | y | x or y |\n|-------|-------|---------|\n| True | True | True |\n| True | False | True |\n| False | True | True |\n| False | False | False |\n:::\n::::\n\n`x and y` evaluates to `True` if and only if both `x` and `y` are `True`.\n\n`x or y` evaluates to `False` if and only if both `x` and `y` are `False`.\n\n## Order of operations\n\nIn order of higher to lower priority: `not`, `and`, `or`\n\nAs usual, we can use parentheses in order to change the priority.\n\n:::::div{.hgrid}\n::::div{style=\"border-right: solid 1px black;\"}\nWhat does `b and not a or b` evaluate to if `a = False` and `b = True` ?\n:::div{.code}\n- b and not a or b\n- True and not False or True\n- True and True or True\n- True or True\n- True\n:::\n::::\n::::div\nWhat does `a and not (a or b)` evaluate to if `a = True` and `b = False` ? \n:::div{.code}\n- a and not (a or b)\n- True and not (True or False)\n- True and not True\n- True and False\n- False\n:::\n::::\n:::::\n\n## Updated operator precedence table\n\n::::div{.hgrid}\n:::div{.ppt-f80}\n| Operator | Associativity |\n|----------------------------------|---------------|\n| `()` (parentheses) | - |\n| `**` | Right |\n| Unary `-` | - |\n| `*`, `/`, `//`, `%` | Left |\n| Binary `+`, `-` | Left |\n| `==`, `!=`, `\u003c`, `\u003e`, `\u003c=`, `\u003e=` | Left |\n| `not` | - |\n| `and` | Left |\n| `or` | Left |\n| `=` (assignment) | Right |\n:::\n\nYou don't need to memorize all this, use parenthesis when in doubt!\n\n::::\n\n\n\n## Try these examples in Thonny\n\nChange the value of x and see results of boolean expressions.\n\n```python\nx = 30\n# Is an even number greater than 20?\nprint(x % 2 == 0 and x \u003e 20)\n\nx = 10\n# Is an even number or a multiple of 5 greater than 20?\nprint(x % 2 == 0 or x % 5 == 0 and x \u003e 20)\n\n# Is a multiple of 2 or 5, greater than 20? \nprint((x % 2 == 0 or x % 5 == 0) and x \u003e 20)\n```\n\n##\n\n:::div{.px1 .py025 .bggreen}\n:b[Try it!]\n\nWrite a program that takes 3 integers $x, y, z$ as inputs and prints out `True` if $y$ is an even number between $x$ and $z$, `False` otherwise. Assume all 3 numbers will be different.\n\n```python\n# Retrieve inputs from the user\nx = int(input(\"Enter the x: \"))\ny = int(input(\"Enter the y: \"))\nz = int(input(\"Enter the z: \"))\n\n# Write code below\n```\n:::\n\n##\n\n:::solution\n```python\n# Retrieve inputs from the user\nx = int(input(\"Enter the x: \"))\ny = int(input(\"Enter the y: \"))\nz = int(input(\"Enter the z: \"))\n\n# check if y is even\nis_even = y % 2 == 0\n\n# check if y is between x and z\nis_between = (x \u003c y and y \u003c z) or (z \u003c y and y \u003c x)\n\nprint(is_even and is_between)\n```\n:::\n\n\n## Flow of execution\n\n- Flow of execution refers to order in which statements (lines of code) in our program are executed.\n- So far in our programs, each line was executed unconditionally.\n- For most programs, it is not enough as we need to make choices or run code repeatedly.\n\nWe need to control the flow of execution in our programs.\n\n## Control flow statements\n\nThe control flow of a program determines:\n- Which parts of the code should always be executed\n- Which parts should be executed only under certain conditions\n- Which parts should be executed repeatedly\n\nAll of these can be achieved using control flow statements:\n- `if` statement for conditional execution\n- `for` and `while` loops for repeated execution\n\n\n## :code[if] statement — to execute or not to execute\n\n:::::hgrid{cols=\"1fr 4fr\" margin=\"0 0\" gap=\"2em\"}\n::::hgrid\n:::div{.px1 .py025 .br5 style=\"background-color: white; white-space: pre;\"}\n:code[if]{.bgred} :code[condition]{.bggreen} :code[:]{.bgyellow}\n` `{code}:code[code block]{.bgblue}\n:::\n::::\n:::div\n- :code[condition]{.bggreen} must be a boolean expression\n- :code[code block]{.bgblue} is one of more Python statements\n- :code[code block]{.bgblue} is executed only if the condition is True, otherwise it is skipped.\n:::\n:::::\n:::div{style=\"margin-top: -1em;\"}\nNotice space before code block. It is called :sc[indentation]. \n\n:div[Indentation is required to tell Python that the code belongs inside `if` statement.]{.bgred .px1 .py025}\n\nTypically, 4 spaces are used for indentation. We can use :i[tab] key to indent.\n:::\n\n##\n\nTry the following examples with different values for variables.\n\n```python place=\"start\"\nx = 10 \nif x \u003e 0:\n print(x, \"is positive\")\n```\n\n```python place=\"start\"\nnum = -5.2\n\nabsolute_num = num\n\nif num \u003c 0:\n absolute_num = -num\n \nprint(\"Absolute value of\", num, \"is\", absolute_num)\n```\n\n##\n\n```python\nx = 1000\ny = 123\n\nmin_value = x\n\nif y \u003c min_value:\n min_value = y\n \nprint(\"Minimum of\", x, \"and\", y, \"is\", min_value)\n```\n\n\n## `if` statement with `else` part\n\n`if` statements can have `else` part to make a choice between two code blocks.\n\n:::::hgrid{cols=\"1fr 4fr\" margin=\"0 0\" gap=\"2em\"}\n::::hgrid\n:::div{.px1 .py025 .br5 style=\"background-color: white; white-space: pre;\"}\n:code[if]{.bgred} :code[condition]{.bggreen} :code[:]{.bgyellow}\n` `{code}:code[code block:sub[1]]{.bggreen}\n:code[else]{.bgred} :code[:]{.bgyellow}\n` `{code}:code[code block:sub[2]]{.bgblue}\n:::\n::::\n\n:::div\n- When :code[condition]{.bggreen} is `True`, :code[code block:sub[1]]{.bggreen} is executed\n- Otherwise (:code[condition]{.bggreen} is `False`) and :code[code block:sub[2]]{.bgblue} is executed \n- The code blocks are also called :sc[branches] of the if-statement.\n:::\n:::::\n##\n\n```python\nx = 10 # change this to -5 and run\n\nif x \u003e 0:\n print(\"x is positive.\")\nelse:\n print(\"x is not positive.\")\n```\n\n## Variables and `if` statement\n\nVariables can be created inside the branches of `if` statement. \n\nMake sure that all branches have same variable names!\n\n```python\nincome = 15000\n\nif income \u003c 12000:\n tax = 0.0\nelse:\n taxes = income * 15.5 / 100 # Change variable name to tax\n \nprint(\"Your tax is\", tax)\n```\n\n```output\nNameError: name 'tax' is not defined\n```\n\n\n## :span[Mutually exclusive conditions — chained `if-elif-else` statement]{style=\"margin: 0 -2em;\"} \n\n::::hgrid{margin=\"0 -2em\"}\n```python place=\"center\"\nincome = 20000\n\nif income \u003c 12000:\n tax = 0.0\nelif income \u003c 30000:\n tax = income * 15.0 / 100\nelif income \u003c 100000:\n tax = income * 20.0 / 100\nelse:\n tax = income * 25.0 / 100\n \nprint(\"Your tax is\", tax)\n```\n:::div\n- Mutually exclusive — only one of these blocks will get executed. \n- Order matters! If first of the conditions is `True`, later conditions are not checked.\n- We can have as many `elif`'s as you want.\n- The final `else` part is not required so you may omit it if not needed.\n:::\n::::\n\n\n## Example\n\nIs there anything wrong in code below?\n\n:::hgrid\n```python\ntemperature = 25\n\nif temperature \u003e 0:\n print(\"Cold\")\nelif temperature \u003e 20:\n print(\"Warm\")\nelif temperature \u003e 30:\n print(\"Hot\")\nelse: \n print(\"Freezing\")\n```\n```output\nCold\n```\n:::\n\n## Order of conditions matters!\n\n::::solution\n:::hgrid\n```python\ntemperature = 25\n\nif temperature \u003e 30:\n print(\"Hot\")\nelif temperature \u003e 20:\n print(\"Warm\")\nelif temperature \u003e 0:\n print(\"Cold\")\nelse: \n print(\"Freezing\")\n```\n```python\ntemperature = 25\n\nif temperature \u003e 0 and temperature \u003c= 20:\n print(\"Cold\")\nelif temperature \u003e 20 and temperature \u003c= 30:\n print(\"Warm\")\nelif temperature \u003e 30:\n print(\"Hot\")\nelse: \n print(\"Freezing\")\n\n```\n:::\n::::\n\n##\n\nTry \"Blood Pressure\" problem on Ed Lessons.\n\n## `if` statements can be nested\n\nExamples below are logically equivalent.\n\n::::hgrid{gap=\"3em\" margin=\"0 0\"}\n:::div\n:b[Nested `if` statements]\n```python place=\"start\"\nx = 10\nif x \u003e 0:\n print(\"Positive\")\nelse:\n if x \u003c 0:\n print(\"Negative\")\n else:\n print(\"Zero\")\n```\n:::\n:::div\n:b[Chained `if` statement]\n```python place=\"start\"\nx = 10\nif x \u003e 0:\n print(\"Positive\")\nelif x \u003c 0:\n print(\"Negative\")\nelse:\n print(\"Zero\")\n```\n:::\n::::\n\n::div[You can use either one, but nested statements can easily become difficult to read.]{.ppt-95}\n\n\n## Correct indentation is essential!\n\nSometimes, incorrect indentation may not give an error but it may lead to an unexpected program.\n\n```python\nincome = 1000\n\nif income \u003c 12000:\n print(\"You don't have to pay tax.\")\n tax = 0.0\nelse:\n print(\"You have to pay tax.\")\ntax = income * 15.0 / 100 # this line should be indented\n \nprint(\"Your tax is\", tax)\n```\n\n## Iteration using `for` loop\n\n`for` loop can be used to repeatedly execute a block of code.\n\n::::hgrid{gap=\"4em\"}\n:::div\n```python\nfor i in range(5):\n print(\"Hello\")\n```\n```output\nHello\nHello\nHello\nHello\nHello\n```\n:::\n:::div\n```python\nfor i in range(5):\n print(i)\n```\n\n```output\n0\n1\n2\n3\n4\n```\n:::\n::::\n\n## What happens when the `for` loop is executed?\n\n::::hgrid{cols=\"1fr 3fr\" margin=\"0 0\"}\n:::hgrid\n```python\nfor i in range(5):\n print(i)\n```\n:::\n:::div\n- `range(5)` will produce a sequence of integers $0, 1, 2, 3, 4$ in steps.\n- `for` loop allows us to iterate i.e. \"go over\" that sequence, a number at a time\n - In each step of the loop, variable `i` gets a value from the sequence\n- We can have any valid variable name, other than `i` if we want.\n:::\n::::\n\nTry step-by-step execution of the examples above!\n\n## `range()` function takes up to 3 arguments\n\n`range(end)`: produces sequence `0, 1, 2, ..., end-1`\n\n\n`range(start, end)`: produces sequence `start, start+1, ..., end-1`\n\n\n`range(start, end, step)`:\n- if `step \u003e 0`, produces sequence `start, start+step, ..., N` where `N \u003c end`\n- if `step \u003c 0`, produces sequence `start, start+step, ..., N` where `N \u003e end`\n\n## Examples of range()\n\n:::hgrid\n```python\n# 0, 1, 2, ..., 9\nfor i in range(10):\n print(i)\n\n# 1, 2, ..., 10\nfor i in range(1, 11):\n print(i)\n \n# 0, 2, 4, ..., 18 \nfor i in range(0, 20, 2):\n print(i)\n```\n```python\n# 10, 15, 20, 25, ..., 95\nfor i in range(10, 100, 5):\n print(i)\n\n# 10, 9, 8, ..., 1\nfor i in range(10, 0, -1):\n print(i)\n```\n:::\n\n\n## Exercise\n\nCompute sum of first N numbers.\n\n##\n\n:::solution\n```python\nN = 50\n\ntotal = 0\nfor num in range(1, N+1):\n total = total + num\n \nprint(total)\n```\n:::\n\n##\n\nTry \"Harmonic sum\" problem on Ed Lessons.\n\n\n\n## Indexing \u0026 Slicing Strings\n\nRecall that a string is a sequence of characters.\n\nEach character, therefore, has a position or an :sc[index]. \n\nIndex starts with zero. For example, for the string `\"Hello\"`:\n\n:::::div{style=\"display:grid;margin: 0.5rem 0;\"}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[H]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[o]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n::::\n:::::\n\n\n## \nIndices must be integers and cannot be float.\n\nPython also allows negative indices, which go from right to left:\n\n:::::div{style=\"display:grid;margin: 0.5rem 0;\"}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[H]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[o]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[-5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n:::\n\n::::\n:::::\n\nFor any string `s`, \n- valid positive index values are from `0` to `len(s)-1`.\n- valid negative index values are from `-len(s)` to `-1`.\n\n##\nSquare brackets `[]` are used to get the letter in a string at a given index.\n\n```python\n\u003e\u003e\u003e message = \"Hello\"\n\u003e\u003e\u003e message[0] # first letter\n'H'\n\u003e\u003e\u003e message[1] # second letter\n'e'\n\u003e\u003e\u003e message[4] # fifth letter, the last one in the string\n'o'\n```\n\n##\n\n:::::div{style=\"display:grid;margin: 0.5rem 0;\"}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[H]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[o]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[-5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n:::\n\n::::\n:::::\n\n```python\n\u003e\u003e\u003e message = \"Hello\"\n\u003e\u003e\u003e message[5] # there is no letter at this index\nIndexError: string index out of range\n\u003e\u003e\u003e message[-1]\n'o'\n\u003e\u003e\u003e message[-5]\n'H'\n\u003e\u003e\u003e message[-6] # there is no letter at this index\nIndexError: string index out of range\n\u003e\u003e\u003e message[1.0]\nTypeError: string indices must be integers\n```\n\n## Using slice to get substrings \n\n:::div{.ppt-m-2}\nUsing slice notation we can get parts of a string: `string[start:end:step]`. \n`start`, `end`, `step` values must be integers and work similar to `range()` function.\n:::\n\n:::::div{style=\"margin: 0.5rem 0;\" .grid}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[i]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[n]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[a]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[-9]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n:::\n\n::::\n:::::\n\n```python\n\u003e\u003e\u003e fruit = \"pineapple\"\n\n\u003e\u003e\u003e fruit[4:7] # letters at indices 4, 5, 6\n'app'\n\u003e\u003e\u003e fruit[2:7:2] # letters at indices 2, 4, 6\n'nap'\n```\n\n##\n\n\n:::::div{style=\"margin: 0.5rem 0;\" .grid .ppt-only}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[i]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[n]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[a]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[-9]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n:::\n\n::::\n:::::\n\n\n```python\n\u003e\u003e\u003e fruit[:4] # same as fruit[0:4]\n'pine'\n\u003e\u003e\u003e fruit[4:] # same as fruit[4:len(fruit)]\n'apple'\n\n\u003e\u003e\u003e fruit[-5:] # from index -5 to the end of string\n'apple'\n\u003e\u003e\u003e fruit[-5:-2] # letters at indices -5, -4, -3\n'app'\n```\n\n##\n\n:::::div{style=\"margin: 0.5rem 0;\" .grid .ppt-only}\n::::div{style=\"display:grid;grid-auto-flow: row;grid-auto-rows: min-content;gap:0;width: min-content;justify-self:center;\"}\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[0]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n::div[8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:end center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;border: 1px solid black; width: min-content;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[i]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[n]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[a]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[p]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[l]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n::div[e]{style=\"border: 1px solid black;width:calc(var(--unit)*2);height:calc(var(--unit)*1.7);display:grid;place-content:center;\"}\n:::\n\n:::div{style=\"display:grid;grid-auto-flow: column;grid-auto-columns: min-content;gap:0;font-family: Hack;width: min-content; font-size: 0.7em;\"}\n::div[-9]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-8]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-7]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-6]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-5]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-4]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-3]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-2]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n::div[-1]{style=\"width:calc(var(--unit)*2);height:calc(var(--unit)*1.2);display:grid;place-content:start center;\"}\n:::\n\n::::\n:::::\n\n```python\n# Negative step size of -1 means go from \n# right to left, i.e. in reverse order\n\u003e\u003e\u003e fruit[-4:-8:-1] # letters at -4, -5, -6, -7\n'paen'\n\n# Omitting start and end mean select whole string,\n# but step size -1 means right to left i.e. reverse order \n\u003e\u003e\u003e fruit[::-1]\n'elppaenip'\n```\n\n::divider\n","title":"2.2 — Defining functions, Comparing Values, Control flow statements","date":"2024-05-07","published":true},{"slug":"Lecture-2.3.md","content":"\n\n## Traversing a string\n\nWe can use `for` loop with `range()` function to go over a string letter-by-letter.\n\n:::hgrid\n```python\nmessage = \"Hello\"\n\nfor i in range(len(message)):\n print(i, message[i])\n```\n\n```output\n0 H\n1 e\n2 l\n3 l\n4 o\n```\n:::\n\n## \n\nAnother example:\n\n```python\nletters = \"bcmrst\"\n\nfor i in range(len(letters)):\n print(letters[i] + \"ake\")\n```\n\n##\n\nTry \"Remove spaces from a string\" problem on Ed Lessons.\n\n\n## Equality and floating point numbers\n\nConsider following example:\n\n```python\n\u003e\u003e\u003e x = 1.1 + 2.2\n\u003e\u003e\u003e x == 3.3 # why is this False?\nFalse\n```\n\n- As we saw earlier, a floating-point number is stored with 64-bit :i[finite precision].\n- This means that a number may not be stored as precisely as we would like.\n\n## Correct way to check if two `float` values are equal\n\nWe should check if they are \"close enough\".\n\n```python\n\u003e\u003e\u003e epsilon = 0.000001 # define how close two numbers need to be\n\n\u003e\u003e\u003e x = 1.1 + 2.2\n\u003e\u003e\u003e x\n3.3000000000000003\n\n# Check if x and 3.3 are within epsilon distance\n\u003e\u003e\u003e abs(x - 3.3) \u003c epsilon\nTrue\n```\n\nThe epsilon value depends on the application and how much error we are willing tolerate.\n\n\n\n## \n\n:::div{.ppt-scale-1_25}\n:b[What does this mystery function do?]{.ppt-f80}\n\n\u003ciframe width=\"800\" height=\"420\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=def%20mystery%28s%29%3A%20%20%20%20%20%0A%20%20%20%20result%20%3D%20%22%22%0A%20%20%20%20for%20i%20in%20range%28len%28s%29%29%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20s%5Bi%5D%20%2B%20result%0A%0A%20%20%20%20return%20result%0A%0Anew_string%20%3D%20mystery%28%22hello%22%29%0Aprint%28new_string%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n## Functions and return value `None`\n\nFunctions that do not have an explicit `return` statement, return a special value `None`.\n\n##\n\nThe following 3 functions are equivalent because\n- Python implicitly returns `None` for a function that does not use a return statement\n- if the `return` statement is used without a value, `None` is returned.\n\n```python\ndef greeting():\n print(\"Welcome!\")\n```\n\n:::hgrid\n```python\ndef greeting():\n print(\"Welcome!\")\n return\n```\n```python\ndef greeting():\n print(\"Welcome!\")\n return None\n```\n:::\n\n## \n\n::::hgrid\n:::div\n:b[Function that prints]\n```python\ndef f(x):\n result = x * x - x - 1\n print(result)\n\nf(5) # No print here\n\ny = f(10) + 10 # TypeError\n```\nLess flexible to use; cannot be used with other expressions\n:::\n\n:::div\n:b[Function that returns a value]\n```python\ndef f(x): \n result = x * x - x - 1\n return result\n\nprint(f(5)) # print here\n\ny = f(10) + 10 # works\n```\nMore flexible to use; can be used with other expressions\n:::\n::::\n\n\n##\n\nTry the problem \"Max of three numbers\" on Ed Lessons.\n\n\n## Short Circuit Evaluation\n\nThe evaluation of a boolean expression with `and` and `or` stops as soon as the end result can be inferred.\n\nFor example, in the expression below evaluates to `False` no matter what `not (x \u003e= 1 or y == 3)` evaluates to.\n\n```python lineno=false\n\u003e\u003e\u003e 2 \u003c 1 and not (x \u003e= 1 or y == 3)\nFalse\n```\n\n##\n\nIn general, for any expression with `and` operator:\n\n```python lineno=false\nleft_operand and right_operand\n```\n\nif `left_operand` is `False`, Python does not evaluate `right_operand`.\n\n##\n\nSimilarly, for any expression with `or` operator:\n\n```python lineno=false\nleft_operand or right_operand\n```\n\nif `left_operand` is `True`, Python does not evaluate `right_operand`.\n\nFor example,\n```python lineno=false\n# Evaluates to True no matter what (x \u003c 5) evaluates to\n\u003e\u003e\u003e 1 == 1 or (x \u003c 5)\nTrue\n```\n\n## Why is Short Circuit Evaluation useful?\n\n- It can save time, e.g. when `right_operand` has a computationally expensive function call.\n- It can avoid unnecessary errors as show below.\n\n```python\n\u003e\u003e\u003e x = 0\n\u003e\u003e\u003e 1 / x \u003c 0.5 # cannot divide a number by zero\nZeroDivisionError: division by zero\n\n\u003e\u003e\u003e x != 0 and 1 / x \u003c 0.5\nFalse\n\u003e\u003e\u003e x = 3\n\u003e\u003e\u003e x != 0 and 1 / x \u003c 0.5\nTrue\n```\n\n## \n\n:b[Common mistake when using logical operators]\n\n:::hgrid\n```python lineno=false\nx == \"a\" or \"b\" # Incorrect\n\nx == \"a\" or x == \"b\" # Correct\n```\n\n```python lineno=false\nx == \"a\" and \"b\" # Incorrect\n\nx == \"a\" and x == \"b\" # Correct\n```\n:::\n\n## `while` statement\n\n`while` statement is another way to repeatedly execute a block of code.\n\nGeneral format of a while loop:\n\n:::div{.px1 .py025 .br5 style=\"background-color: white; width: 90%; white-space: pre;\"}\n:code[Initialize variables so that ] :code[condition]{.bggreen .ibox} :code[is True]\n:code[while]{.b} :code[condition]{.bggreen .ibox} :code[:]{.b}\n` `{code}:code[code block]{.bgblue .ibox}\n` `{code}:code[update variables that affect ] :code[condition]{.bggreen .ibox}\n:::\n\n##\n\nWhat `while` loop does:\n1. Evaluate the :code[condition]{.bggreen .ibox}\n2. If :code[condition]{.bggreen .ibox} evaluates to `False`, loop body is not executed.\n3. If :code[condition]{.bggreen .ibox} evaluates to `True`, run the loop body (all indented lines of code) \n a) In loop body we perform some task, :code[code block]{.bgblue .ibox}, and update variables that may change the :code[condition]{.bggreen .ibox} value \n b) Go back to step 1 \n\n##\n\n```python\n# a program to compute sum of first N numbers\nN = 10\n\ntotal = 0\n\ni = 1 # Set value so that condition below is True \nwhile i \u003c= N: # Check if condition is True\n # main task of summing numbers:\n total = total + i \n \n # update i, affects value of condition i \u003c= N \n i = i + 1\n\n# print result outside the loop \nprint(total)\n```\n\n##\n\nIt is a common mistake to forget updating the condition inside loop body. \n\nSee what happens when you remove/comment out the line `i = i + 1` in the previous example. \n\nThe loop will never end — an infinite loop!\n\n\n## Augmented assignment statements\nAugmented assignment is the combination, in a single statement, of a arithmetic operation and an assignment statement:\n\n::::hgrid\n\n```python\nx = 3\ny = 5\n\nx += 1 # same as: x = x + 1\nx += y # x = x + y\nx += x * y # x = x + x * y\n\nx -= 5 # x = x - 5\n\nx *= 2 # x = x * 2\n```\n:::div\n- Similarly, other operators exist: `/=`, `//=`, `%=`, `**=`.\n- These are very useful, especially when updating the condition in while loop.\n:::\n::::\n\n## Loops with indefinite number of steps\n\nSo far we have seen loops that work with fixed number of steps. \n\nBut while loop can be used for repeating code for unknown number of steps.\n\n:::greenbox\nWrite a program to keep asking for password until correct password is entered.\n\nAssume that correct (secret) password is `abcd1234`.\n:::\n\n\n##\n\n```python\npassword = input(\"Enter password: \")\n\nwhile password != \"abcd1234\":\n print(\"Incorrect password, try again!\")\n \n password = input(\"Enter password: \")\n \n# Below line executes only after the above loop ends,\n# i.e. when the correct password was entered.\nprint(\"Login successful!\")\n```\n\n## `for` vs `while` loops\n\n- `for` loops are better when we want to go over a fixed sequence such as a string or a sequence of numbers\n- `while` loop is more flexible as it allows arbitrary conditions and number of steps. e.g. do something until user enters correct data\n\n\n\n::divider\n","title":"2.3 — return vs. print, while statement","date":"2024-05-09","published":true},{"slug":"Lecture-3.1.md","content":"\n## IndexError: slicing vs indexing\n\n```python\nfruit = \"pineapple\"\n\n# slicing beyond valid index works\nprint(fruit[4:20])\n\n# but indexing does not work\nprint(fruit[20])\n```\n```output\napple\nTraceback (most recent call last):\n File \"myprogram.py\", line 7, in \u003cmodule\u003e\n print(fruit[20])\nIndexError: string index out of range\n```\n\n## Importing modules\nA :sc[module] is a Python file (typically a `.py` file) containing function definitions, statements, etc.\n\nMany modules such as `math` and `random` are already installed with Python.\n\n## \n\nUsing `import` statement, we can use functions, variables etc. from a module in our program:\n\n```python\nimport math\n\n# Call a function defined in a module using dot operator\nx = math.sqrt(16)\nprint(x)\n\ny = math.sin(math.pi / 2)\nprint(y)\n```\n\n##\n\nAnother way to import functions, variables from the module:\n```python\nfrom math import sqrt, sin, pi\n\n# Now, we can call sqrt and sin without the \"math.\" prefix\nx = sqrt(16)\nprint(x)\n\ny = sin(pi / 2)\nprint(y)\n```\n\n## \n\nUse `help()` function in Python Shell to see list of all function contained in `math` module:\n```python lineno=false\n\u003e\u003e\u003e import math\n\u003e\u003e\u003e help(math) # will display a long doc, not showing here\n\n\u003e\u003e\u003e help(math.sqrt) # show help on a specific function\nHelp on built-in function sqrt in module math:\n\nsqrt(x, /)\n Return the square root of x.\n```\n\n\n## `random` module\nIn Python, we can generate random numbers using the `random` module.\n\nThe module provides us with a lot of different functions but for the moment we’ll focus on the following:\n- `random()` – It returns a random float value between $0.0$ (inclusive) and $1.0$ (exclusive)\n- `randint(x, y)` – It returns a random integer between x and y, both included.\n\n##\n\nEach time we execute these functions, we will get a different value, try it!\n\n```python\nimport random\n\nprint(random.random()) # 0.12826246225939641\n\nprint(random.randint(1, 10)) # 9\n```\n\n\n##\n\n:::greenbox\nTry the problem \"Guessing Game\" on Ed Lessons.\n:::\n\n\n\n## `in` operator (membership operator)\n\n- In Python `in` is a keyword.\n- The `in` and `not in` operators test for membership.\n- We can use them with strings to test if one string is a substring of another. This operation is case-sensitive.\n\n```pythons\ns = \"Pineapple\"\n\nprint(\"app\" in s) # True\nprint(\"pine\" in s) # False\n\nprint(\"x\" not in s) # True\nprint(\"P\" not in s) # False\n``` \n\n##\n\n:::greenbox\nTry the problem \"Remove Duplicates\" on Ed Lessons.\n:::\n\n##\n\n:::greenbox\nTry the problem \"Benchpress\" on Ed Lessons.\n:::\n\n## String methods\n\nA :sc[method] is similar to a function except that a method is :i[always] called on an object:\n\n:::div{.px1 .py025 .code .br5 style=\"background-color: white; width: auto; white-space: pre;\"}\nobject.method_name(argument1, argument2, ...)\n:::\n\n##\n\n`str` type has several methods that we can call on a string object:\n```python\nprint(\"hello\".upper()) # calling method upper() on the string \"hello\"\n# HELLO\n\nmessage = \"hello\"\nprint(message.upper()) # using variable that refers to string\n# HELLO\n\nmessage = 10\nprint(message.upper()) # upper() only available for str objects\n# AttributeError: 'int' object has no attribute 'upper'\n```\n\n## Useful string methods\n\n```python\ns = \"Luke, I am your father\"\n\n# s.lower() : returns a copy of s, but with all lower case letters.\nprint(s.lower())\n# luke, i am your father\n\n# s.upper() : returns a copy of s, but with all upper case letters.\nprint(s.upper())\n# LUKE, I AM YOUR FATHER\n```\n\n##\n\n```python\ns = \"Luke, I am your father\"\n# s.replace(old, new) : returns a copy of s with all occurrences of \n# the substring old replaced by new.\nprint(s.replace(\"am\", \"am not\"))\n# Luke, I am not your father\n\n# replace space with empty string\nprint(s.replace(\" \", \"\"))\n# Luke,Iamyourfather\n```\n\n## \n\n```python\ns = \"banana\"\n# s.count(c) : returns the number of non-overlapping \n# occurrences of substring c in s.\nprint(s.count(\"na\"))\n# 2\n\n# s.find(c) : returns the index where the substring begins in s begins. \n# If c is not a substring of s, then -1 is returned.\nprint(s.find(\"an\"))\n# 1\n\nprint(s.find(\"naa\"))\n# -1\n```\n\n## :style{.ppt-f90}\n\n```python\nx = 1\ny = 2.5\nz = 3.14\nname = \"Reza\"\n\n# fmt.format(a1, a2, ...):\n# returns a string where the placeholders {} in format string fmt\n# are replaced by args a1, a2, etc.\n\nprint(\"x = {}, y = {}\".format(x, y))\n# x = 1, y = 2.5\n\nprint(\"Point: ({}, {}, {})\".format(x, y, z))\n# Point: (1, 2.5, 3.14)\n\nprint(\"Welcome {}!\".format(name))\n# Welcome Reza!\n```\n\n## Formatted Strings\n\n```python\nx = 1\ny = 2.5\nz = 3.14\nname = \"Reza\"\n\nprint(f\"x = {x}, y = {y}\")\n# x = 1, y = 2.5\n\nprint(f\"Point: ({x}, {y}, {z})\")\n# Point: (1, 2.5, 3.14)\n\nprint(f\"Welcome {name}!\")\n# Welcome Reza!\n```\n\n## Example\n\nIn just one expression, compare if two strings `s1` and `s2` are equal in a case-insensitive manner.\n\n```python\ns1 = \"Hello Everyone\"\ns2 = \"hello everyone\"\n\nis_equal = s1.upper() == s2.upper()\n\n# OR\n# is_equal = s1.lower() == s2.lower()\n\nprint(is_equal) # prints True\n```\n\n## `break` statement\n\n`break` statement can be used to terminate a loop before it normally ends. \nAfter a `break` statement is executed, no other code inside the loop is executed.\n\n:::hgrid\n```python\nfor i in range(10):\n if i \u003e 5:\n break\n print(i)\n \nprint(\"Bye!\")\n```\n```output\n0\n1\n2\n3\n4\n5\nBye!\n```\n:::\n\n\n## \n\n:::greenbox\nWrite a function `is_prime` that takes an integer as argument and returns `True` if the number is prime, otherwise returns `False`.\nTo check if a number `n` is prime:\n- Assume `n` is prime\n- Divide `n` by each number `i` from `2` to `n-1`\n - if `n` is divisible by any `i` then `n` cannot be not prime\n\nIn other words, if `n` is not divisible by all `i`'s then `n` is prime.\n:::\n\n##\n\n:::solution\n```python\ndef is_prime(num):\n prime = True\n\n if num \u003c 2:\n prime = False\n else:\n for i in range(2, num):\n if num % i == 0:\n prime = False\n break\n\n return prime\n\nprint(is_prime(7)) # True\nprint(is_prime(21)) # False\n```\n:::\n\n## \n\n```python\npassword = input(\"Enter password: \")\n\nwhile password != \"abcd1234\":\n print(\"Incorrect password, try again!\")\n password = input(\"Enter password: \")\n\nprint(\"Login successful!\")\n```\n\n:::greenbox\nChange above program so that it keeps asking email and password until both are correct. \nPassword comparison must be case-sensitive, while email comparison should be case-insensitive. \nFor comparison, just choose any email, password that you like.\n:::\n\n##\n\n:::solution\n```python\nemail = input(\"Enter email: \")\npassword = input(\"Enter password: \")\n\nwhile email.lower() != \"abcd@gmail.com\" or password != \"1234\":\n print(\"Incorrect email or password, try again!\")\n \n email = input(\"Enter email: \")\n password = input(\"Enter password: \")\n \n# If we reach this line it means both email and password were correct\nprint(\"Login successful!\")\n\n```\n:::\n\n## Using `break` in a `while` loop\n\nWe can simplify the login example using a `break` statement:\n\n```python\nwhile True:\n email = input(\"Enter email: \")\n password = input(\"Enter password: \")\n if email.lower() == \"abcd@gmail.com\" and password == \"1234\":\n break\n print(\"Incorrect email or password, try again!\")\n\nprint(\"Login successful!\")\n```\n\n## `continue` statement\n\n`continue` statement is useful to skip some steps in a loop.\n\nAfter a `continue` statement is executed, code that follows the statement is skipped and execution continues from the next step of the loop.\n\n```python\nfor i in range(1, 50):\n if i % 2 == 0 or i % 3 == 0:\n continue\n print(i)\n```\n```output\n1 5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49\n```\n\n\n::divider\n","title":"3.1 — Modules, String methods, break \u0026 continue statements","date":"2024-05-12","published":true},{"slug":"Lecture-3.2.md","content":"\n\n## `continue` statement\n\n`continue` statement is useful to skip some steps in a loop.\n\nAfter a `continue` statement is executed, code that follows the statement is skipped and execution continues from the next step of the loop.\n\n```python\nfor i in range(1, 50):\n if i % 2 == 0 or i % 3 == 0:\n continue\n print(i)\n```\n```output\n1 5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49\n```\n\n\n## Assignment 1\n\n- Read the instruction pages\n- Ask questions on Ed Discussion — posts can be made anonymous\n - Make the post private when you want to include code\n- Thinking about testing your solutions:\n - Q1: use the blood type compatibility table to test your code with different values\n - Q2: Try different strings of lengths 1–10\n - Q3: For small `n`, you can use the given formula to verify your solution \n\n\n\n\n## Controlling the flow of execution with `return` statement\n\nAs we have seen, `return` statement allows us to return a value from a function back to the code that calls the function.\n\nBut at the same time return statement also ends execution of the function. \n\n##\n\nWhen return statement is executed, no further code in the function gets executed.\n\n:::hgrid\n```python\ndef display(message):\n print(\"*** \" + message + \" ***\")\n return\n print(\"This will never be displayed\")\n \n \ndisplay(\"hello\")\n```\n\n```output\n*** hello ***\n```\n:::\n\n`return` can be very useful when placed inside an `if` statement if we want to exit from the function under certain conditions.\n\n\n## \n \nUsing return statement, we can simplify the prime number example:\n\n:::hgrid\n```python\n# We saw this before\ndef is_prime(num):\n prime = True\n\n if num \u003c 2:\n prime = False\n else:\n for i in range(2, num):\n if num % i == 0:\n prime = False\n break\n\n return prime\n```\n\n```python\n# Simplified version\ndef is_prime(num):\n if num \u003c 2:\n return False\n\n for i in range(2, num):\n if num % i == 0:\n return False\n\n return True\n```\n:::\n\n##\n\n:::redbox\n:b[Important]: we must make sure that all branches/cases in the function return the correct values. \n\nIn previous example, if we forget the last return statement in the simplified `is_prime` function, `return None` will happen implicitly, which would be incorrect.\n:::\n\n## Be careful — incorrect indentation changes logic (1)\n\n:::hgrid{.ppt-f90 margin=\"0 0\"}\n```python\ndef remove_spaces(s):\n result = \"\" \n for i in range(len(s)):\n letter = s[i]\n if letter != \" \":\n result = result + letter\n\n return result\n```\n```python\ndef remove_spaces(s):\n result = \"\" \n for i in range(len(s)):\n letter = s[i]\n if letter != \" \":\n result = result + letter\n\n return result\n```\n:::\n\n## Be careful — incorrect indentation changes logic (2)\n\n:::hgrid{.ppt-f90 margin=\"0 0\"}\n```python\ndef remove_spaces(s):\n result = \"\" \n for i in range(len(s)):\n letter = s[i]\n if letter != \" \":\n result = result + letter\n\n return result\n```\n```python\ndef remove_spaces(s):\n result = \"\" \n for i in range(len(s)):\n letter = s[i]\n if letter != \" \":\n result = result + letter\n\n return result # what will this do?\n```\n:::\n\n## ASCII code and special characters\n\n::div[Recall ASCII table from a previous lecture:]{style=\"margin: 0 auto;\" .ppt-f80}\n\n::image{style=\"margin: 0 auto; width: 70%;\" src=\"module-1/ASCII-Table.png\"} \n\n##\nPython has built-in functions to convert ASCII code (decimal) to/from a single character.\n\n```python\nprint(ord(\"a\")) # 97, the ASCII code for letter \"a\"\nprint(ord(\"A\")) # 65\nprint(ord(\"$\")) # 36\n\nprint(ord(\"hi\")) # doesn't work for more than one character\n# TypeError: ord() expected a character, but string of length 2 found\n\nprint(chr(70)) # F, the character for ASCII code 70\n\nprint(chr(103)) # g\n```\n\n##\n\n:::greenbox\nWrite a program that shifts each letter in a string to the left by 3 steps according to ASCII table. \ni.e. :code[A → \u003e, B → ?, C → @, D → A, E → B], etc.\n:::\n\n\n```python\nword = \"Python\"\n\nresult = \"\"\nfor i in range(len(word)):\n code = ord(word[i]) # Get ASCII code for the letter\n code = code - 3 # Shift code by 3\n # Get letter for the code and add it to result:\n result = result + chr(code) \n \nprint(result) # Mvqelk\n\n```\n\n## \n\nText is stored in a file as a sequence of character codes.\n\n:::hgrid{margin=\"1em 0\" gap=\"1em\"}\n```python lineno=false \ncat dog\n```\n::seqbox{text=\"99,97,116,32,100,111,103\" margin=\"0\" w=\"2.5\" h=\"2\"}\n:::\n\nMultiple lines in text:\n:::hgrid{margin=\"1em 0\" gap=\"1em\"}\n```python lineno=false\ncat\ndog\n```\n::seqbox{text=\"99,97,116,10,100,111,103\" margin=\"0\" w=\"2.5\" h=\"2\"}\n:::\n\nThe :em[newline character], which represents \"enter\" or \"return\" key, is also stored when text contains multiple lines.\n\n## Escape characters\n\nThere are special characters, which we may not directly include in a string, e.g.:\n- :i[newline character]: This is the character representing \"enter\" or \"return\" key.\n- :i[tab character]: This is the character representing \"tab\" key.\n\nSuch special characters can be used in a string using escape characters.\n\n## \n\n```python\n# Trying to enter a newline character directly fails:\nmessage = \"Hello\nworld\"\n```\n\n```output\n message = \"Hello\n ^\nSyntaxError: EOL while scanning string literal\n```\n\n## \n\nTo include a newline character in a string we can use the escape character `\\n` in the string:\n\n:::hgrid\n```python\nmessage = \"hello\\nworld\"\nprint(message)\n```\n\n```output\nhello\nworld\n```\n:::\n\n`\\n` is stored as a single character even though it looks like two.\n\n```python lineno=false\nprint(ord(\"\\n\")) # 10\n```\n\n##\n\nAnother escape character is `\\t` which represents the tab character. \nIt is useful as a separator when displaying values:\n\n:::hgrid\n```python\n# print uses space as separator by default\nprint(\"Khalid\", 85)\nprint(\"Reza\", 90)\n\n# Using tab as separator\nprint(\"Khalid\", 85, sep=\"\\t\")\nprint(\"Reza\", 90, sep=\"\\t\")\n```\n\n```output\nKhalid 85\nReza 90\nKhalid\t85\nReza\t90\n```\n:::\n\n## Controlling print() function\n\nIn previous example, we used a :sc[keyword argument] `sep=` to tell print which separator to use between values. \n\nUnlike the usual arguments, keyword arguments are given in the form `name=value`; in the example `sep` is the name of argument and `\"\\t\"` is the value.\n\n```python\n# separator can be any string\nprint(\"Alice\", 90, 3.14, sep=\",\")\n# Alice,90,3.14\n```\n\n##\n\n```python\nprint(\"Alice\", 90, 3.14, sep=\"|\")\n# Alice|90|3.14\n\n# even longer than one character\nprint(\"Alice\", 90, 3.14, sep=\"-----\")\n# Alice-----90-----3.14\n\nprint(\"Alice\", 90, 3.14, sep=\"\") # No separator!\n# Alice903.14\n```\n\n##\n\nBy default, `print()` function displays a newline character `\\n` at end of line. \n\n:::hgrid\n```python\nprint(\"Good\", \"morning\")\nprint() # no arguments, just prints \"\\n\"\nprint(123, 3.14)\n```\n\n```output margin=\"-1em 0 0 0\"\nGood morning\n\n123 3.14\n```\n:::\n\n##\n\nWe can change the `end` character using another keyword argument to `print()` function, `end=`. \n\n:::hgrid\n```python\nprint(\"A sequence of numbers:\")\nprint(1, end=\",\")\nprint(4, end=\",\")\nprint(9, end=\",\")\n```\n\n```output\nA sequence of numbers:\n1,4,9,\n```\n:::\n\n##\n\n`end=` is useful in a loop:\n\n```python\nN = 10\nfor i in range(N):\n print(i*i, end=\", \") # comma and a space\n```\n\n```output\n0, 1, 4, 9, 16, 25, 36, 49, 64, 81, \n```\n\n:::greenbox\nChange the above example to not print the last comma. E.g.,\n\n```output\n0, 1, 4, 9, 16, 25, 36, 49, 64, 81\n```\n:::\n\n##\n\n:::solution\n```python\nN = 10\nfor i in range(N):\n if i == N - 1:\n print(i * i)\n else:\n print(i * i, end=\", \")\n```\n:::\n\n## Multiline strings\n\nUsing `\\n`, we can create a single string that contains all of the following lines:\n\n```python\nshopping_list = \"Shopping list\\n- Milk\\n- Eggs\\n- Apples\\n\"\nprint(shopping_list)\n```\n\n```output\nShopping list\n- Milk\n- Eggs\n- Apples\n```\n\n##\n\nPython provides a better create multiline strings using :i[triple quotes]: `'''` or `\"\"\"`.\n\n```python\n\u003e\u003e\u003e shopping_list = \"\"\"Shopping list\n- Milk\n- Eggs\n- Apples\n\"\"\"\n\u003e\u003e\u003e shopping_list\n'Shopping list\\n- Milk\\n- Eggs\\n- Apples\\n'\n```\n\n## Nested Loops\n\nWe can have a for/while loop inside other for/while loops.\n\nThis is useful when we have two sequences and we need all combinations/pairs of items from the sequences.\n\n##\n\n:::greenbox\nWrite a program that prints all pairs of numbers that sum to $7$ when two six-sided dice are rolled.\n:::\n\n:::hgrid\n```python\n# outer loop for first die d1:\nfor d1 in range(1, 7): \n # inner loop for second die d2\n for d2 in range(1, 7):\n if d1 + d2 == 7:\n print(d1, d2)\n```\n\n```output\n1 6\n2 5\n3 4\n4 3\n5 2\n6 1\n```\n:::\n\n\n## \n\n:::greenbox\nWrite a program that takes two string—one with consonants and other with vowels—and combines each consonant with every vowel to print a syllable.\n:::\n\n:::hgrid{margin=\"0\"}\n```python\nconsonants = \"bdfghjklmn\"\nvowels = \"aeiou\"\n```\n```output\nba be bi bo bu \nda de di do du \nfa fe fi fo fu \nga ge gi go gu \nha he hi ho hu \nja je ji jo ju \nka ke ki ko ku \nla le li lo lu \nma me mi mo mu \nna ne ni no nu\n```\n:::\n\n##\n\n:::solution\n```python\nconsonants = \"bdfghjklmn\"\nvowels = \"aeiou\"\n\nfor i in range(len(consonants)):\n for j in range(len(vowels)):\n syllable = consonants[i] + vowels[j]\n print(syllable, end=\" \")\n print() # to start printing from next line\n\n```\n:::\n\n##\n\nTime for some problems on Ed Lessons.\n\n\n::divider\n","title":"3.2 — Controlling print(), Nested Loops","date":"2024-05-13","published":true},{"slug":"Lecture-3.3.md","content":"\n\n## Scope of variables\n\n- A variable name only exists inside the body of the function in which it is created.\n - It does not exist outside the function or in any other functions.\n\n:::hgrid\n```python\ndef f():\n x = 3\n print(\"In f(), x =\", x)\n \nf()\nprint(x)\n```\n```output\nIn f(), x = 3\nNameError: name 'x' is not defined\n```\n:::\n\n##\n\n- The :sc[scope] of a variable consists of parts of the program where the variable name exists and can be used.\n- Each function has its own :sc[local scope], which other functions cannot access.\n- :sc[global scope] consists of names accessible by the entire module (Python file).\n\nA variable created inside a function is called a :sc[local variable]. \n\nA variable created outside any function is called a :sc[global variable].\n\n##\n\n:::hgrid\n```python\ndef f():\n x = 3 # local variable\n # local x is used below!\n print(\"In f(), x =\", x) \n\n\nx = 100 # global variable\nf()\nprint(x) # global x is used\n```\n```output\nIn f(), x = 3\n100\n```\n:::\n\n- As we saw above, it is possible to create a local variable with the same name as a global variable. \n- These are considered two different variables, and inside the function only the local one will be used.\n\n## \n\n:::hgrid\n```python\ndef f():\n # global x is used below!\n print(\"Inside f(), x =\", x)\n\n\nx = 100 # global variable\nf()\nprint(x)\n```\n```output\nInside f(), x = 100\n100\n```\n:::\n\nWhat will happen if the global variable `x` is created after the function call `f()`?\n\n\n## How variable name is looked up?\n- Inside a function, when a name is used:\n - First, name is searched within the function (local scope) to see if it exists.\n - If name is not found in the function, it is searched globally\n- Outside a function, name is simply searched globally\n- If a name cannot be found anywhere (local or global scope), we get `NameError` complaining that the name is not defined.\n\n##\n\nWhat will be printed in each case?\n\n:::hgrid{margin=\"0\" gap=\"5em\"}\n```python\ndef f():\n y = 5\n print(x)\n\nx = 10\nf()\n```\n```python\ndef f():\n x = 5 \n print(x)\n\nx = 10\nf()\nprint(x)\n```\n:::\n\n##\n\n::::solution\n:::hgrid{gap=\"5em\"}\n```output\n10\n```\n```output\n5\n10\n```\n:::\n::::\n\n\n##\n\n:::div{.ppt-scale-1_25}\n:b[Function parameters are also local to the function.]{.ppt-f80}\n\u003ciframe width=\"800\" height=\"380\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=def%20f%28x%29%3A%0A%20%20%20%20print%28%22In%20f%28%29,%20x%20%3D%22,%20x%29%0A%20%20%20%20return%20x%20*%20x%0A%0Ax%20%3D%2010%0Ay%20%3D%20f%28x%29%0Aprint%28f%22Global,%20x%20%3D%20%7Bx%7D,%20y%20%3D%20%7By%7D%22%29\u0026codeDivHeight=400\u0026codeDivWidth=360\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n##\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"840\" height=\"420\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=def%20f%28x%29%3A%0A%20%20%20%20x%20%3D%203%0A%20%20%20%20print%28f%22In%20f%28%29,%20x%20%3D%20%7Bx%7D,%20y%20%3D%20%7By%7D%22%29%0A%20%20%20%20return%20x%20*%20y%0A%0Ax%20%3D%2010%0Ay%20%3D%205%0Az%20%3D%20f%28x%29%0Aprint%28f%22Global,%20x%20%3D%20%7Bx%7D,%20y%20%3D%20%7By%7D,%20z%20%3D%20%7Bz%7D%22%29\u0026codeDivHeight=400\u0026codeDivWidth=440\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n\n## Best Practice\n- Try to avoid using global variables within functions when possible.\n - It is okay to use variables that don't change (e.g. constants such as $\\pi$)\n - It is also okay to use modules inside functions \n\n##\n\n:::redbox\n- Scope is concerned with names only (variables, parameters, etc.) and not objects.\n- An object created inside a function can be returned by the function and that object will be available after the function has finished executing.\n:::\n\n## Lists\n\n- A list is like a container that holds a sequence of objects.\n- Objects contained in a list are called :sc[elements] or :sc[items].\n- Lists are ordered! The order in which the items are stored in the list matters.\n- Each item is associated with an index (index $0$: first item, index $1$: second item, etc.)\n\n## Creating a list\n\nA list is created using square brackets, with each item separated by a comma.\n\n```python\nprime_numbers = [2, 3, 5, 7, 11, 13]\nprint(prime_numbers)\n# [2, 3, 5, 7, 11, 13]\n\nprint(type(prime_numbers))\n# \u003cclass 'list'\u003e\n```\n\n##\nA list can contain items of any type. For example we can have a list of strings:\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\n# Number of items in the list\nprint(len(days)) # 7\n\nempty_list = []\nprint(len(empty_list)) # 0\n```\n\nA list can contain any number of items, from zero to as many as the computer's memory allows.\n\n##\nA list can contains objects of different types.\n\n```python\n# list with mixed types\nnumbers = [1, 'two', 3.75]\n```\n\nItems of a list don't need to be unique.\n\n```python\n# list with duplicate values\nnumbers = [5, \"five\", 5]\n```\n\n## Why use a list?\n\n```python\n# Suppose we want to store grades for multiple students\n\ngrades1 = 80\ngrades2 = 100\ngrades3 = 65\n# ...\n# How many variables?!!\n\n# Use just one variable name \"grades\"\ngrades = [80, 100, 65]\n```\n\n## Indexing a list\n\nWe can access an item inside a list using indexing (square brackets), just as we did for strings.\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\nfirst_day = days[0]\nsecond_day = days[1]\nlast_day = days[6]\nprint(first_day, second_day, last_day) # Mon Tue Sun\n\n# No item at index 7\nprint(days[7]) \n# IndexError: list index out of range\n```\n\n##\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\n# Negative indexing can be used as well\nprint(days[-1], days[-2]) # Sun Sat\n\nnumbers = [1, 'two', 3.75]\nprint(numbers[0] + numbers[2]) # 4.75\n\nprint(numbers[0] + numbers[1])\n# TypeError: unsupported operand type(s) for +: 'int' and 'str'\n```\n\n## Modifying the content of a list\n\nWe can modify the content of a list after it has been created.\n\nWe can change a single item using its index and the assignment operator.\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\ndays[0] = \"Sun\"\nprint(days)\n# ['Sun', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\ndays[7] = \"No such day\"\n# IndexError: list assignment index out of range\n```\n\n\n## \n\n`for` loops are very useful for looping through all the items in a list.\n\n```python\n# compute an average of grades\ngrades = [85, 78.5, 98, 75, 100]\ntotal = grades[0] + grades[1] + grades[2] + grades[3] + grades[4] \navg = total / 5\nprint(avg) # prints 87.3\n```\n:::hgrid{margin=\"0.5rem 0\"}\nFor instance, we could use a for loop to compute the average of grades.\n```python\ngrades = [85, 78.5, 98, 75, 100]\n\ntotal = 0\nfor i in range(5):\n total += grades[i]\n\navg = total / 5\nprint(avg) # 87.3\n```\n:::\n\n##\n\nGeneralized version:\n```python\ngrades = [85, 78.5, 98, 75, 100]\n\ntotal = 0\nN = len(grades) \nfor i in range(N):\n total += grades[i]\n\navg = total / N\nprint(avg) # 87.3\n```\n\n\n## Lists — Concatenation `+` and Replication `*`\n\n```python\n# lists a and b are joined to produce a third list c:\na = [1, 2]\nb = [10, 11, 12]\nc = a + b\nprint(c)\n# [1, 2, 10, 11, 12]\n\na = [1, 2]\n# resulting list consists of repeated items of list a:\nc = a * 3 \nprint(c)\n# [1, 2, 1, 2, 1, 2]\n```\n\n\n## membership operators: `in` and `not in`\n\nWe can use them to test if an object is present in a list.\n\n```python\na = [1, 2]\nb = [10, 11, 12]\n\nprint(1 in a) # True\nprint(11 in a) # False\nprint(5 not in b) # True\n\nx = 3.14\nprint(x in a or x in b) # False\n```\n\n##\n\n`in`/`not in` operators are very useful in simplifying code:\n\n:::hgrid\n```python\n# Instead of long conditions like this:\nif x == 5 or x == 7 or x == 10:\n # do something\n```\n```python\n# Now we can do:\nif x in [5, 7, 10]:\n # do something\n```\n:::\n\n\n## Slicing a list\n\nSimilar to strings, we can also get a sub-list — parts of a list — using slice notation. Slicing creates a new list.\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\nprint(days[2:5])\n# ['Wed', 'Thu', 'Fri']\n\nprint(days[:6:2])\n# ['Mon', 'Wed', 'Fri']\n```\n\n##\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\n# Make a copy of the whole list\nprint(days[:])\n# ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\n# Makes a reversed copy of the list\nprint(days[::-1])\n# ['Sun', 'Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon']\n```\n\n## Iterating through a list\n\nWe can either use an index or loop directly over items in a list:\n\n:::hgrid{margin=\"0\"}\n```python\ndef average(nums):\n total = 0\n\n for i in range(len(nums)):\n total += nums[i]\n\n return total / len(nums)\n\ngrades = [85, 100, 98, 75]\nprint(average(grades))\n```\n```python\ndef average(nums):\n total = 0\n\n for x in nums:\n total += x\n\n return total / len(nums)\n\ngrades = [85, 100, 98, 75]\nprint(average(grades))\n```\n:::\n\n##\n\nBut when we need to modify a list inside a loop we have to use an index:\n\n```python\ndef add_bonus(grades, bonus):\n \"\"\" Add bonus to each grade in grades list\n (grade should not exceed 100) \n Returns: None\n \"\"\"\n for i in range(len(grades)):\n grades[i] = min(grades[i] + bonus, 100)\n \n \nassignment_grades = [55, 60, 67, 97]\nadd_bonus(assignment_grades, 5)\nprint(assignment_grades) # [60, 65, 72, 100]\n```\n\n\n::divider\n","title":"3.3 — Scope of variables, Lists","date":"2024-05-16","published":true},{"slug":"Lecture-4.1.md","content":"\n## Review — Iterating through a list\n\nWe can either use an index or loop directly over items in a list:\n\n:::hgrid{margin=\"0\"}\n```python\ndef average(nums):\n total = 0\n\n for i in range(len(nums)):\n total += nums[i]\n\n return total / len(nums)\n\ngrades = [85, 100, 98, 75]\nprint(average(grades))\n```\n```python\ndef average(nums):\n total = 0\n\n for x in nums:\n total += x\n\n return total / len(nums)\n\ngrades = [85, 100, 98, 75]\nprint(average(grades))\n```\n:::\n\n##\n\nBut when we need to modify a list inside a loop we have to use an index:\n\n```python\ndef add_bonus(grades, bonus):\n \"\"\" Add bonus to each grade in grades list\n (grade should not exceed 100) \n Returns: None\n \"\"\"\n for i in range(len(grades)):\n grades[i] = min(grades[i] + bonus, 100)\n \n \nassignment_grades = [55, 60, 67, 97]\nadd_bonus(assignment_grades, 5)\nprint(assignment_grades) # [60, 65, 72, 100]\n```\n\n\n##\n\nTime for some problems on Ed Lessons.\n\n\n## Built-in functions that work with lists\n\n```python\ngrades = [90, 70, 60.5, 70, 80]\n\n# len(x): \n# Returns the number of items in the list x.\nprint(len(grades)) # 5\n\n# sum(x): \n# Returns the sum of all the numbers in list x.\n# A TypeError occurs when some item is not a number.\nprint(sum(grades)) # 370.5\n```\n\n##\n\n```python\ngrades = [90, 70, 60.5, 70, 80]\n\n# min(x) / max(x) : \n# Returns the smallest/largest item in the list x.\n# A TypeError occurs if the items cannot be compared.\n\nprint(min(grades)) # 60.5\n\nprint(max(grades)) # 90\n\nprint(min([\"90\", 70, 60.5, 70, 80]))\n# TypeError: '\u003c' not supported between instances of 'int' and 'str'\n```\n\n## List methods\nPython has several methods that we can call on a list object:\n\n```python\n# list.append(x): Adds the item x to the end of the list\n\ngrades = [90, 70, 60.5, 70, 80]\ngrades.append(100)\nprint(grades) # [90, 70, 60.5, 70, 80, 100]\n\ngrades.append(\"30\")\nprint(grades) # [90, 70, 60.5, 70, 80, 100, '30']\n\ngrades.append(False)\nprint(grades) # [90, 70, 60.5, 70, 80, 100, '30', False]\n```\n\n## \n\n:b[Example:]\n\n```python\ndef filter_values(nums, threshold):\n ''' Return a new list to include numbers from \n the list nums that are above threshold\n '''\n new_list = []\n \n for n in nums:\n if n \u003e threshold:\n new_list.append(n)\n\n return new_list\n\nprint(filter_values([3, 1, 2, 5, 4], 3)) # [5, 4]\nprint(filter_values([3, 1, 2, 5, 4], 5)) # []\n```\n\n##\n\n```python\n# list.insert(i, x): Adds the item x to the list at index i\n\ngrades = [90, 70, 60.5, 70, 80]\ngrades.insert(0, 100)\nprint(grades) # [100, 90, 70, 60.5, 70, 80]\n\n# insert() works even when index is greater than length of list\ngrades = [90, 70, 60.5, 70, 80]\ngrades.insert(10, \"B+\")\nprint(grades) # [90, 70, 60.5, 70, 80, 'B+']\n```\n\n##\n\n```python\n# list.remove(x): \n# Removes the first occurrence of the item x in the list. \n# A ValueError occurs if there is no such item.\n\ngrades = [90, 70, 60.5, 70, 80, 'B+']\ngrades.remove(70)\nprint(grades) # [90, 60.5, 70, 80, 'B+']\n\ngrades.remove(\"B+\")\nprint(grades) # [90, 60.5, 70, 80]\n\ngrades.remove(60)\n# ValueError: list.remove(x): x not in list\n```\n\n##\n\n```python\n# list.pop(i): \n# Removes and returns item at index i \n# list.pop(): \n# Removes and returns the last item from the list\n\ngrades = [90, 70, 60.5, 70, 80, 'B+']\nlast_item = grades.pop()\nprint(last_item) # B+\nprint(grades) # [90, 70, 60.5, 70, 80]\n\nsecond_item = grades.pop(1)\nprint(second_item) # 70\nprint(grades) # [90, 60.5, 70, 80]\n```\n\n##\n\n```python\n# list.count(x) : \n# Returns the number of occurrences of the item x. \n\ngrades = [90, 70, 60.5, 70, 80, 'B+']\nprint(grades.count(70)) # 2\nprint(grades.count(\"B+\")) # 1\nprint(grades.count(60)) # 0\n```\n\n##\n\n```python\n# list.index(x) : \n# Returns the index of the first occurrence of item x in list. \n# A ValueError occurs if item x is not found in list.\n\ngrades = [90, 70, 60.5, 70, 80, 'B+']\n\nprint(grades.index(70)) # 1\nprint(grades.index(60)) # ValueError: 60 is not in list\n```\n\n##\n\n```python\n# list.extend(sequence) : \n# Extend list by appending items from the sequence.\n\ngrades = [90, 70, 60.5, 70, 80]\n\ngrades.extend([100, 95])\nprint(grades) # [90, 70, 60.5, 70, 80, 100, 95]\n\ngrades.extend(\"cat\")\nprint(grades)\n# [90, 70, 60.5, 70, 80, 100, 95, 'c', 'a', 't']\n```\n\n## Lists and Strings\n\nThere are built-in functions and string methods that allows us to transform strings to/from lists.\n\n```python\n# list(seq): built-in function which converts a sequence (such as\n# a string or a list) into a list\n\nfruit = \"apple\"\nletters = list(fruit)\nprint(letters) # ['a', 'p', 'p', 'l', 'e']\n```\n##\n\n```python\n# s.split(): Breaks the string s using whitespace\n# (spaces, tab character and newline) as the separator and\n# returns a list of strings containing the separated parts\n\ndata = \"Red Green Blue\"\nnames = data.split()\nprint(names) # ['Red', 'Green', 'Blue']\n\n# Multiple spaces are also removed\ndata = \" Red Green Blue \"\nnames = data.split()\nprint(names) # ['Red', 'Green', 'Blue']\n```\n\n##\n\n```python\ndata = \"Red\\tGreen\\tBlue\" # separated by tab\nnames = data.split()\nprint(names) # ['Red', 'Green', 'Blue']\n\ndata = \"\"\"Red\nGreen\nNavy Blue\"\"\"\nnames = data.split()\nprint(names) # ['Red', 'Green', 'Navy', 'Blue']\n```\n\n##\n\n```python\n# s.splitlines(): Breaks a multi-lines strings into separate lines\n# and returns a list containing those lines.\n\ndata = \"\"\"Red\n Green\nNavy Blue\n\"\"\"\nnames = data.splitlines()\nprint(names) # ['Red', ' Green', 'Navy Blue']\n```\n\n##\n\n:::hgrid{margin=\"0 0 0 -1em\"}\n```python\n# s.strip(): Return a copy of the string with \n# leading and trailing whitespace removed.\n# s.lstrip(): removes leading whitespace only \n# s.rstrip(): removes trailing whitespace only \n\nname = \" Green \"\nprint(\"|\" + name + \"|\") \nprint(\"|\" + name.strip() + \"|\")\nprint(\"|\" + name.lstrip() + \"|\")\nprint(\"|\" + name.rstrip() + \"|\")\n```\n\n```output\n| Green |\n|Green|\n|Green |\n| Green|\n```\n:::\n\n##\n\n:::hgrid{margin=\"0 0 0 -2em\" .ppt-f95}\n```python\n# s.strip(chars): Return a copy of the string \n# with leading and trailing chars removed.\n# s.lstrip(chars): removes leading chars only \n# s.rstrip(chars): removes trailing chars only \n\ntext = \"...#some . text #...\"\n\nprint(text.strip(\".\"))\nprint(text.lstrip(\".\"))\nprint(text.rstrip(\".\"))\n\n# multiple chars to remove\nprint(text.strip(\". #\"))\n```\n\n```output\n#some . text #\n#some . text #...\n...#some . text #\nsome . text\n```\n:::\n\n\n##\n\n```python\n# s.split(sep): Breaks the string s using the separator string sep\n# and returns a list of strings containing the separated parts\n\ndata = \"Red,Green,Blue\"\nnames = data.split(\",\")\nprint(names) # ['Red', 'Green', 'Blue']\n\ndata = \"Red, Green, Blue\"\nnames = data.split(\",\")\nprint(names) # ['Red', ' Green', ' Blue'] \n# notice space in strings above\n```\n\n##\n\n```python\n# sep.join(L): joins all the strings in the list L using \n# the string sep and returns the joined string.\n\nnames = ['Red', 'Green', 'Blue']\njoined = \" \".join(names)\nprint(joined) # Red Green Blue\n\n# a comma\njoined = \",\".join(names)\nprint(joined) # Red,Green,Blue\n```\n\n##\n\n```python\n# a comma and a space\nnames = ['Red', 'Green', 'Blue']\njoined = \", \".join(names)\nprint(joined) # Red, Green, Blue\n\n# empty string, no separator\nletters = [\"a\", \"p\", \"p\", \"l\", \"e\"]\njoined = \"\".join(letters)\nprint(joined) # apple\n```\n\n##\n\nTime for some problems on Ed Lessons.\n\n\n## Tuples\n\n- A tuple is an ordered collection of objects, like lists.\n- A tuple is :i[immutable]. A tuple object cannot be modified after it is created.\n- We create a tuple using parentheses `()`.\n\n```python\ntup = (1, 2, 3)\nprint(type(tup)) # \u003cclass 'tuple'\u003e\n\n# tuple with only one item\ntup = (10,) # comma is required!\nprint(tup, type(tup)) # (10,) \u003cclass 'tuple'\u003e\n```\n\n##\n\nWe can use `tuple` function to convert other sequences such as lists and strings into a tuple.\n\n```python\nword = \"apple\"\ntup = tuple(word)\nprint(tup)\n# ('a', 'p', 'p', 'l', 'e')\n\nprimes = [2, 3, 5, 7]\nprimes = tuple(primes)\nprint(primes)\n# (2, 3, 5, 7)\n```\n\n## Tuples are immutable\n\nItems cannot be added, removed or changed in a tuple.\n\nTherefore, unlike lists, none of the operations that modify a tuple are allowed.\n\n```python\nx = (1, 1, 2, 3, 5)\nx[3] = 100\n# TypeError: 'tuple' object does not support item assignment\n```\n\n## \n\nAssigning a new object to a variable does not affect/modify the current object the variable refers to.\n\n```python\nx = (1, 1, 2, 3, 5)\n\n# This does not modify the above tuple object so\n# it is allowed\nx = (1, 2, 3) \n```\n\n##\n\nIn general, operations that do not modify a tuple are available.\n\n```python\ntup = (45, 23, 'abc') \n\n# Indexing and slicing work the same way as lists.\nprint(tup[1])\n# 23\nprint(tup[1:])\n# (23, 'abc')\n\nprint(len(tup)) # number of items in a tuple\n# 3\n```\n\nSimilary `min()`, `max()`, and `sum()` functions work with tuples.\n\n##\n\n```python\ntup = (45, 23, 'abc') \n\n# Following methods are available for tuples\nprint(tup.index(\"abc\"))\n# 2\n\nprint(tup.count(23))\n# 1\n```\n\n##\n\nSince a tuple is a sequence, we can use it in a `for` loop just like a list:\n:::hgrid\n```python\nnumbers = (10, 20, 30)\n\nfor n in numbers:\n print(n)\n```\n```output\n10\n20\n30\n```\n:::\n\n## Why use tuples?\n\nIf lists are more flexible than tuples, why should we use tuples?\n\n- Immutability is useful to avoid changing data by mistake.\n- We can use tuples as elements of sets and as keys in a dictionary.\n- Programs are a bit faster when working with tuples.\n\n\n## Object Identity\n\n- Each object is assigned an :sc[ID] at its creation (think of a memory address).\n- This ID is unique and constant for this object as long as the object exists.\n- The built-in function `id()` can be used to retrieve the ID of an object.\n\n\n```python\nx = 1234\ny = x\n# x and y both refer to the same object, \n# therefore the IDs are the same.\nprint(id(x) == id(y)) # True\n```\n\n##\n\n```python\n# x and y point to two different objects, \n# therefore we expect x and y to have different IDs.\nx = 1234\ny = 5678\nprint(id(x) == id(y)) # False\n\n# x and y point to two different objects (with the same value),\n# therefore we expect x and y to have different IDs.\nx = int(\"1234\") # integer 1234\ny = int(\"12\" + \"34\") # integer 1234\n\nprint(id(x) == id(y)) # False\n```\n\n\n## Identity operators :style{.ppt-f95}\n\n- `is` and `is not` are comparison operators used to check if the two operands refer to the same object.\n- Using `is` operator means: are two variables referring to one and the same object?\n- Using `==` operator means: are two variables referring to objects that contain same data?\n\n:::hgrid\n```python\nx = int(\"1234\")\ny = int(\"12\" + \"34\")\nz = x \nprint(x == y) # True\nprint(x == z) # True\n```\n```python\nx = int(\"1234\")\ny = int(\"12\" + \"34\")\nz = x \nprint(x is y) # False\nprint(x is z) # True\n```\n:::\n\n\n## Mutable vs Immutable objects\n\n:sc[Immutable]: the content of the object cannot be changed after the object has been created. \n- e.g. `str`, `int`, `float`, `tuple`\n\n:sc[Mutable]: the content of the object can be changed after its creation without changing its identity.\n- e.g. `list`, `dict`, `set`\n\n\n## Strings are immutable\n\nUnlike lists, we cannot use the square brackets to modify a character in the string. \n\n```python lineno=false\ns = \"cats\"\ns[0] = \"r\" # TypeError: 'str' object does not support item assignment\n```\n\n##\n\nAll strings operations that seem to change a string actually :i[create] a new string.\n\n:::hgrid\n```python\ns = \"cat\"\nt = s\nprint(\"Before:\", s is t)\n\ns = s.replace('c','r')\nprint(\"After:\", s is t)\n\nprint(\"s:\", s)\nprint(\"t:\", t)\n```\n```output\nBefore: True\nAfter: False\ns: rat\nt: cat\n```\n:::\n\n## Lists are mutable\n\nThe following code does not create a copy of the list `x`. \nIt simply create a new variable name for the same list.\n\n```python\nx = [1, 2, 3]\ny = x # new name y for same list\n\nprint(x is y) # True\n```\n\nLet us see some implications of this.\n\n##\n\nIn the following illustrations, think about what is modified.\n- Whether a variable changes its value i.e. the variable refers to a different value\n- Whether a list object is modified i.e. some element of the list is changed.\n\n##\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"800\" height=\"310\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=x%20%3D%205%0Ay%20%3D%20x%0Ay%20%3D%20y%20%2B%203%20%0Aprint%28x,%20y%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n##\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"800\" height=\"400\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=x%20%3D%20%5B5,%206,%207%5D%0Ay%20%3D%20x%0Ay%5B0%5D%20%3D%20y%5B0%5D%20%2B%203%0Aprint%28x%5B0%5D,%20y%5B0%5D%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n##\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"800\" height=\"400\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=def%20example%28x%29%3A%0A%20%20%20%20x%20%3D%20x%20*%205%0A%0Ax%20%3D%205%0Aexample%28x%29%20%0Aprint%28x%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n##\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"800\" height=\"500\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=def%20example%28x%29%3A%0A%20%20%20%20x%5B0%5D%20%3D%20x%5B0%5D%20*%205%0A%0Ax%20%3D%20%5B5,%206,%207%5D%0Aexample%28x%29%0Aprint%28x%5B0%5D%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=true\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n::divider\n\n\n","title":"4.1 — List methods, Tuples, Immutable objects","date":"2024-05-20","published":true},{"slug":"Lecture-4.2.md","content":"\n\n\n## Sets\n\n- A set is a an :i[unordered] collection of :i[immutable] objects.\n- A set always contains unique elements, unlike lists and tuples which allow duplicates.\n- A set is unordered i.e. we cannot use indexing or slicing on a set object\n\n:::hgrid\n```python lineno=false\nnumbers = {1, 2, 3}\nprint(numbers) # {1, 2, 3}\nprint(type(numbers)) # \u003cclass 'set'\u003e\n```\n```python lineno=false\n# only unique values are kept\nnumbers = {1, 2, 3, 1, 3}\nprint(numbers) # {1, 2, 3}\nprint(len(numbers)) # 3\n```\n:::\n\n## Other ways to create a set\n\n```python\n# a set can be created from any sequence\n# such as list, tuple or a string\nthings = set([10, 42, \"apple\", 42])\nprint(things) # {'apple', 10, 42}\n\nword = \"pineapple\"\nletters = set(word)\nprint(letters) \n# {'p', 'n', 'l', 'i', 'e', 'a'}\n```\n\n##\n\n```python\n# create an empty set\nempty_set = set()\nprint(len(empty_set)) # 0\n\n# This does not create an empty set!\nempty_dictionary = {}\nprint(type(empty_dictionary)) # \u003cclass 'dict'\u003e\n```\n\n## Set elements must be immutable\n\nA set can contain `int`, `float`, `str`, `bool` and `tuple` objects because they are all immutable.\n\nBut a set cannot contain a list because lists are mutable.\n\n```python\n# tuples are immutable so allowed in set\npoints = {(1, 1), (3, 10), (3, 10)}\nprint(points) \n# {(3, 10), (1, 1)}\n\n# lists are mutable so not allowed\npoints = {[1, 1], [3, 10], [3, 10]} \n# TypeError: unhashable type: 'list'\n```\n\n## Set operators and methods\n\n```python\n# set.add(x):\n# Adds an element x to the set if x does not exist in the set. \n# Does not return anything.\n\nnumbers = {1, 2, 3}\nnumbers.add(20)\nprint(numbers) # {1, 2, 3, 20}\n\nnumbers.add(3)\nprint(numbers) # {1, 2, 3, 20}\n```\n\n##\n\n```python\n# set.remove(x):\n# Remove an element x from the set. Does not return anything.\n# Throws KeyError if element x is not present in the set.\n\nnumbers = {1, 2, 3}\nnumbers.remove(2)\nprint(numbers) # {1, 3}\n\nnumbers.remove(5) # KeyError: 5\n```\n\n##\n\n`in`, `not in` operators can be used to check if an element exists in a set.\n\n```python\nnumbers = {1, 2, 3}\nprint(2 in numbers) # True\nprint(5 not in numbers) # True\n\nshapes = {'circle', 'square'}\nprint(\"circle\" in shapes) # True\n```\n\n## \n\n:b[Sets cannot be indexed or sliced because they are not ordered.]\n\n:::hgrid\n```python\nprimes = {2, 3, 5, 7, 11}\nprimes[4] # TypeError: 'set' object is not subscriptable\n```\n:::\n\nBut we can use for loop to iterate over the items:\n\n:::hgrid\n```python\n# The order in which items will be printed \n# is not defined because sets are not ordered\n\nnumbers = {10, 1, 5, 20}\nfor n in numbers:\n print(n)\n```\n```output\n1\n10\n20\n5\n```\n:::\n\n## Why use sets?\n\n- Set are faster than lists and tuples, when inserting, removing and searching elements.\n- When order of elements is important or when elements are mutable, use lists or tuples\n- When only unique immutable elements need to be stored, use sets.\n\n##\n\nTime for some problems on Ed Lessons.\n\n##\n\n\n## Set methods\n\n```python\n# set.update(iterable):\n# Adds all items from the iterable to the set.\n\nnumbers = {1, 2, 3}\nnumbers.update([10, 2, 2, 3, 20])\nprint(numbers) # {1, 2, 3, 10, 20}\n\nnumbers.update((\"a\", \"b\")) \nprint(numbers) # {1, 2, 3, 'a', 10, 20, 'b'}\n\nprimes = {2, 3, 5}\nprimes.update({5, 7, 11})\nprint(primes) # {2, 3, 5, 7, 11}\n```\n\n##\n\n```python\n# set.clear(): Remove all elements from this set.\n\nnumbers = {1, 2, 3}\nnumbers.clear()\nprint(numbers) # set()\n```\n\n##\n\n```python\n# For the following methods, suppose A and B are sets.\n\n# A.intersection(B):\n# Returns a new set that contains elements that are\n# present in both A and B \n\nodd = {3, 5, 7, 9, 25}\nsquares = {4, 9, 25, 36}\nodd_squares = odd.intersection(squares)\nprint(odd_squares) # {9, 25}\n\n# Intersection can also be done using operator \u0026\nodd_squares = odd \u0026 squares\nprint(odd_squares) # {9, 25}\n```\n\n##\n\n```python\n# A.union(B):\n# Returns a new set that contains elements that are\n# present in A or B or both\n\nx = {1, 2, 3}\ny = {2, 3, 5}\nall_numbers = x.union(y)\nprint(all_numbers) # {1, 2, 3, 5}\n\n# Same above but using an operator |\nall_numbers = x | y\nprint(all_numbers) # {1, 2, 3, 5}\n```\n\n##\n\n```python\n# A.difference(B):\n# Returns a new set that contains elements that are\n# present only in A but not in B\nx = {1, 2, 3}\ny = {2, 3, 5}\ndiff = x.difference(y)\nprint(diff) # {1}\n\n# Same as above but using operator -\ndiff = x - y\nprint(diff) # {1}\n```\n\nAll of the `set` methods work the same when elements are of other types such as strings.\n\n## Dictionaries :style{.ppt-f95}\n\nSuppose we would like to store the following enrollment data:\n\n:::hgrid{.ppt-f95 margin=\"0\"}\n| semester | no. of students |\n|----------|-----------------|\n| F2017 | 816 |\n| W2018 | 613 |\n| F2018 | 709 |\n| W2019 | 590 |\n:::\n\nWe can do this using two lists for the two columns:\n```python\nsemesters = ['F2017', 'W2018', 'F2018', 'W2019']\nstudents = [816, 613, 709, 590]\n```\n\n\n\n##\n\nWhat should we do if we want to add new data?\n\n```python\nsemesters = ['F2017', 'W2018', 'F2018', 'W2019']\nstudents = [816, 613, 709, 590]\n\nsemesters.append(\"F2020\")\nstudents.append(550)\n# ['F2017', 'W2018', 'F2018', 'W2019', 'F2020']\n# [816, 613, 709, 590, 550]\n``` \n\n##\n\nWhat if we want to modify the value for a specific semester?\n\n```python\nsemesters = ['F2017', 'W2018', 'F2018', 'W2019']\nstudents = [816, 613, 709, 590]\n\nidx = semesters.index(\"W2018\")\nstudents[idx] = 600\n```\n\n##\n\nWhat we if try to add an entry for a semester that already exists? \n\nList allows duplicates so it does not check if a semester already exists.\n\n```python\nsemesters = ['F2017', 'W2018', 'F2018', 'W2019']\nstudents = [816, 613, 709, 590]\n\nsemesters.append(\"F2018\")\nstudents.append(500)\n# ['F2017', 'W2018', 'F2018', 'W2019', 'F2018']\n# [816, 613, 709, 590, 500]\n```\n\n## Use a dictionary!\n\n- You can think of an item of a dictionary as a pair of objects:\n - The first object of the pair is called a :sc[key].\n - The second object is referred to as the :sc[value].\n- A dictionary is called a :i[mapping] type because it maps key objects to value objects.\n\n```python\n# A dictionary is created using a sequence of key-value pairs\nenrollment = {'F2017': 816, 'W2018': 613,\n 'F2018': 709, 'W2019': 590}\n\nprint(type(enrollment)) # \u003cclass 'dict'\u003e\n```\n\n##\n\n```python\n# Number of key-value pairs\nprint(len(enrollment)) # 4\n\n# This is an empty dictionary, not a set!\nempty_dict = {}\nprint(len(empty_dict)) # 0\n```\n\n## Dictionary Examples\n\n```python\n# Key: a number, Value: True if number is prime, else False\nis_prime = {2: True, 3: True, 4: False, 5: True,\n 7: True, 10: False}\n\n# Key: inventory items, Value: count of items in inventory\ninventory = {\"sofa\": 5, \"table\": 10, \"chair\": 20, \"mattress\": 5}\n\n# Key: city name, Value: area of city\npopulation = {\"Montreal\": 431.50, \"Toronto\": 630.20}\n\n# Key: country name, Value: capital city\ncapitals = {\"Canada\": \"Ottawa\", \"United States\": \"Washington, D.C.\",\n \"France\": \"Paris\", \"Germany\": \"Berlin\"}\n```\n\n## Note on keys and values\n\n- Keys\n - Have to be immutable objects.\n - Have to be unique in a dictionary. A dictionary cannot contain two items with the same key.\n- Values\n - Values can be of any type; both mutable and immutable values are allowed.\n - Many keys can map to the same value. i.e. values need not be unique.\n\n## Dictionary Lookup\n\nWith lists, we can access an item of the list through its index.\n\nWith dictionaries, we can access a value stored in the dictionary through the key associated with it.\n\n```python\nenrollment = {'F2017': 816, 'W2018': 613, 'F2018': 709,\n 'W2019': 590, 'F2019': 744}\n\nnum_students = enrollment[\"F2018\"]\nprint(num_students) # 709\n\n# Key must exist in the dictionary if we want to access its value\nprint(enrollment[\"F2020\"]) # KeyError: 'F2020'\n```\n\n::divider\n\n\n","title":"4.2 — Sets, Dictionaries","date":"2024-05-22","published":true},{"slug":"Lecture-5.1.md","content":"\n\n## Iterable\n\nAn :sc[iterable] is a kind of object that can produce a sequence of other objects and hence can be used in a `for` loop.\n\n- range: sequence of integers\n- strings: sequence of characters\n- tuples: (immutable) sequence of any object\n- lists: sequence of any object\n- dictionaries: collection of tuples (key, value)\n- sets: collection of immutable objects (but order is not defined)\n\n\n##\n\nIterables can be used as arguments of the functions `list()`, `set()`, `tuple()`, `dict()`, etc.\n\n```python\n# range object only stores start, end and step size.\nprint(range(10, 101, 10)) # range(10, 101, 10)\n\n# list stores all objects in memory \nl = list(range(10, 101, 10))\nprint(l)\n# [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n\ns = set(range(10, 101, 10))\nprint(s)\n# {100, 70, 40, 10, 80, 50, 20, 90, 60, 30}\n```\n\n\n## Dictionary Lookup\n\nWith lists, we can access an item of the list through its index.\n\nWith dictionaries, we can access a value stored in the dictionary through the key associated with it.\n\n```python\nenrollment = {'F2017': 816, 'W2018': 613, 'F2018': 709,\n 'W2019': 590, 'F2019': 744}\n\nnum_students = enrollment[\"F2018\"]\nprint(num_students) # 709\n\n# Key must exist in the dictionary if we want to access its value\nprint(enrollment[\"F2020\"]) # KeyError: 'F2020'\n```\n\n## Adding an item\n\nWe can add a new item by specifying a key and a value: `dictionary[key] = value`\n\n```python\nenrollment = {'F2018': 709, 'W2019': 590}\n\nenrollment[\"F2020\"] = 800 # add an item\nenrollment[\"W2020\"] = 900 # add another item\n\n# {'F2018': 709, 'W2019': 590, 'F2020': 800, 'W2020': 900}\n\nenrollment[\"F2018\"] = 700 # change an existing item\n\n# {'F2018': 700, 'W2019': 590, 'F2020': 800, 'W2020': 900}\n```\n\n## Removing an item\n\nWe can delete an item using the following syntax: `del dictionary[key]`\n\n```python\nenrollment = {'F2018': 709, 'W2019': 590, 'F2019': 744}\n\ndel enrollment[\"F2019\"]\n\n# {'F2018': 709, 'W2019': 590}\n\ndel enrollment[\"F2020\"]\n# KeyError: 'F2020'\n```\n\n## \n\nWhat will be printed in the following examples?\n\n:::hgrid\n```python\nd = {'x' : 0, 'y' : 1, 'z' : 2} \nx = d['y']\nprint(x)\n```\n:::\n:::hgrid\n```python\nd = {'x' : 0, 'y' : 1, 'z' : 2} \nx = d[0]\nprint(x)\n```\n:::\n\n## Check for membership\n\nWe can check if a key is part of a dictionary using the `in` and `not in` operators.\n\n```python\nd = {'x' : 0, 'y' : 1, 'z' : 2} \nprint('x' in d) # True \nprint(0 in d) # False\nprint(0 not in d) # True\n```\n\n##\n\n:::greenbox\n\"Performance of searching a list, a set and dictionary\" on Ed.\n:::\n\n\n## Iterating through a dictionary\n\nWe can use a for loop to iterate through all the keys in a dictionary.\n\n```python\nenrollment = {'F2018': 709, 'W2019': 590, 'F2019': 744}\n\nfor key in enrollment:\n print(key, \"-\u003e\", enrollment[key])\n```\n\n```output\nF2018 -\u003e 709\nW2019 -\u003e 590\nF2019 -\u003e 744\n```\n\n## Functions and methods for dictionaries\n\n```python\n# dict(L): creates and returns a dictionary using a list L of tuples, \n# where each tuple is of length 2 in form of (key, value).\n\npairs = [(\"Montreal\", 1.78), (\"Rome\", 2.87), (\"Tokyo\", 9.27)]\npopulation_data = dict(pairs)\n\nprint(population_data)\n# {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\n\nprint(population_data[\"Rome\"])\n# 2.87\n```\n\n##\n\n```python\npopulation_data = {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\n\n# dict.keys(): returns a iterable (sequence) of all keys\n\ncities = list(population_data.keys())\nprint(cities) # ['Montreal', 'Rome', 'Tokyo']\n\n# dict.values(): returns a iterable (sequence) of all values\n\npopulation = list(population_data.values())\nprint(population) # [1.78, 2.87, 9.27]\n```\n\n##\n\n```python\n# dict.items(): returns a iterable (sequence) of tuples (key, value) \n# for all items in the dictionary\n\npopulation_data = {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\npairs = list(population_data.items())\n\nprint(pairs)\n# [('Montreal', 1.78), ('Rome', 2.87), ('Tokyo', 9.27)]\n```\n\n\n##\n\nUsing the `dict` methods in `for` loop:\n\n```python\npopulation_data = {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\n\ntotal = 0\nfor population in population_data.values():\n total += population\n\nprint(total) # 13.92\n```\n\n##\n\n```python\npopulation_data = {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\n\n# dict.items() returns an iterable of key-value tuples\n\nfor tup in population_data.items():\n city = tup[0]\n population = tup[1]\n print(city, \"-\u003e\", population)\n\n# Montreal -\u003e 1.78\n# Rome -\u003e 2.87\n# Tokyo -\u003e 9.27\n```\n\n\n## Packing vs Unpacking\n\nWhen we create a string, a list, or a tuple, we are packing several elements into a single object.\n\n```python\ns = \"cat\"\nmy_list = [5, 'a']\nmy_tuple = (0, 3, 7)\n```\n\n##\n\n:sc[Unpacking] allows us to assign values in a string/list/tuple to multiple variables. \nWe must know the exact length of the string/list/tuple.\n\n```python\ns = \"cat\"\na, b, c = s\n# a, b and c are all strings\nprint(a) # c\nprint(b) # a\nprint(c) # t\n\nmy_list = [5, 'cat']\nx, y = my_list\nprint(x) # 5\nprint(y) # cat\n```\n\n##\n\n```python\n# Parentheses are optional in this context.\nmy_tuple = 0, 3, 7\nx, y, z = my_tuple\nprint(x) # 0\nprint(y) # 3\nprint(z) # 7\n\n# Variables must match number of elements\ntup = 1, 2, 3 \nx, y = tup\n# ValueError: too many values to unpack (expected 2)\n```\n\n## Multiple assignment using packing/unpacking on same line\n\n```python\n# We are creating a tuple on the right side and \n# unpacking it into 3 variables.\ncity, population, area = 'Montreal', 1704694, 431.5\nprint(city) # Montreal\nprint(population) # 1704694\nprint(area) # 431.5\n```\n\n## Returning a tuple from a function and unpacking\n\n```python\ndef min_max(mylist):\n # Return a tuple of two elements\n return min(mylist), max(mylist)\n \n \n# Unpack the returned tuple into 2 variables\nx, y = min_max([2, -3, 10, 20])\nprint(x) # -3\nprint(y) # 20\n```\n\n\n##\n\nMaking use of tuple unpacking in the for loop:\n\n```python\npopulation_data = {'Montreal': 1.78, 'Rome': 2.87, 'Tokyo': 9.27}\n\nfor city, population in population_data.items():\n print(city, \"-\u003e\", population)\n\n# Montreal -\u003e 1.78\n# Rome -\u003e 2.87\n# Tokyo -\u003e 9.27\n```\n\n##\n\nTime for some problems on Ed Lessons.\n\n\n## Updating multiple items in a list using slice assignment\n\nThe syntax of the slice assignment is as follows:\n\n```python lineno=false\nlist_object[start:stop:step] = iterable\n```\n\nThink of `iterable` as a sequence/collection type such as a list, string, tuple etc.\n\n##\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\nprint(days[1:3])\n# ['Tue', 'Wed']\n\n# Replaces items at indices 1 \u0026 2\ndays[1:3] = [1, 2]\nprint(days)\n# ['Mon', 1, 2, 'Thu', 'Fri', 'Sat', 'Sun']\n```\n\n##\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\nprint(days[1::2])\n# ['Tue', 'Thu', 'Sat']\n\ndays[1::2] = [\"\", \"\", \"\"]\nprint(days)\n# ['Mon', '', 'Wed', '', 'Fri', '', 'Sun']\n\n# String is considered as sequence of letters\ndays[:3] = \"MTW\"\nprint(days)\n# ['M', 'T', 'W', '', 'Fri', '', 'Sun']\n```\n\n##\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\n# Replace many items with fewer\ndays[0:5] = [\"Vacation!\"]\nprint(days) # ['Vacation!', 'Sat', 'Sun']\n\n# Removing items\ndays[1:] = []\nprint(days) # ['Vacation!']\n```\n\n##\n\n```python\ndays = [\"Mon\", \"Fri\", \"Sat\", \"Sun\"]\nprint(days[1:2]) # ['Fri']\n\n# Replace fewer items with many\ndays[1:2] = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", ]\nprint(days)\n# ['Mon', 'Mon', 'Tue', 'Wed', 'Thu', 'Sat', 'Sun']\n```\n\n##\n\n```python\ndays = [\"Mon\", \"Fri\", \"Sat\", \"Sun\"]\n\n# Start and stop indices are same\nprint(days[1:1]) # []\n\n# Replace fewer items with many\ndays[1:1] = [\"Tue\", \"Wed\", \"Thu\", ]\nprint(days)\n# ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n```\n\n## Other list methods\n\n```python\n# list.reverse() : Reverse the list \"in place\"\ngrades = [90, 70, 60.5, 70, 80]\ngrades.reverse()\nprint(grades) # [80, 70, 60.5, 70, 90]\n\n# list.clear() : Remove all items from list.\ngrades = [90, 70, 60.5, 70, 80]\ngrades.clear()\nprint(grades) # []\n```\n\n## What modifies a list?\n\n- Assigning a value to an element using its index. \n ```python\n a = [1, 2, 3]\n a[0] = 5\n ```\n- Using the slice assignment to modify the elements of a list. \n ```python\n a = [1, 2, 3]\n a[:2] = [4, 5]\n ```\n- Using methods like :code[append(), insert(), remove() , pop(), clear()], etc.\n\n\n## What does not modify a list?\n\n- Slicing! It is a useful tool to create new lists out of an existing lists.\n ```python\n a = [1, 2, 3]\n b = a[:] # makes a copy of a\n print(a is b) # False\n ```\n- The `+` and `*` operators create a new list\n ```python\n a = [1, 2, 3]\n b = a # does not copy, just a new name for same list\n b = b + [4] # b now refers to a new list [1, 2, 3, 4]\n # a still refers to [1, 2, 3]\n print(a is b) # False\n ```\n\n\n## Nested Lists\n\nAn element of a list can be another list! \nSuch lists are called :sc[nested] lists.\n\n```python\nnested_list = [[1], [1, 2, 3], [1, 2]]\nprint(type(nested_list))\n# \u003cclass 'list'\u003e\n\nprint(nested_list[0])\n# [1]\nprint(nested_list[1])\n# [1, 2, 3]\nprint(nested_list[2])\n# [1, 2]\n```\n\n##\n\nNested lists are useful to store data which come in form of a table or spreadsheet.\n\n```python\n# Name, A1, A2, A3\nstudent_grades = [[\"Student-A\", 90, 95, 100], \n [\"Student-B\", 85, 90, 98], \n [\"Student-C\", 70, 75, 80]] \n```\n\n##\n\nWe can perform same operations on nested lists as we saw earlier: indexing, slicing, etc.\n\n```python\nstudent_grades = [[\"Student-A\", 90, 95, 100],\n [\"Student-B\", 85, 90, 98],\n [\"Student-C\", 70, 75, 80]]\n\n# Print name of 2nd student\nprint(student_grades[1][0]) \n# Student-B\n\n# Change A2 grade for Student-B\nstudent_grades[1][2] = 100\nprint(student_grades)\n# [['Student-A', 90, 95, 100], ['Student-B', 85, 100, 98], \n# ['Student-C', 70, 75, 80]]\n```\n\n## Iterating in a :i[row-first] order\n\n:::hgrid\n```python\nmatrix = [[81, 75, 90, 60], \n [80, 70, 85, 55],\n [40, 50, 45, 85]]\n\nnum_rows = len(matrix) \nnum_cols = len(matrix[0]) \n\nprint(\"Row-first order:\")\nfor r in range(num_rows):\n for c in range(num_cols):\n print(matrix[r][c], end=\" \")\n print()\n```\n```output\nRow-first order:\n81 75 90 60 \n80 70 85 55 \n40 50 45 85 \n```\n:::\n\n## Iterating in a :i[column-first] order\n\n:::hgrid\n```python\nmatrix = [[81, 75, 90, 60], \n [80, 70, 85, 55],\n [40, 50, 45, 85]]\n\nnum_rows = len(matrix) \nnum_cols = len(matrix[0])\n\nprint(\"\\nColumn-first order:\")\nfor c in range(num_cols):\n for r in range(num_rows):\n print(matrix[r][c], end=\" \")\n print()\n```\n```output\nColumn-first order:\n81 80 40 \n75 70 50 \n90 85 45 \n60 55 85\n```\n:::\n\n## List of tuples\n\n```python\npoints = [(1, 1, 3), (4, 10.5, 9), (7, 4.4, 9.7)]\n\n# List element can be modified:\npoints[1] = (4, 12, 10) # Assign new point\nprint(points) # [(1, 1, 3), (4, 12, 10), (7, 4.4, 9.7)]\n\n# Trying to change the second points's z-coordinate\npoints[1][2] = 20 \n# TypeError: 'tuple' object does not support item assignment\n```\n\n##\n\nIterating over list of tuples:\n\n:::hgrid\n```python\npoints = [(1, 1, 3), (4, 10.5, 9), \n (7, 4.4, 9.7)]\n\nfor p in points: # p is a tuple\n print(p)\n\n# Unpack a tuple into 3 variables\nfor x, y, z in points: \n print(x, y, z)\n```\n\n```output\n(1, 1, 3)\n(4, 10.5, 9)\n(7, 4.4, 9.7)\n1 1 3\n4 10.5 9\n7 4.4 9.7\n```\n:::\n\n\n::divider\n","title":"5.1 — Iterables, More dict \u0026 list operations, Nested lists","date":"2024-05-24","published":true},{"slug":"Lecture-5.2.md","content":"\n\n## What's wrong in this example?\n\n```python\ndef average(nums):\n total = 0\n\n for x in grades:\n total += x\n\n return total / len(grades)\n\n\ngrades = [85, 100, 98, 75]\nprint(average(grades)) # 89.5\n```\n\n##\n\n- Try to avoid using global variables within functions when possible.\n- Assignment autograder will fail if you use global variable(s) instead of function parameter(s) inside a function.\n\n\n\n## List of dictionaries\n\nA dictionary can be used to represent a data record such as student record in a course in the following example.\n\nA list of such dictionaries can store multiple data records.\n\n```python\nstudent_records = [\n {\"name\": \"Student-A\", \"ID\": 2601234, \"grades\": [90, 95, 100]},\n {\"name\": \"Student-B\", \"ID\": 2601000, \"grades\": [95, 95, 97]},\n {\"name\": \"Student-C\", \"ID\": 2605000, \"grades\": [80, 85, 90]}\n]\n\n# Each \"grades\" list has grades for 3 assignments.\n```\n\n##\n\nWhat are assignment 1 grades for \"Student-B\" ?\n\n```python\nstudent_records = [\n {\"name\": \"Student-A\", \"ID\": 2601234, \"grades\": [90, 95, 100]},\n {\"name\": \"Student-B\", \"ID\": 2601000, \"grades\": [95, 95, 97]},\n {\"name\": \"Student-C\", \"ID\": 2605000, \"grades\": [80, 85, 90]}\n]\n\nprint(student_records[1][\"grades\"][0]) # 95\n```\n\n##\n\nDisplay total grade for each student.\n\n```python\nstudent_records = [\n {\"name\": \"Student-A\", \"ID\": 2601234, \"grades\": [90, 95, 100]},\n {\"name\": \"Student-B\", \"ID\": 2601000, \"grades\": [95, 95, 97]},\n {\"name\": \"Student-C\", \"ID\": 2605000, \"grades\": [80, 85, 90]}\n]\n\nfor record in student_records:\n name, total_grade = record[\"name\"], sum(record[\"grades\"])\n print(f\"Total grade for {name} is {total_grade}\")\n```\n```output\nTotal grade for Student-A is 285\nTotal grade for Student-B is 287\nTotal grade for Student-C is 255\n```\n\n\n## Comparing data structures\n\nData structures — list, tuples, sets and dictionaries can be compared for equality using `==` and `!=` operators.\n\n```python\n# Lists, order matters\ngrades1 = [85, 80, 100]\ngrades2 = [85, 80, 100]\ngrades3 = [85, 100, 80]\n\n# True only when all elements are equal in order\nprint(grades1 == grades2) # True\nprint(grades2 == grades3) # False\n```\n\n##\n\n```python\n# Comparing tuples, order matters\npoint1 = (1, 1, 2)\npoint2 = (1, 2, 1)\nprint(point1 != point2) # True\n\n\n# Comparing sets, order does not matter\nfruits1 = {\"apple\", \"orange\", \"banana\"}\nfruits2 = {\"orange\", \"apple\", \"banana\"}\n\n# True only when sets are of equal length and \n# both sets contain same elements\nprint(fruits1 == fruits2) # True\nprint(fruits1 == {\"apple\", \"orange\", \"banana\", \"grapes\"}) # False\n```\n\n##\n\n```python\n# Comparing dictionaries, order does not matter\nphonebook1 = {\"A\": 5140001000, \"B\": 5140002000, \"C\": 5140003000 }\nphonebook2 = { \"B\": 5140002000, \"A\": 5140001000, \"C\": 5140003000 }\n\n# True only when dictionaries are of equal length and \n# both contain same key-value pairs\nprint(phonebook1 == phonebook2) # True\n\nprint(phonebook1 == {\"A\": 4381001000, \"B\": 5140002000, \n \"C\": 5140003000 }) # False\n```\n\n## Comparison works for nested structures as well\n\n```python\npoints1 = [(1, 1), (2, 10)]\npoints2 = [(1, 1), (2, 10)]\nprint(points1 == points2) # True\n\nprint(points1 == [(1, 1), (2, 5)]) # False\nprint(points1 == [(1, 1), [2, 10]]) # False\n\nstudent1 = {\"name\": \"Student-B\", \"grades\": [90, 100, 100]}\nstudent2 = {\"name\": \"Student-B\", \"grades\": [90, 100, 90]}\nprint(student1 == student2) # False\n```\n\n\n## List, set and dictionary comprehensions\n\nWe often find ourselves repeating the following pattern to create a list. \n\nPython provides a simpler way to create a list using list comprehension.\n\n```python\nsome_list = []\nfor i in some_iterable:\n some_list.append(some_expression)\n\n# Using list comprehension\nsome_list = [some_expression for i in some_iterable]\n```\n\n##\n\n```python\neven_nums = [i for i in range(2, 20, 2)]\nprint(even_nums)\n# [2, 4, 6, 8, 10, 12, 14, 16, 18]\n\n\n# Need not use loop variable i in the expression\nzeros = [0 for i in range(7)]\nprint(zeros)\n# [0, 0, 0, 0, 0, 0, 0]\n```\n\n##\n\n```python\nimport math\n\nsine_values = [math.sin(x) for x in [-math.pi/2, 0, math.pi/2]]\nprint(sine_values)\n# [-1.0, 0.0, 1.0]\n\nstring = \"10.0,20.5,100.123\"\nnumbers = [float(word) for word in string.split(\",\")]\nprint(numbers)\n# [10.0, 20.5, 100.123]\n```\n\n## Using if-statement in list comprehension\n\n```python\nsquares_of_odds = []\nfor x in range(1, 20):\n if x % 2 != 0:\n squares_of_odds.append(x * x)\n\nprint(squares_of_odds)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n\n# same as above but using list comprehension\nsquares_of_odds = [x * x for x in range(1, 20) if x % 2 != 0]\nprint(squares_of_odds)\n# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]\n\n```\n\n## Set and dictionary comprehension\n\n```python\nodd_squares = {i*i for i in range(1, 20, 2)}\nprint(odd_squares)\n# {1, 121, 225, 289, 9, 169, 361, 81, 49, 25}\n\n\nnames = [\"A\", \"B\", \"C\", \"D\"]\nnames_to_index = {name: i for i, name in enumerate(names)}\nprint(names_to_index)\n# {'A': 0, 'B': 1, 'C': 2, 'D': 3}\n```\n\n::divider\n","title":"5.2 — Nested data structures, Comprehensions","date":"2024-05-27","published":true},{"slug":"Lecture-5.3.md","content":"\n\n##\n\n:b[How list is stored]{.sans}\n\n:::hgrid{gap=\"1em\" margin=\"0.5em 0\"}\n```python lineno=false\nmylist = [3, 17, 42]\n```\n\n::image{style=\"width: 100%\" src=\"lecture-8.1/mylist_actual.png\"} \n:::\n\n:::hgrid{cols=\"1fr 4fr\" margin=\"0.5em 0\"}\n:b[Simplified: ]{.sans}\n::image{style=\"width: 70%\" src=\"lecture-8.1/mylist_simplified.png\"} \n:::\n\n##\n\n:b[How :i[nested list] is stored]{.sans}\n\n```python lineno=false margin=\"0.5em 0\"\nnested_list = [[3, 17, 42], [10, 20, 30]]\n```\n\n::image{style=\"width: 65%; margin: 1em auto;\" src=\"lecture-8.1/nested_list.png\"} \n\n##\n\n:::hgrid{margin=\"0\"}\n```python lineno=false margin=\"0.5em 0\"\nzeros = [0] * 5\nC = [zeros] * 3\nC[1][3] = 100 \nprint(C) \n```\n```python lineno=false margin=\"0.5em 0\"\nzeros = [0] * 5\nC = []\nfor i in range(3):\n C.append(zeros)\nC[1][3] = 100 \nprint(C) \n```\n```output\n[[0, 0, 0, 100, 0], \n [0, 0, 0, 100, 0], \n [0, 0, 0, 100, 0]]\n```\n:::\n\n::image{style=\"width: 80%; margin: 0.5em auto;\" src=\"lecture-8.1/nested_zeros.png\"} \n\n##\n\n:::hgrid{margin=\"0\" cols=\"1fr 4fr\"}\n```python lineno=false margin=\"0.5em 0\"\nC = []\nfor i in range(3):\n C.append([0] * 5)\nC[1][3] = 100 \nprint(C)\n```\n\n::image{style=\"width: 100%; margin: 0.5em auto;\" src=\"lecture-8.1/nested_zeros_correct.png\"} \n:::\n\n\n## Lexicographic order\n\nGiven two sequences $a = a_0, a_1, ..., a_n$ and $b = b_0, b_1, ..., b_m$, if they are not equal, their ordering (whether $a \u003c b$) is decided as follows:\n- If $i$ is the first index where the mismatch occurs, then the order is decided by result of $a_i \u003c b_i$\n- If there is no mismatch and $a$ is shorter sequence than $b$, then $a$ must match $b$ at the beginning (i.e. $a$ is subsequence of $b$ starting at index $0$). In this case, $a \u003c b$ is true.\n\n## Comparing strings\nComparison operators work for strings as well. \nThe comparison is done :i[alphabetically] (i.e. follows order in Character Encoding such as ASCII or Unicode: https://d3vp.github.io/comp202-notes/Lecture-1.1.html#text-in-binary) \n\n\n:::div{.hgrid}\n```python\n\u003e\u003e\u003e \"cat\" == \"cat\"\nTrue\n\u003e\u003e\u003e \"cat\" == \"dog\"\nFalse\n\u003e\u003e\u003e \"cat\" != \"Cat\"\nTrue\n```\n```python\n# \"c\" appears before \"d\" alphabetically\n\u003e\u003e\u003e \"cat\" \u003c \"dog\"\nTrue\n\n# A-Z appear before a-z alphabetically\n\u003e\u003e\u003e \"cat\" \u003c \"Dog\"\nFalse\n```\n:::\n\n##\n\n```python\n# The first mismatched letter decides the order\n\u003e\u003e\u003e \"apple\" \u003c \"apply\"\nTrue\n\n# Shorter string is \"minimum\" if all letters in it match\n\u003e\u003e\u003e \"app\" \u003c \"apple\"\nTrue\n```\n\n##\n\n```python\n# Objects of different types are always not equal\n\u003e\u003e\u003e \"cat\" == 123\nFalse\n\n# inequality is not allowed \n# between a number and str\n\u003e\u003e\u003e \"cat\" \u003c 123\nTypeError: '\u003c' not supported between instances of 'str' and 'int'\n```\n\n## Comparing sequences — lists \u0026 tuples\n\nFor comparing sequences like lists and tuples, lexicographic order is used — comparing items at same index from beginning to end.\n\n```python\n\u003e\u003e\u003e [10, 20, 30] \u003c [10, 25, 30]\nTrue\n\n# Here: \"banana\" \u003c \"orange\" decides the order\n\u003e\u003e\u003e [\"apple\", \"banana\"] \u003c [\"apple\", \"orange\"]\nTrue\n# Here: \"app\" \u003c \"apple\" decides the order\n\u003e\u003e\u003e [\"apple\", \"banana\"] \u003c [\"app\", \"orange\"]\nFalse\n\n# Comparison tuples works the same way as lists\n\u003e\u003e\u003e (1, 1, 3) \u003c (1, 2, 3)\nTrue\n\n\n```\n\n##\n\n```python\n# Shorter sequence is \"minimum\" if all items in it match\n\u003e\u003e\u003e [1, 2] \u003c [1, 2, 3]\nTrue\n\u003e\u003e\u003e (10, 15) \u003c (10, 15, 20)\nTrue\n\n\u003e\u003e\u003e [1, 5] \u003c [1, 2, 3]\nFalse\n```\n\n## Sorting a list using `sort` method\n\n```python\n# list.sort() : \n# Sorts the list \"in place\" i.e. list will be modified.\n# Returns None.\n# The list is sorted in ascending order.\n\ngrades = [90, 70, 60.5, 70, 80]\ngrades.sort()\nprint(grades) # [60.5, 70, 70, 80, 90]\n\n\ngrades.sort(reverse=True) # descending order\nprint(grades) # [90, 80, 70, 70, 60.5]\n```\n\n##\n\n```python\ngrades = [90, 70, 60.5, 70, 80, \"A\"]\ngrades.sort()\n# TypeError: '\u003c' not supported between instances of 'str' and 'int'\n\n\n# Works with strings as well\nfruits = [\"banana\", \"orange\", \"apple\"]\nfruits.sort()\nprint(fruits) # ['apple', 'banana', 'orange']\n```\n\n## `sorted` function\n\n```python\n# sorted(iterable): \n# Return a new list containing all items from \n# the iterable in ascending order.\n# If any items cannnot be compared to each other, TypeError occurs.\n\ngrades = [90, 70, 60.5, 70, 80]\nsorted_grades = sorted(grades)\nprint(sorted_grades) # [60.5, 70, 70, 80, 90]\nprint(grades) # [90, 70, 60.5, 70, 80]\n\n# Sort in descending order\nsorted_grades = sorted(grades, reverse=True)\nprint(sorted_grades) # [90, 80, 70, 70, 60.5]\n```\n\n##\n\n```python\n# string is iterable\nword = \"pineapple\"\nsorted_letters = sorted(word)\nprint(sorted_letters)\n# ['a', 'e', 'e', 'i', 'l', 'n', 'p', 'p', 'p']\n\n\n# set is iterable\nfruits = {\"banana\", \"orange\", \"apple\"}\nsorted_fruits = sorted(fruits)\nprint(sorted_fruits) # ['apple', 'banana', 'orange']\n```\n\n##\n\n```python\n# dictionary is considered as an iterable of its keys\ninventory = {\"sofa\": 5, \"table\": 10, \"chair\": 20, \"mattress\": 5}\nsorted_names = sorted(inventory)\nprint(sorted_names) # ['chair', 'mattress', 'sofa', 'table']\n\n# same as above\nsorted_names = sorted(inventory.keys())\nprint(sorted_names) # ['chair', 'mattress', 'sofa', 'table']\n\n# iterable of values in the dictionary\nsorted_counts = sorted(inventory.values())\nprint(sorted_counts) # [5, 5, 10, 20]\n```\n\n##\n\n```python\npoints = [(4, 10, 9), (1, 1, 3), (7, 4, 11)]\n\nprint(min(points)) # (1, 1, 3)\n\nprint(max(points)) # (7, 4, 11)\n\nprint(sorted(points))\n# [(1, 1, 3), (4, 10, 9), (7, 4, 11)]\n```\n\n## `enumerate` function\n\n```python\nmylist = [10, 50, -3.14, 5]\nprint(enumerate(mylist))\n# \u003cenumerate object at 0x10e327100\u003e\n\n# enumerate creates an iterable of tuples (index, element), \n# which we convert to list\nlist_of_tuples = list(enumerate(mylist))\nprint(list_of_tuples)\n# [(0, 10), (1, 50), (2, -3.14), (3, 5)]\n```\n\n##\n\n:::hgrid\n```python\nmylist = [10, 50, -3.14, 5]\n\nfor i in range(len(mylist)):\n num = mylist[i]\n print(i, num)\n```\n```python\nmylist = [10, 50, -3.14, 5]\n\nfor i, num in enumerate(mylist):\n print(i, num)\n```\n:::\n\n\n## Writing \u0026 importing modules\n\n- What are modules exactly?\n - A module is simply a Python file containing definitions and statements.\n - Every `.py` file is a module. The name of the module is the name of the file.\n- Name of a Python file (module) must follow same rules as variable names.\n - Module names can only start with letters a-z, A-Z or an underscore and must only contain these letters, digits and underscores.\n\n##\n\nDownload module-files.zip from Ed Lessons. It contains `geometry.py` and `geometry_tester.py`\n\n##\n\nIn `geometry_tester.py` file, we import and use the functions defined in the module `geometry`:\n\n```python\n# Import functions from the module\nfrom geometry import euclidean_distance, sine\n\n# Call the sine function\nprint(sine(90))\n\ndist = euclidean_distance((1, 1), (2, 3)) # tuples\nprint(dist) # 2.23606797749979\n\n```\n\n## Some observations\n\n- When we import a module, all code inside that module is executed.\n- Add some statement such as `print(\"hello\")` in `geometry` module outside the functions. Then,\n - Run the `geometry.py` as the main program\n - Run another program which imports the module `geometry`.\n\n## Running a file as main program vs importing it\n\n- Sometimes, we may want to run some code only when a Python file is executed directly as main program but not when it is imported as a module.\n- For example, suppose we want the following test cases in `geometry.py`\n ```python\n print(sine(-90))\n print(sine(180))\n ```\n- How to make sure the test cases do not execute when `geometry` is imported as a module?\n\n## `__name__`\n\n- `__name__` is a special variable that the interpreter initializes whenever it executes a file.\n- When a module is executed, the interpreter does the following: \n - sets the value of `__name__` for that module using the filename\n - executes all the code in the module.\n- Each module has its own `__name__` variable.\n- Add `print(__name__)` in `geometry.py` and import `geometry` module in another program.\n\n## `\"__main__\"`\n\n- When we execute a file as the main program, then the variable `__name__` is set to be `\"__main__\"`\n- Run `geometry.py` directly and see what value of `__name__` is printed.\n\n## How to :i[not execute] code in a module when it is imported\n\nAdd the following at the end of the file `geometry.py`:\n\n```python\nif __name__ == \"__main__\":\n # Run the following code only when this file is \n # executed as main program but not when it is imported\n print(\"hello from geometry!\")\n print(sine(-90))\n print(sine(180))\n```\n\n\n## How to work with a file in Python\n\nA file is a sequence of characters or bytes stored on a storage device such as a hard drive.\n\n- Open the file using the built-in function `open()`\n- Read data from the file or write data into the file\n- Close the file\n\n\n## Opening a file with :code[open()]\n\nBuilt-in function `open(filename, mode)`:\n- `filename` (str): name of the file to read (if the file is in the current directory) or full path to the file.\n- `mode` (str): `'r'` for reading, `'w'` for writing, `'a'` for appending. If this argument is omitted, it defaults to `'r'`\n- Returns: a :sc[file object] which allows reading from/writing to the file.\n\n```python\nfilename = \"quotes.txt\"\nfobj = open(filename, \"r\") # mode \"r\" for reading\n```\n\n## Reading a file with :code[read()] method of file object\n\nFile objects have a method `read(size)` that takes one optional argument:\n- `size`: the number of characters to read from the file\n - If omitted, the entire file will be read.\n- Returns: a string containing the characters in the file\n\n## Closing a file with :code[close()] method of file object\n\n- `close()` method takes no argument and returns nothing. It closes the file associated with the file object\n\n##\n\nFor the following example, download the files from Ed Lessons and keep them in the same directory as the python program.\n\n```python\nfilename = \"quotes.txt\"\nfobj = open(filename, \"r\") # mode \"r\" for reading \n\nfile_content = fobj.read() # read whole file as a string\nprint(file_content) # print the string \n\nfobj.close() # close the file\n```\n\n## Why close a file? \n\nClosing the file is important for many reasons \n- Operating System (e.g Windows, Mac OS) may lock the file until it is closed (Other programs may not use the file as long as it is open)\n- Too many open files may cause your program/computer to slow down\n\n\n## Reading a file line by line\n\n- The file object returned by `open()` is an iterable that can produce a sequence of lines in the file. So we can use it in a `for` loop.\n- Each line will have a trailing newline (`\"\\n\"`) character.\n\n```python\nfilename = \"quotes.txt\"\nfobj = open(filename, \"r\")\n\nfor line in fobj: # file object is iterable\n line = line.rstrip(\"\\n\") # Remove trailing \"\\n\" character\n print(line) \n\nfobj.close()\n```\n\n## Reading a file as list of lines\n\n```python\nfilename = \"quotes.txt\"\nfobj = open(filename, \"r\")\n\n# Read whole file, split into lines and return a list of lines\nall_lines = fobj.readlines() \n\n# Each line will have a trailing newline character \"\\n\"\n\nprint(all_lines)\n\nfobj.close()\n```\n\n## Writing text to a file in Python\n\nTo write to a file in Python:\n- Open the file with open() using mode `'w'` for \"write.\"\n - If the file does not exist, it will be created.\n - :span[If the file exists, it will be deleted and replaced with an empty file.]{.bgred .px05}\n- Call `write(s)` method on the file object to write the string `s` into the file.\n- Close the file.\n\n##\n\n```python\nfilename = \"grades.txt\"\nfobj = open(filename, \"w\") # mode \"w\" for writing \n\nfobj.write(\"Name,A1,A2,A3\\n\") # write line to file\nfobj.write(\"Student-A,90,80,100\\n\") # write another line\nfobj.write(\"Student-B,100,90,100\\n\") # write another line\n\nfobj.close()\n```\n\n## Appending text to a file in Python\n\nAppending means adding text to the end of a file without changing/deleting text already present in the file.\n\n- Open the file with open() using mode `'a'` for \"append.\"\n - If the file does not exist, it will be created.\n - If the file exists, it is NOT deleted.\n- Call `write(s)` method on the file object, to write the string `s` at the end of the file.\n- Close the file.\n\n##\n\n```python\nfilename = \"grades.txt\"\nfobj = open(filename, \"a\") # mode \"a\" for appending \n\nfobj.write(\"Student-C,85,90,97\\n\")\nfobj.write(\"Student-D,95,90,97\\n\") \n\nfobj.close()\n```\n\n##\n\nTime for some problems on Ed Lessons.\n\n\n::divider","title":"5.3 — Sorting, Modules, Reading \u0026 Writing Files","date":"2024-05-30","published":true},{"slug":"Lecture-6.1.md","content":"\n## list comprehension — examples\n\nAssume that x and y are lists of numbers and both have equal length. Using list comprehension, find their dot product.\n\n```python\nx = [1, 2, 3]\ny = [10, 10, 10]\n\ndot_product = ???\nprint(dot_product) # 60\n```\n\n##\n\n```python\nx = [1, 2, 3]\ny = [10, 10, 10]\n\ndot_product = sum([x[i] * y[i] for i in range(len(x))])\nprint(dot_product) # 60\n```\n\n##\n\nGiven a matrix (list of lists), find the maximum of row sums using list comprehension.\n\n```python\nmatrix = [[1, 1, 1],\n [5, 5, 5],\n [2, 2, 2]]\nmax_sum = ???\nprint(max_sum) # 15\n```\n\n##\n\n```python\nmatrix = [[1, 1, 1],\n [5, 5, 5],\n [2, 2, 2]]\nmax_sum = max([sum(row) for row in matrix])\nprint(max_sum) # 15\n```\n\n## Nested list comprehensions\n\n```python\nmatrix = [[10, 20, 30],\n [40, 50, 60],\n [70, 80, 90]]\n\nsquared = [[x ** 2 for x in row] for row in matrix]\n\nprint(squared)\n# [[100, 400, 900],\n# [1600, 2500, 3600],\n# [4900, 6400, 8100]]\n\n```\n\n\n## `with` statement to open files\n\n```python\nwith open(\"myfile.txt\", \"r\") as fobj:\n # file remains open here inside with-statement\n file_content = fobj.read()\n\n# the file is automatically closed when\n# the with-statement is done\n\nprint(file_content)\n```\n\n##\n\nAny valid operations/methods can be performed on the file object inside the `with` statement.\n\n```python\nwith open(\"myfile.txt\", \"r\") as fobj:\n for line in fobj:\n line = line.rstrip(\"\\n\")\n print(line)\n```\n\nFor the Write and Append modes, the `write` method can be used in the `with` statement.\n\n##\n\n```python\nwith open(\"myfile.txt\", \"r\") as fobj:\n print(\"hello\")\n\nprint(fobj.read())\n```\n\n```output\nhello\nTraceback (most recent call last):\n File \"lecture_8.py\", line 4, in \u003cmodule\u003e\n print(fobj.read())\nValueError: I/O operation on closed file.\n```\n\n\n## Reading \u0026 writing files using pathlib\n\nTo read a file entirely into a string:\n\n```python\nfrom pathlib import Path\n\nfile_content = Path(\"myfile.txt\").read_text()\nprint(file_content)\n```\n\n##\n\nTo write text into a file:\n\n```python\nfrom pathlib import Path\n\ndata = \"\"\"Name,A1,A2,A3\nStudent-A,90,80,100\nStudent-B,100,90,100\n\"\"\"\n\n# this will delete existing contents of the file\nPath(\"grades.txt\").write_text(data)\n```\n\n## Shallow copy\n\nWhen we create a new list using slicing or `list()` function on a nested list, inner lists are not copied but they are shared. Such a copy is called :sc[shallow copy].\n\n```python\nnested_list = [[3, 17, 42], [10, 20, 30]]\n\nnew_copy = nested_list[:] # shallow copy\n\nnew_copy = list(nested_list) # shallow copy\n```\n\n##\n\n```python lineno=false\nnested_list = [[3, 17, 42], \n [10, 20, 30]]\n\nnew_copy = nested_list[:]\n```\n\n::image{style=\"width: 70%; margin: 1em auto\" src=\"lecture-8.2/shallow_copy.png\"} \n\n##\n\nChanging values in a shallow copy affects the original nested list because inner lists are shared.\n\n:::hgrid\n```python\nnested_list = [[3, 17, 42], \n [10, 20, 30]]\n\nnew_copy = nested_list[:] \n# new_copy = list(nested_list)\n\nnew_copy[0][2] = 123\n\nprint(new_copy)\nprint(nested_list)\n```\n```output\n[[3, 17, 123], [10, 20, 30]]\n[[3, 17, 123], [10, 20, 30]]\n```\n:::\n\n##\n\nSimilary `dict()`, `tuple()`, `set()` functions will make a shallow copy.\n\n```python lineno=false\nstudent = {\"name\": \"Reza\", \"ID\": 2601000, \"grades\": [95, 95, 97]}\nstudent_copy = dict(student)\n```\n\n::image{style=\"width: 100%; margin: 1em auto\" src=\"lecture-8.2/shallow_copy_dict.png\"} \n\n##\n\nChanging values via the shallow copy affects the original dictionary because grades list is shared.\n\n```python\nstudent = {\"name\": \"Reza\", \"ID\": 2601000, \"grades\": [95, 95, 97]}\n\nstudent_copy = dict(student)\nstudent_copy[\"grades\"][0] = 100\n\nprint(student_copy)\nprint(student)\n```\n```output\n{'name': 'Reza', 'ID': 2601000, 'grades': [100, 95, 97]}\n{'name': 'Reza', 'ID': 2601000, 'grades': [100, 95, 97]}\n```\n\n## Deep copy :style{.ppt-f95}\n\n`deepcopy` function from `copy` module can copy a nested structure recursively (all inner lists etc. are copied as well). Such a copy is called :sc[deep copy].\n\n:::hgrid\n```python\nimport copy\n\nnested_list = [[3, 17, 42], \n [10, 20, 30]]\nnew_copy = copy.deepcopy(nested_list)\nnew_copy[0][2] = 123\n\nprint(new_copy)\nprint(nested_list)\n```\n```output\n[[3, 17, 123], [10, 20, 30]]\n[[3, 17, 42], [10, 20, 30]]\n```\n:::\n\n##\n\n```python\nimport copy\n\nstudent = {\"name\": \"Reza\", \"ID\": 2601000, \"grades\": [95, 95, 97]}\n\nstudent_copy = copy.deepcopy(student)\nstudent_copy[\"grades\"][0] = 100\n\nprint(student_copy)\nprint(student)\n```\n\n```output\n{'name': 'Reza', 'ID': 2601000, 'grades': [100, 95, 97]}\n{'name': 'Reza', 'ID': 2601000, 'grades': [95, 95, 97]}\n```\n\n::divider","title":"6.1 — Files, Shallow vs. deep copy","date":"2024-06-03","published":true},{"slug":"Lecture-6.2.md","content":"\n## Runtime Errors (Exceptions) :style{.ppt-f90}\n\nIn Python, all exceptions are objects of some exception type. \nCommon exceptions are:\n\n:::div\n| Exception Type | Meaning |\n|-------------------|-----------------------------------------------------------------------|\n| IndexError | Index is out of range in a list or tuple |\n| KeyError | Specified key does not appear in a dictionary |\n| NameError | Specified local or global name does not exist |\n| TypeError | Operation or function applied to an inappropriate type |\n| ValueError | Operation or function applied to correct type but inappropriate value |\n| ZeroDivisionError | Second operand of division or remainder operation is zero |\n:::\n\n## Exception Traceback\n\nTraceback is an error message that allows tracing an exception back to its origin\n\n:::hgrid{cols=\"1fr 2fr\"}\n```python\ndef func():\n return 5 / 0\n\ndef main():\n return func()\n\nmain()\n```\n```output\nTraceback (most recent call last):\n File \"myprogram.py\", line 7, in \u003cmodule\u003e\n main()\n File \"myprogram.py\", line 5, in main\n return func()\n File \"myprogram.py\", line 2, in func\n return 5 / 0\nZeroDivisionError: division by zero\n```\n:::\n\n## Using `try` statement to handle errors\n\n```python\ntry:\n # try-block: code that may cause runtime error\nexcept:\n # except-block: handle the error here\n```\n\n- Identify a code that can potentially produce errors\n- Put that code in `try-block`. \n- Write code in `except-block` to handle the case when error occurs\n\n## Example\n\nSuppose we want to take a number from user input:\n\n```python\nnumber = float(input('Please enter a number: '))\n```\n\n```output\nValueError: could not convert string to float: 'abcd'\n```\n\nSince we know that `float()` function throws the `ValueError`, we can handle that error using `try` statement.\n\n##\n\n```python\ntry:\n number = float(input('Please enter a number: '))\n # the following line only executes when float() function\n # worked i.e. it did not throw ValueError\n print(\"You entered: \", number) \nexcept ValueError:\n print(\"Please enter valid number!\")\n```\n\nThere are two cases when we run the code above:\n\n::::div{.flex .ml-2}\n:::div{.flexc}\n\n:div[No error occurs in try-block]{.sans}\nexcept-block is not executed.\n\n```output\nPlease enter a number: -3.1415\nYou entered: -3.1415\n```\n:::\n\n:::div{.flexc}\n\n:div[Error occurs in try-block]{.sans}\nexcept-block is executed.\n\n```output\nPlease enter a number: abcd\nPlease enter valid number!\n```\n:::\n::::\n\n## \n\n:::greenbox\nUsing try statement in a loop, ask user to input a number (float) until they enter a valid number. \nYou can use a break statement in the try-block.\n:::\n\n```output\nPlease enter a number: abcd\nInvalid number!\nPlease enter a number: -1.61\nCorrect number entered: -1.61\n```\n\n##\n\n:::solution\n```python\nwhile True: # Loop forever\n try:\n number = float(input('Please enter a number: '))\n break # Get out of the loop\n except ValueError:\n print('Invalid number!')\n\nprint(\"Correct number entered:\", number)\n```\n:::\n\n## Unhandled exceptions are thrown as usual\n\nIf an exception occurs in try-block but it is not the same type as in the except part, the exception occurs as usual i.e. except-block is not executed and program crashes.\n\n:::hgrid{.ppt-f80 margin=\"1em 0 1em -2em\"}\n```python\nstudent_grades = {\"Reza\": 90.0}\n\ntry:\n name = input('Enter name: ')\n grade = float(input(\"Enter grade to add: \"))\n student_grades[name] += grade\n print(student_grades)\nexcept ValueError:\n print('Grade should be a number!')\n```\n\n```output\nEnter name: Dev\nEnter grade to add: 5\nTraceback (most recent call last):\n File \"myprogram.py\", line 20\n student_grades[name] += grade\nKeyError: 'Dev'\n```\n:::\n\n\n## Catching multiple exceptions\n\nExcept blocks can be chained to handle multiple exceptions that may occur in try-block.\n\nDepending on the exception that occurs, only one of the except-blocks executes. Others are skipped.\n\n```python\ntry:\n # try-block: code that may cause runtime error(s)\nexcept Error1:\n # handle the Error1 here\nexcept Error2:\n # handle the Error2 here\n```\n\n## :style{.ppt-f90}\n\n```python\nstudent_grades = {\"Reza\": 90.0}\n\ntry:\n name = input('Enter name: ')\n grade = float(input(\"Enter grade to add: \"))\n student_grades[name] += grade\n print(student_grades)\nexcept ValueError:\n print('Grade should be a number!')\nexcept KeyError:\n print(name, \"was not found.\")\n```\n\n::::div{.flex style=\"width: 110%; margin-left: -2em;\"}\n:::div{.flexc}\n:span[No error]{.sans}\n```output\nEnter name: Reza\nEnter grade to add: 5\n{'Reza': 95.0}\n```\n:::\n\n:::div{.flexc}\n:span[ValueError]{.sans}\n```output\nEnter name: Reza\nEnter grade to add: 10x\nGrade should be a number!\n```\n:::\n\n:::div{.flexc}\n:span[KeyError]{.sans}\n```output\nEnter name: Dev\nEnter grade to add: 5\nDev was not found.\n```\n:::\n::::\n\n## :style{.ppt-f95}\n\nWe can have a :b[default except block] without any exception type to handle exception of any kind. \n\n::::hgrid{cols=\"3fr 1fr\" margin=\"0.5em 0 1em -2em\"}\n```python\nstudent_grades = {\"Reza\": 90.0}\n\ntry:\n name = input('Enter name: ')\n grade = float(input(\"Enter grade to add: \"))\n student_grades[name] += grade\n print(no_such_variable) # error here\nexcept ValueError:\n print('Grade should be a number!')\nexcept KeyError:\n print(name, \"was not found.\")\nexcept:\n print(\"Some error occured.\")\n```\n:::div\n```output\nEnter name: Reza\nEnter grade to add: 5\nSome error occured.\n```\n(Again, only one of the except-blocks will execute.)\n:::\n::::\n\n## \n\n:::redbox\nIn general it is :em[not a good practice] to catch all errors using a default block. \nInstead, specific errors should be handled explicitly by using an exception type.\n:::\n\n## `finally` block :style{.ppt-f95}\n\n- We can have an optional `finally` block in a `try` statement; it is always executed whether an exception occurs in `try` block or not. \n- It is useful to clean up resources (e.g. closing a file), which needs to be done even when exceptions occur.\n\n```python\ntry:\n # try-block: code that may cause runtime error(s)\nexcept Error1:\n # handle the Error1 here\nexcept Error2:\n # handle the Error2 here\nfinally:\n # this block always executes\n```\n\n## \n\nFor this example, download the files `read_matrix.py` and `matrixdata.txt` from Ed Lesson and keep it in the same folder as the program.\n\n## When to use try statement \n\n- It is a bad practice to use a try/except to \"hide\" bugs in the program!\n- try/except should be used when we know that a specific error may occurs and there is no other way to handle it\n\n## :style{.ppt-f90}\n\n::::hgrid{margin=\"0\"}\n:::div\n:span[Good practice]{.sans}\n```python\ntry:\n number = float(input(\"Enter a number: \"))\nexcept ValueError:\n print('Number is not valid!')\n```\n:::\nbecause there is no other better way to check if a string contains a valid number.\n::::\n\n::::hgrid{margin=\"0\"}\n:::div\n:span[Not a good practice]{.sans}\n```python\nstudent_grades = {\"Reza\": 90.0}\ntry:\n name = input('Enter name: ')\n student_grades[name] += 10\nexcept KeyError:\n print(name, \"was not found.\")\n```\nbecause there is another way to do this (shown on right -\u003e)\n:::\n```python place=\"center\"\nstudent_grades = {\"Reza\": 90.0}\n\nname = input('Enter name: ')\n\nif name in student_grades:\n student_grades[name] += 10\nelse:\n print(name, \"was not found.\")\n```\n::::\n\n\n## Where do Exceptions come from? \n\n`raise` statement is used to throw an exception from our code to tell Python that an unexpected case or error has occurred.\n\n```python lineno=false\nraise SomeException(\"Some message\")\n```\n\nCheck `euclidean_distance` function in `distance.py` and `distance2.py`.\n\nAn exception raised in this way must be handled using `try` statements, otherwise Python will stop execution with the error as usual.\n\n## Example\n\nHow exceptions are raised in Python modules? \nOpen the following link and search for `raise`: \nhttps://github.com/python/cpython/blob/main/Lib/random.py\n\n## Checking type of an object\n\n```python\n# isinstance(obj, class):\n# Return whether an object is an instance of a class\n\nx = 123\nprint(isinstance(x, int)) # True\nprint(isinstance(x, float)) # False\n\nx = \"apple\"\nprint(isinstance(x, str)) # True\n\nx = [1, 5, 9]\nprint(isinstance(x, list)) # True\nprint(isinstance(x, tuple)) # False\n```\n\n##\n\n```python\n# isinstance(obj, tuple_of_classes):\n# A tuple of classes, e.g. isinstance(x, (A, B, ...)), may be given.\n# Equivalent to isinstance(x, A) or isinstance(x, B) or ...\n\nx = 3.14\nprint(isinstance(x, (int, float))) # True\n\nx = [1, 5, 9]\nprint(isinstance(x, (list, tuple))) # True\n\nx = (11, 51, 4)\nprint(isinstance(x, (list, tuple))) # True\n```\n\n##\n\nObject Oriented Programming (OOP)\n\n## Objects\n\n- An object consists of data and a set of methods can be provided to work with it. \n- For example, a string is a collection of characters and methods like :code[isupper] or :code[split] can be called :i[on it].\n- Python is an object-oriented language. This means that it uses objects to represent data and provides methods related to them.\n\n## Object-oriented programming (OOP)\n\n- Up to now, we have been using functions to organize our code, and built-in types :code[(list, str, list, or dict)] to organize our data.\n- OOP is a way to use programmer-defined data classes to organize code and data.\n\n\n## Class and objects\n\n- A class is like a :i[blueprint/template] for creating objects. It specifies what data the objects have and what methods can operate on the data.\n- An object is an :sc[instance] of some class. The terms :i[instance] and :i[object] are used interchangeably.\n\n## Example — Student\n\nWe want to define a class that would be a good template for objects representing students.\n\n::::hgrid{margin=\"0\"}\n:::div\nUseful data: \n- Name\n- Student ID\n- Current courses \n- Past grades\n:::\n:::div\nUseful methods: \n- compute_GPA \n- add_course\n- drop_course\n:::\n::::\n\nEach instance of the class (i.e., each object) would represent one particular student.\n\n## Defining a class\n\n```python\n# This class does not contain any useful code yet\nclass MyClass:\n \"\"\" a new data type \"\"\"\n pass\n```\n\n- Class names should follow the UpperCamelCase convention. \n- In a Python file, we can define as many classes as we want. \n\n## Instantiating a class\n\n```python\nclass Student:\n \"\"\" Represents a student \"\"\"\n pass\n\n# We can now create an object using the constructor Student()\nstudent1 = Student() \nprint(student1) # \u003c__main__.Student object at 0x7fa7806c9310\u003e\n\nstudent2 = Student() \nprint(student2) # \u003c__main__.Student object at 0x7fa7806c9290\u003e\n```\n\nThe variables `student1` and `student2`refer to two different objects of class `Student`. \n\n## Attributes\n\n- We can create a variable that belongs to a specific object. These variables are called :sc[attributes]. \n- We create an attribute by assigning it a value using the dot notation: `object.attribute = value`\n- Attributes can be accessed only through the object they belong to, using dot notation: `object.attribute`\n\n##\n\n```python\nclass Student:\n \"\"\" Represents a student \"\"\"\n pass\n\nstudent1 = Student()\n# Create an attribute inside the student1 object\nstudent1.name = \"Reza\"\n\n# Use the attribute inside student1 object:\nprint(student1.name) # Reza\n\nstudent2 = Student()\nprint(student2.name)\n# AttributeError: 'Student' object has no attribute 'name'\n\nprint(name) # NameError: name 'name' is not defined\n```\n\n## Visualization\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"800\" height=\"400\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=class%20Student%3A%0A%20%20%20%22%22%22%20Represents%20a%20student%0A%20%20%20%22%22%22%0A%0Astudent1%20%3D%20Student%28%29%0Astudent1.name%20%3D%20%22Deven%22%0Aname%20%3D%20student1.name%0Aprint%28name%29%0A\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=nevernest\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n## Try it!\n\n- Define a class `Student`.\n- Write a function that takes as arguments a string `name` and an integer `id_num` and returns a `Student` object with two attributes `name` and `id_num`.\n- Write a function that takes as arguments two Student objects and returns the `name` of the student with the larger `id_num`.\n- Test the above functions by creating two objects of Student class.\n\nCode available in the file `student_example1.py`.\n\n##\n\n:::redbox\nSo far, we saw how to create attributes in an object, from outside a class. That is not how we usually create attributes. It was done for demonstration purposes to understand what attributes are.\n:::\n\n## Constructor and `__init__` method\n\n- A constructor in an expression of form `MyClass(arg1, arg2, ...)` which creates an object of class `MyClass`. For example, `Student()` or `Student(\"Reza\", 1234)`\n- In Python, we define a special method named `__init__` (known as initializer method). It is invoked automatically whenever a new object is created using a contructor.\n\n```python\nclass MyClass:\n def __init__(self):\n # do something when the object is being created\n```\n\n##\n\nLet's write an `__init__` method for the `Student` class that takes no arguments (besides `self`) and prints out \"Creating a new student\".\n\n```python\nclass Student:\n \"\"\" Represents a student \"\"\"\n \n def __init__(self):\n print(\"Creating a new student\")\n\n\n# constructor without arguments:\nstudent1 = Student() # __init__ will be called\n```\n```output\nCreating a new student\n```\n\n## Constructor with arguments\n\nThe constructor can have arguments which are typically used to create the attributes and set their initial values.\n\nNow, let's modify the `__init__` method to add more arguments:\n- `name` (string) of the student and their `id_num` (int)\n- Create attributes `name` and `id_num` using `self` and set their values to the respective arguments.\n\nCode available in the file `student_example2.py`.\n\nWhat happens in example above if we do not create attributes in `__init__` ?\n\n::divider","title":"6.2 — Handling exceptions, Object Oriented Programming (OOP)","date":"2024-06-05","published":true},{"slug":"Lecture-7.1.md","content":"\n## Review from last lecture\n\n- Define a class `Student`.\n- Write a function that takes as arguments a string `name` and an integer `id_num` and returns a `Student` object with two attributes `name` and `id_num`.\n- Write a function that takes as arguments two Student objects and returns the `name` of the student with the larger `id_num`.\n- Test the above functions by creating two objects of Student class.\n\nCode available in the file `student_example1.py`.\n\n## Visualization\n\n:::div{.ppt-scale-1_25}\n\u003ciframe width=\"900\" height=\"550\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=class%20Student%3A%0A%20%20%20%20%22%22%22Represents%20a%20student.%22%22%22%0A%0Adef%20create_student%28name,%20id_num%29%3A%0A%20%20%20%20new_student%20%3D%20Student%28%29%20%20%0A%20%20%20%20new_student.name%20%3D%20name%0A%20%20%20%20new_student.id_num%20%3D%20id_num%0A%20%20%20%20return%20new_student%0A%0As1%20%3D%20create_student%28%22ABC%22,%20123%29%0Aprint%28s1.name,%20s1.id_num%29%0A%0As2%20%3D%20create_student%28%22XYZ%22,%20999%29%0Aprint%28s2.name,%20s2.id_num%29\u0026codeDivHeight=400\u0026codeDivWidth=350\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=nevernest\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n\n## Constructor and `__init__` method\n\n- A constructor in an expression of form `MyClass(arg1, arg2, ...)` which creates an object of class `MyClass`. For example, `Student()` or `Student(\"Reza\", 1234)`\n- In Python, we define a special method named `__init__` (known as initializer method). It is invoked automatically whenever a new object is created using a contructor.\n\n```python\nclass MyClass:\n def __init__(self):\n # do something when the object is being created\n```\n\n##\n\nLet's write an `__init__` method for the `Student` class that takes no arguments (besides `self`) and prints out \"Creating a new student\".\n\n```python\nclass Student:\n \"\"\" Represents a student \"\"\"\n \n def __init__(self):\n print(\"Creating a new student\")\n\n\n# constructor without arguments:\nstudent1 = Student() # __init__ will be called\n```\n```output\nCreating a new student\n```\n\n## Constructor with arguments\n\nThe constructor can have arguments which are typically used to create the attributes and set their initial values.\n\nNow, let's modify the `__init__` method to add more arguments:\n- `name` (string) of the student and their `id_num` (int)\n- Create attributes `name` and `id_num` using `self` and set their values to the respective arguments.\n\nCode available in the file `student_example2.py`.\n\nWhat happens in example above if we do not create attributes in `__init__` ?\n\n\n## Defining Methods\n\nWe can define methods inside a class using `def` keyword.\n\n:sc[Instance methods] – methods that are associated or bound to instances of a class. \n\n- These methods are called on an instance (object) and they can access attributes specific to that instance.\n\n##\n\n```python\nclass MyClass:\n def my_method(self, argument1, argument2, ..., argumentN):\n # do something \n```\n\nThe first argument of every instance method is always refers to the object on which we are calling the method.\n```python\nobj = MyClass() # Create an instance\n\n# call my_method on obj\nobj.my_method(argument1, argument2, ..., argumentN) \n```\n\nBy convention, the first argument is always named `self`. (`self` is not a keyword! If we use any other name instead of `self`, it would not be an error.)\n\n## Example continued\n\nLet's go back to the Student class:\n\n- Add a method `display_info()` that displays the information of a student i.e. prints the attributes of the instance.\n\nCode available in the file `student_example3.py`.\n\n## \n\n:::div{.ppt-scale-1_25}\n:span[Understanding `self`]{.sans .ppt-f70}\n\u003ciframe width=\"930\" height=\"500\" scrolling=\"no\" style=\"overflow: hidden;\" frameborder=\"0\" src=\"https://pythontutor.com/iframe-embed.html#code=class%20Student%3A%0A%20%20%20%20def%20__init__%28self,%20student_name,%20id_num%29%3A%0A%20%20%20%20%20%20%20%20self.name%20%3D%20student_name%0A%20%20%20%20%20%20%20%20self.id_num%20%3D%20id_num%0A%20%20%20%20%0A%20%20%20%20def%20display_info%28self%29%3A%0A%20%20%20%20%20%20%20%20print%28%22Name%20of%20student%3A%22,%20self.name%29%0A%20%20%20%20%20%20%20%20print%28%22Student%20ID%3A%22,%20self.id_num%29%0A%0As1%20%3D%20Student%28%22Reza%22,%2026000%29%0As1.display_info%28%29%0As2%20%3D%20Student%28%22Jane%22,%2026001%29%0As2.display_info%28%29\u0026codeDivHeight=400\u0026codeDivWidth=450\u0026cumulative=false\u0026curInstr=0\u0026heapPrimitives=nevernest\u0026origin=opt-frontend.js\u0026py=3\u0026rawInputLstJSON=%5B%5D\u0026textReferences=false\"\u003e \u003c/iframe\u003e\n:::\n\n\n\n## Defining functions/methods with keyword arguments\n\nA keywords argument has a default value in function or method definition.\n\n```python lineno=false margin=\"1em 0\"\ndef func(pos1, pos2, ..., name1=value1, name2=value2, ...):\n```\n\nHere, `pos1`, `pos2`, etc are positional arguments and \n`name1`, `name2`, etc are keyword arguments with default values `value1`, `value2`, respectively.\n\nKeyword arguments cannot appear before positional arguments.\n\n##\n\nIf the function is called without passing a keyword argument, that argument gets its default value.\n\n```python\ndef greet(name, greeting=\"Hello\", num_of_times=1): \n for i in range(num_of_times):\n print(greeting, name)\n\n# try the following one at a time:\n# greet(\"Dev\")\n# greet(\"Dev\", greeting=\"Hi\")\n# greet(\"Dev\", num_of_times=3)\n# greet(\"Dev\", greeting=\"Hi\", num_of_times=3)\n# greet(\"Dev\", num_of_times=3, greeting=\"Hi\")\n```\n\n\n## OOP continued :style{.ppt-f90}\n\n```python\nclass Student:\n \"\"\" Represents a student. \"\"\"\n \n def __init__(self, student_name, id_num):\n self.name = student_name\n self.id_num = id_num\n \n def display_info(self):\n print(\"Name of student:\", self.name)\n print(\"Student ID:\", self.id_num)\n\n\nnew_student = Student(\"Bob\", 260000000)\nnew_student.display_info()\n# Name of student: Bob\n# Student ID: 260000000\n```\n\n##\n\nLet's add a new attribute to store courses and a new method which allows adding a course.\n\n```python\ns1 = Student(\"Robin\", 26005)\ns1.add_course(\"COMP 208\")\ns1.add_course(\"POLI 220\", pass_fail=True)\ns1.add_course(\"MATH 250\")\ns1.display_info()\n```\n\n```output\nName of student: Robin\nStudent ID: 26005\nCourses: COMP 208, POLI 220 (pass/fail), MATH 250\n```\n\n##\n\n- Add an attribute `courses`, initializing it to empty dictionary. This dictionary will store a course name as a key and a boolean value to indicate whether the course is registered as pass/fail.\n- Update `display_info` method to also display a comma-separate list of course names. If there are no courses in the `courses` dictionary, do not display any line for it.\n- Add a method `add_course` that takes as a course name (`str`) and a keyword argument `pass_fail` (default value: `False`) and adds them to the attribute `courses`.\n\nCode available in the file `student_methods.py`.\n\n## Displaying objects: `__str__` method\n\nWhen we display `student1` we see what class the object belongs to, and the identity of the object.\n\n```python\ns1 = Student(\"Dev\", 26001)\nprint(s1)\n```\n```output\n\u003c__main__.Student object at 0x7f8cd66aa890\u003e\n```\n\nWouldn't it be nice to display `name`, `id_num` and other attributes when we do `print(student1)`?\n\n\n## `__str__` method \n\n- We can change the string representation of our class objects by implementing a method called `__str__` in our class.\n\n ```python\n def __str__(self):\n # must return a string\n ```\n\n- If we do that, then when we call `print(obj)` or `str(obj)` with an instance `obj` of our class, `__str__` method is called automatically and the returned string is used.\n\n## Try it!\nIn the `Student` class, add a `__str__` method that returns a string in the following format:\n```\nName: \u003cname attribute\u003e\nStudent ID: \u003cid_num attribute\u003e\nCourses: \u003ccomma-separated courses\u003e\n```\n\nThen, try to use print with an object of Student class.\n\nCode available in the file `student_str.py`.\n\n## Example — List of Student objects\n\n```python\nstudents = [Student(\"Dev\", 260001),\n Student(\"Reza\", 260005)]\n\n# Create a student object and append it to the list\nstudents.append(Student(\"Alice\", 260011))\n\nprint(students[2]) # uses __str__ of Student class\n# Name: Alice\n# Student ID: 260011\n# Courses: None registered.\n```\n\n##\n\n```python\n# Continued from previous slide:\n\nprint(students) # Does not use __str__ of Student class\n# [\u003c__main__.Student object at 0x10ad16100\u003e,\n# \u003c__main__.Student object at 0x10ad169d0\u003e,\n# \u003c__main__.Student object at 0x10ad16a00\u003e]\n\n\nfor s in students:\n print(s) # uses __str__ of Student class\n```\n\n::divider\n\n","title":"7.1 — Object Oriented Programming (OOP)","date":"2024-06-09","published":true},{"slug":"Lecture-7.2.md","content":"\n## Polynomial Example — Procedural vs OOP\n\nSee `polynomial` python files on Ed Lesson.\n\n## More on special methods\n\nhttps://docs.python.org/3/reference/datamodel.html#special-method-names\n\n\"A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names.\"\n\nSee the files `point.py` and `point_tester.py`. \n\n## `zip` function\n\n`zip(x, y)` function creates an iterable of tuples $(x_i, y_i)$ where $x_i$ is element from `x` and $y_i$ is element from `y`.\n\n```python\nx_values = [0.5, -2, 5, 10]\ny_values = [-1.5, 3, -3.5, 20]\n\npoints = list(zip(x_values, y_values))\nprint(points)\n# [(0.5, -1.5), (-2, 3), (5, -3.5), (10, 20)]\n```\n\n##\n\n:::div{.flex}\n```python\nx = [1, 2.5, 5]\ny = [2, 4, 10.5]\n\ntotal = 0\nfor i in range(len(x)):\n total += x[i] * y[i]\n \nprint(total)\n```\n```python\nx = [1, 2.5, 5]\ny = [2, 4, 10.5]\n\ntotal = 0\nfor x, y in zip(x, y):\n total += x * y\n \nprint(total)\n```\n:::\n\n\nWhat happens when one of the argument lists of `zip` is shorter than the other?\n\n## Truth Value Testing\n\nAny object can be tested for truth value, e.g. when used in an if or while condition.\n\n```python\nx = [1, 2, 3]\n\nif x:\n print(\"do something\")\nelse:\n print(\"do other thing\")\n```\n\n##\n\nBy default, an object is considered true \n- unless its class defines either a special `__bool__()` method that returns False or a `__len__()` method that returns zero, when called with the object. \n\nHere are most of the built-in objects considered false:\n\n- Constants defined to be false: `None` and `False`\n- Zeros: `0`, `0.0`\n- empty sequences and collections: `\"\"`, `tuple()`, `[]`, `{}`, `set()`, `range(0)`\n\n##\n\nIncorrect:\n\n```python\nans = input('Are you sure? ')\n\nif ans == 'y' or 'yes':\n print('Installing...')\n```\n\nCorrect:\n\n```python\nans = input('Are you sure? ')\n\nif ans == 'y' or ans == 'yes':\n print('Installing...')\n```\n\n\n\n## Matplotlib\n\n- Matplotlib is an extensive Python library commonly used to generate different types of plots.\n- To install Matplotlib: if you use Thonny, go to Tools -\u003e Manage packages. Type `matplotlib` on the\nsearch bar and click \"Search on PyPI\". Then click Install.\n- If you do not have Thonny, you can do so by typing the following commands in the terminal:\n ```\n python -m pip install -U pip \n python -m pip install -U matplotlib \n ```\n\n## matplotlib.pyplot\n\n- `matplotlib.pyplot` is a module in the package Matplotlib. \n- This is the module we'll be using to create plots.\n- To use it, we first need to import it\n ```\n import matplotlib.pyplot as plt\n ```\n- For more details: https://matplotlib.org/devdocs/api/pyplot_summary.html\n\n## Example – A Line plot\n\nWe can use the function plot to create a line plot between the points in the input sequence.\n\n:::hgrid\n```python\nimport matplotlib.pyplot as plt\n\nsome_numbers = [3, 1, 5, 2, 9, 3] \nplt.plot(some_numbers)\nplt.show() # display figure\n```\n\n::img{src=\"week10/line1.png\" style=\"width: 100%;\"}\n:::\n\n##\n\nIn the previous example:\n- We provided only one input to the function `plot`.\n- If we do that, then the input values are going to be considered as the y-coordinates. Their corresponding x-coordinates are the indices of the list.\n- In the example, we plot the following points: $(0, 3), (1, 1), (2, 5), (3, 2), (4, 9), (5, 3)$\n\n\n## Example - two inputs\n\n:::div{.flex}\n```python\nimport matplotlib.pyplot as plt\n\nx_coord = range(0, 10, 2)\ny_coord = [0, 0, 9, 8, 2]\nplt.plot(x_coord, y_coord)\nplt.show()\n```\n\n::img{src=\"week10/line2.png\" style=\"width: 50%; margin-left: 2em;\"}\n\n:::\n\n## Example - a linear function\nUsing pyplot, we can plot the graph of the linear function $y = x + 5$.\n\n:::div{.flex}\n```python\nimport matplotlib.pyplot as plt\n\nx_coord = range(15)\ny_coord = [x + 5 for x in x_coord]\n\nplt.plot(x_coord, y_coord)\nplt.show()\n```\n\n::img{src=\"week10/linear.png\" style=\"width: 50%; margin-left: 2em;\"}\n\n:::\n\n## Plot title and axis labels\n\n`plt.title(label)`: takes as argument a string and adds the title label to the figure.\n\n`plt.xlabel(label)`: takes as argument a string and sets the label for the x-axis. \n\n`plt.ylabel(label)`: takes as argument a string and sets the label for the y-axis.\n\nWe can choose the font size of the labels using the keyword argument: `plt.title(\"First plot\", fontsize=22)`\n\n##\n\n:::div{.flex}\n```python\nimport matplotlib.pyplot as plt\n\nx_coord = range(15)\ny_coord = [x + 5 for x in x_coord]\n \nplt.plot(x_coord, y_coord)\n\nplt.title(\"First plot\", fontsize=20)\nplt.xlabel(\"x\", fontsize=14)\nplt.ylabel(\"y = x + 5\", fontsize=14)\n\nplt.show()\n```\n\n::img{src=\"week10/linear2.png\" style=\"width: 50%; margin-left: 1em;\"}\n:::\n\n## Colors, markers and line styles\n\n- We can chose the style/color of the plots, the style/size of the markers, etc. Here is just a taste:\n\n:::hgrid{cols=\"1fr 1fr 1fr\" margin=\"0.5em -2em\" gap=\"0.5em\"}\n::img{src=\"week10/colors.png\" style=\"width: 100%;\" }\n::img{src=\"week10/linestyles.png\" style=\"width: 100%; \"}\n::img{src=\"week10/markers.png\" style=\"width: 100%;\"}\n:::\n\n:span[More info: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html]{.ppt-f80}\n\n\n## Using color, marker and line style\n\n- The function plot can take as arguments one or two lists (for x and y coordinates) and a format string. \n- The format string consists of three parts: `[marker][line][color]`. Each part is optional.\n\n:::div{.flex}\n```python\nimport matplotlib.pyplot as plt\n\nsome_numbers = [3, 1, 5, 2, 9, 3]\n\n# circle marker, dashed line, green\nplt.plot(some_numbers, \"o--g\") \nplt.show() # display figure\n```\n\n::img{src=\"week10/colorplot.png\" style=\"width: 40%; margin-left: 2em;\"}\n\n:::\n\n## \n\n:b[Multiple plots in same figure]{.sans}\n\n:::div{.flex}\n```python\nimport matplotlib.pyplot as plt\nfrom math import sin, cos, radians\n\nx_coord = range(0, 540, 20)\ny_sin = [sin(radians(x)) for x in x_coord]\ny_cos = [cos(radians(x)) for x in x_coord]\n \n# + marker, blue color and use label for legend\nplt.plot(x_coord, y_sin, '+b', label=\"sin(x)\")\n\n# magenta color and use label for legend\nplt.plot(x_coord, y_cos, 'm', label=\"cos(x)\")\nplt.legend() # show legend\nplt.show()\n```\n:::\n\n##\n\n::img{src=\"week10/twoplots.png\" style=\"width: 110%;\"}\n\n## Saving a figure\n\n- `plt.savefig(filename)`: Save the figure in a file (.jpg, .png, etc.)\n\n```python\nimport matplotlib.pyplot as plt\nfrom math import sin, radians\n\nx_coord = range(0, 540, 20)\ny_sin = [sin(radians(x)) for x in x_coord]\n\nplt.plot(x_coord, y_sin, 'm')\n\n# the figure won't be displayed, but saved in y_sin.png \nplt.savefig(\"myplot.png\")\n```\n\n## Bar Plots\n\nWhen working with data that can be broken down into categories, it might be useful for us to use a bar plot instead. \n\n```python\nimport matplotlib.pyplot as plt\n\nmtl_pop = [1293992, 1080545, 1015420, 1016376, 1620693, 1704694]\nyears = ['1966', '1976', '1986', '1996', '2006', '2016']\n\nplt.bar(years, mtl_pop)\n\nplt.title(\"Population of Montreal\")\nplt.show()\n```\n\n##\n\n::img{src=\"week10/barplot.png\" style=\"width: 110%;\"}\n\n\n::divider","title":"7.2 — More OOP, Plotting using Matplotlib","date":"2024-06-11","published":true},{"slug":"Lecture-7.3.md","content":"\n\n## What is NumPy?\n- The core library for scientific computing\n- Provides a new data structure called an \"array\"\n - It is like a list, but more efficient\n - Provides many functions that work with NumPy arrays\n- https://numpy.org\n\n## Installing NumPy\n\n- If you use Thonny, go to Tools -\u003e Manage packages. Type `numpy` in the\nsearch bar and click \"Search on PyPI\". Then click Install.\n- If you do not have Thonny, you can do so by typing the following commands in the terminal:\n ```\n python -m pip install -U pip \n python -m pip install -U numpy \n ```\n\n## What is a NumPy array?\n- A NumPy array is a multidimensional collection/grid of items of same type .\n - Multidimensional: it could be a linear array (like a list e.g. vector) or a 2D array (like a list of lists e.g. matrix), 3D array etc.\n- Unlike a regular Python list,\n - All the values in a NumPy array must have the same type.\n - A NumPy array's size cannot be changed after creation.\n - All such operations that change the size (e.g. adding/removing items) result in a copy of the array.\n\n## Creating an array\n\n```python\nimport numpy as np\n\n# Create a NumPy array from a Python list\narr = np.array([1, 2, 3])\nprint(arr) \n# [1 2 3] \n# note the lack of commas in the output\n\nprint(type(arr)) # \u003cclass 'numpy.ndarray'\u003e\n\n# convert a tuple object to numpy array\nx = np.array((1, 10, 100))\nprint(x) # [ 1 10 100]\n```\n\n##\n\nWe can specify the data type of elements when creating an array.\n\n```python\nimport numpy as np\n\n# these floats will be converted to ints (by truncation)\nx = np.array([1.2, 3.14, 10.65], dtype=int)\nprint(x) # [ 1 3 10]\nprint(x.dtype) # int64\n\n# We can also specify a NumPy-defined data type\nx = np.array([10, 20, 30], dtype=np.float64)\nprint(x) # [10. 20. 30.]\nprint(x.dtype) # float64\n```\n\n## Shape and dimensions of a NumPy array\n\n- The number of dimensions is how many levels of nested arrays there are. e.g. 1D array, 2D array, etc. It can be obtained by accessing the `ndim` attribute.\n- `shape` attribute of a NumPy array is a tuple containing size/length in each dimension.\n\n```python\nimport numpy as np\n\nx = np.array([10, 20, 30])\n\nprint(x.ndim) # 1\nprint(x.shape) # (3,)\n```\n\n##\n\n```python\nimport numpy as np\n\nx = np.array([[10, 20, 30], [40, 50, 60]])\nprint(x)\n# [[10 20 30]\n# [40 50 60]]\n\nprint(x.ndim) # 2\nprint(x.shape) # (2, 3) \u003c-- row, col\n```\n\n## Indexing a NumPy array \n\n```python\nimport numpy as np\n\narr = np.array([2, 4, 8])\nprint(arr[0], arr[1], arr[2]) # 2 4 8\n\n# we can modify existing elements.\narr[0] = 1\nprint(arr) # [1 4 8]\n```\n\n## Other ways to create arrays\n\n```python\nimport numpy as np\n\n# create an array of 0's, with shape (2,).\nx = np.zeros(2)\nprint(x)\n# [0. 0.] \n# dots above mean float values\n\n# create an array of all 1's, with shape (3,),\n# of integer type.\ny = np.ones(3, dtype=int)\nprint(y) # [1 1 1]\n```\n\n##\n\n```python\n# create an array of shape (5,) filled with one value\nx = np.full(5, 7)\nprint(x)\n# [7 7 7 7 7]\n\n# create an array of 4 random values in the interval [0.0, 1.0).\ny = np.random.random(4)\nprint(y)\n[0.70260439 0.68529032 0.59847495 0.88655089]\n```\n\n## Some useful numpy functions\n\n```python\nimport numpy as np\n\n# Similar to the built-in range() function, \n# we can use arguments start, stop and step.\nx = np.arange(10)\nprint(x)\n# [0 1 2 3 4 5 6 7 8 9]\n\n# Unlike range(), float numbers are allowed.\nx = np.arange(10.0, 20.0, 2.5)\nprint(x)\n# [10. 12.5 15. 17.5]\n```\n\n##\n\nWhen we want to create a list of evenly-spaced numbers, it is better to use `np.linspace()` than `np.arange()`.\n\n```python\nimport numpy as np\n\n# Create an array of 5 evenly spaced numbers in interval [0, 1]\nx = np.linspace(0, 1, 5)\nprint(x)\n# [0. 0.25 0.5 0.75 1. ]\n\n# 7 evenly spaced numbers in interval [10, 100]\nx = np.linspace(10, 100, 7)\nprint(x)\n# [ 10. 25. 40. 55. 70. 85. 100.]\n```\n\n## Broadcasting operations\n\n- An arithmetic operation between an array and a scalar (number) is applied to all elements of the array. It is known as :sc[broadcasting].\n- It does not modify the given array; instead a new copy is created.\n\n```python\nimport numpy as np\narr = np.linspace(-1.0, 5.0, 7)\nprint(arr) # [-1. 0. 1. 2. 3. 4. 5.]\n\n# Multiplication is broadcasted to each element.\nprint(arr * 6) # [-6. 0. 6. 12. 18. 24. 30.]\n```\n\n##\n\n```python\n# Unary minus\nprint(-arr)\n# [ 1. -0. -1. -2. -3. -4. -5.]\n\n# Other operators work in same way\nprint(arr / 5)\n# [-0.2 0. 0.2 0.4 0.6 0.8 1. ]\n\nprint(arr + 4)\n# [3. 4. 5. 6. 7. 8. 9.]\n\nprint((arr + 3) * 2)\n# [ 4. 6. 8. 10. 12. 14. 16.]\n```\n\n##\n\nUnlike Python lists, NumPy arrays implement operators such as `*` to perform arithmetic operations.\n\n```python\nimport numpy as np\n\nx = [1, 2, 3] # Python list\ny = x * 5\nprint(y)\n# [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]\n\nx = np.array([1, 2, 3])\ny = x * 5\nprint(y)\n# [ 5 10 15]\n```\n\n##\n\nNumPy also defines math functions that broadcast to all elements in the array.\n\n```python\nimport numpy as np\n\narr = np.linspace(-1.0, 5.0, 7)\n\nprint(np.sin(arr))\n# [-0.841 0. 0.841 0.909 0.141 -0.757 -0.959]\n\nprint(np.exp(arr)) # e^x function\n# [ 0.368 1. 2.718 7.389 20.086 54.598 148.413]\n```\n\n## Vector operations\n\nAn arithmetic operation between arrays is done element-wise. It is also called a :sc[vectorized] operation. \n\n```python\nimport numpy as np\n\na = np.array([34.0, -12.0, 5.0])\nb = np.array([68.0, 5.0, 20.0])\nprint(a + b) # [102. -7. 25.]\nprint(a / b) # [ 0.5 -2.4 0.25]\n```\n\n## Example\n\nWrite code to evaluate the expression below using NumPy: \n$$\ns = \\sum_{k=0}^{100} \\sqrt{\\frac{k \\pi}{100}} sin \\frac{k \\pi}{100}\n$$\n\n##\n\n:::solution\n```python\nimport numpy as np\n\nk = np.arange(0, 101)\nx = k * (np.pi / 100)\ns = np.sum(np.sqrt(x) * np.sin(x))\nprint(s) # 77.51389798916512\n```\n:::\n\n## Comparing the performance of numpy vs pure python solution\n\n:::hgrid\n```python\nimport numpy as np\nimport time\n\nstart = time.time()\n\nN = 10000000\nk = np.arange(0, N+1)\nx = k * (np.pi / N)\ns = np.sum(np.sqrt(x) * np.sin(x))\nprint(s) \n\nprint(\"Time:\", time.time() - start)\n```\n\n```python\nimport math\nimport time\n\nstart = time.time()\n\nN = 10000000\ns = 0\nfor k in range(0, N+1):\n x = k * (math.pi / N)\n s += math.sqrt(x) * math.sin(x)\nprint(s) \n\nprint(\"Time:\", time.time() - start)\n```\n:::\n\n## Slicing NumPy arrays\nSimilar to lists, we can slice a 1D NumPy array.\n```python\nimport numpy as np\n\ny = np.array([0.0, 1.3, 5.0 , 10.9, 18.9, 28.7, 40.0])\nprint(y[1:4]) # print from index 1 until but not including index 4\n# [ 1.3 5. 10.9]\n\nprint(y[::-1]) # reversed copy\n# [40. 28.7 18.9 10.9 5. 1.3 0. ]\n```\n\n\n## Matrix in form of a 2D NumPy array\n\n```python\nimport numpy as np\n\n# convert a list of lists into 2D NumPy array\nm = np.array([[1, 2, 3], [4, 5, 6]])\nprint(m)\n# [[1 2 3]\n# [4 5 6]]\n\n# create an array of all 0's of shape (2, 2).\n# By default, dtype is float64.\nprint(np.zeros((2, 2))) # (2, 2) is a tuple.\n# [[0. 0.]\n# [0. 0.]]\n```\n\n##\n\n```python\nimport numpy as np\n# create an array full of 7's, of shape (2, 3).\nprint(np.full((2, 3), 7))\n# [[7 7 7]\n# [7 7 7]]\n\n# create an array of random values in interval [0, 1).\nprint(np.random.random((2, 2)))\n# [[0.1782372 0.35920979]\n# [0.9368368 0.9005017 ]]\n\n# create an identity matrix of shape (3, 3).\nprint(np.eye(3))\n# [[1. 0. 0.]\n# [0. 1. 0.]\n# [0. 0. 1.]]\n```\n\n## Reshaping arrays\n```python\nimport numpy as np\n\n# We can also create a matrix from a 1D array, using np.reshape()\nc = np.arange(6)\nprint(c) # [0 1 2 3 4 5]\n\n# change the shape to (2, 3).\nd = np.reshape(c, (2, 3))\nprint(d)\n# [[0 1 2]\n# [3 4 5]]\n```\n\n## Indexing a 2D array\nWe can index into a multi-dimensional NumPy array by providing a comma-separated list of the indices.\n\n```python\nimport numpy as np\n\nm = np.array([[1, 2, 3], [4, 5, 6]])\nprint(m.shape) # (2, 3)\n\nprint(m[0, 0]) # 1\nprint(m[0, 1]) # 2\nprint(m[1, 0]) # 4\nprint(m[1, 2]) # 6\n```\n\n## Matrix (2D array) operations\n\n```python\nimport numpy as np\n\n# Broadcasting\nb = np.array([[1, 4, 5], [9, 7, 4]])\nprint(2 + b)\n# [[ 3 6 7]\n# [11 9 6]]\n\nprint(np.sin(b))\n# [[ 0.841 -0.757 -0.959]\n# [ 0.412 0.657 -0.757]]\n```\n\n##\n\n```python\nimport numpy as np\n\n# Element-wise product of matrices\nb = np.array([[1, 4, 5], [9, 7, 4]])\nc = np.array([[0, 1, 2], [3, 4, 5]])\n\n# Note: both matrices must have the same shape\nprint(b * c)\n# [[ 0 4 10]\n# [27 28 20]]\n```\n\n##\n\nTo perform matrix multiplication, we use the dot() function.\n\n```python\nimport numpy as np\n\nb = np.array([[1, 4, 5], [9, 7, 4]])\nd = np.array([[ 4, 2], [ 9, 8], [-3, 6]])\n\n# shapes must be (M, N) dot (N, P) --\u003e (M, P)\nprint(np.dot(b, d))\n# [[25 64]\n# [87 98]]\n```\n\n## Slicing a 2D array\n\n::img{src=\"week10/slicing.png\" style=\"width: 85%; margin: 0.5em auto 0 auto\"}\n:span[(source: https://github.com/ContinuumIO/tutorials/blob/master/NumPy.pdf)]{.ppt-f80}\n\n##\n\n```python\nimport numpy as np\n\na = np.array([[ 1, 2, 3, 4, 5, 6],\n [11, 12, 13, 14, 15, 16],\n [31, 32, 33, 34, 35, 36],\n [41, 42, 43, 44, 45, 46],\n [51, 52, 53, 54, 55, 56],\n [61, 62, 63, 64, 65, 66]])\n\nprint(a[:, 1])\n# [ 2 12 32 42 52 62]\n\nprint(a[::2, ::3])\n# [[ 1 4]\n# [31 34]\n# [51 54]]\n```\n\n##\n\n```python\nimport numpy as np\n\na = np.array([[ 1, 2, 3, 4, 5, 6],\n [11, 12, 13, 14, 15, 16],\n [31, 32, 33, 34, 35, 36],\n [41, 42, 43, 44, 45, 46],\n [51, 52, 53, 54, 55, 56],\n [61, 62, 63, 64, 65, 66]])\n\nprint(a[1, 2:5])\n# [13 14 15]\n\nprint(a[3:5, 4:6])\n# [[45 46]\n# [55 56]]\n```\n\n\n## `for` loop and tricky cases\n\nDo not add/remove items to/from a list while iterating over the same list in a `for`-loop.\nThe same applies to sets and dictionaries.\n\n```python\nnums = [10, -20, -50, -30, 100]\n\nfor i in range(len(nums)):\n print(i, nums)\n if nums[i] \u003c 0:\n nums.remove(nums[i])\n \nprint(nums)\n```\n\n##\n\nWill this version of `for` loop work?\n\n```python\nnums = [10, -20, -50, -30, 100]\n\nfor n in nums:\n if n \u003c 0:\n nums.remove(n)\n \nprint(nums)\n```\n\n##\n\nOne possible solution:\n\n```python\nnums = [10, -20, -50, -30, 100]\n\ni = 0\nwhile i \u003c len(nums):\n if nums[i] \u003c 0:\n nums.remove(nums[i])\n else:\n i += 1\n \nprint(nums)\n```\n\nOther solution is to simply create a new list to include positive numbers only while not modifying the original list at all.\n\n##\n\nWhat will happen when following code is executed?\n\n```python\nnums = [10, 20]\n\nfor n in nums:\n nums.append(n*n)\n \nprint(nums)\n```\n\n## Functions are first-class citizens\n\nFunctions can be used in same way as any other Python objects:\n\n- Functions can be passed function as arguments\n- Functions can be created inside other functions\n- A function can be returned from other function.\n\n## Useful examples of passing functions as arguments\n\n```python\npoints = [(1, 20, 3), (4, 10, 9), (7, 4, 11)]\n\n\ndef key_func(p):\n return p[1], p[0], p[2]\n\n\nprint(max(points, key=key_func))\n\nprint(sorted(points, key=key_func))\n```\n\n## lambda\n\nAn anonymous inline function consisting of a single expression which is evaluated when the function is called. \n\nThe syntax to create a lambda function is \n```python\nf = lambda param1, param2, ...: some_expression\n```\n\nEquivalent to\n\n```python\ndef f(param1, param2, ...):\n return some_expression\n```\n\n##\n\n```python\ncounts = {'apple': 1,\n 'orange': 5,\n 'banana': 2}\n\nfruit_max_count = max(counts, key=lambda k: counts[k])\nprint(fruit_max_count)\n\nfruit_max_count = max(counts, key=counts.get)\nprint(fruit_max_count)\n```\n\n##\n\n```python\nwords = [(\"happy\", 0.7), (\"sad\", -0.9),\n (\"fun\", 0.58), (\"enemy\", -0.4)]\n\nwords_sorted = sorted(words, key=lambda tup: tup[1])\nprint(words_sorted)\n```\n\n## Built-in `map` function\n\n`map(func, iterable)` applies a function `func` to every item of an iterable, yielding the results.\n\nExamples:\n```python\nx = [\"23\", \"3.14\", \"1.61\"]\ny = list(map(float, x))\nprint(y)\n```\n\n##\n```python\ninventory = {\"sofa\": 10, \"chair\": 5, \"lamp\": 3}\nnew_inventory = dict(map(lambda item: (item[0].upper(), item[1]+10),\n inventory.items()))\nprint(new_inventory)\n```\n\n```python\npersons = [['music', 'running', 'reading'],\n ['movies', 'boardgames'],\n ['boardgames', 'running', 'hiking']]\n\nnewlist = list(map(set, persons))\nprint(newlist)\n```\n\n## Other built-in functions that work with iterables\n\n`filter(func, iterable)`: yields items from `iterable` for which `func(item)` is `True`. \n\n```python\nnums = [-2, 10, -5, 7]\npositive = list(filter(lambda x: x \u003e 0, nums))\nprint(positive) # [10, 7]\n```\n##\n\n`all(iterable)`: Return `True` if all items of the `iterable` are true (or if the iterable is empty). Equivalent to:\n```python\ndef all(iterable):\n for item in iterable:\n if not item:\n return False\n return True\n```\n\n```python\nnums = [2, 4, 6, 8]\nprint(all([x % 2 == 0 for x in nums])) # True\n\nnums = [1, 4, 6, 8]\nprint(all([x % 2 == 0 for x in nums])) # False\n```\n\n##\n\n`any(iterable)`: Return `True` if any item of the iterable is true. If the iterable is empty, return `False`. Equivalent to:\n```python\ndef any(iterable):\n for item in iterable:\n if item:\n return True\n return False\n```\n```python\nnums = [2, 3, 5, 7]\nprint(any([x % 2 == 0 for x in nums])) # True\n\nnums = [1, 3, 5, 7]\nprint(any([x % 2 == 0 for x in nums])) # False\n```\n\n## Inner/Nested functions\n\n- Functions can be defined inside other functions. \n- This is useful to create helper functions that are not used outside of a function — Encapsulates (hides) inner functions.\n\n```python\ndef main_function():\n def helper(params):\n print(\"do something\", params)\n\n helper(\"hello\")\n helper(123)\n\n\nmain_function()\nhelper()\n```\n\n## Closure\n\nAn enclosed (i.e. inner/nested) function has access to local variables and parameters of outer (enclosing) function.\n\n:::hgrid\n```python\ndef create_quadratic(a, b, c):\n def quadratic(x):\n return a * x ** 2 + b * x + c\n\n return quadratic\n\n# q1 and q2 are functions:\nq1 = create_quadratic(1, 3, 10)\nq2 = create_quadratic(-5, -1, 0)\n```\n```python\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nx = np.linspace(-20, 20, 1000)\nplt.plot(x, q1(x), \"r\")\nplt.plot(x, q2(x), \"b\")\nplt.show()\n```\n:::\n\n\n::divider\n","title":"7.3 — NumPy \u0026 Misc. topics","date":"2024-06-13","published":true}]},"__N_SSG":true},"page":"/comp202-notes","query":{},"buildId":"tDjRbwhe6GKkHs3U2EVUf","isFallback":false,"gsp":true,"scriptLoader":[]}</script></body></html>