<jupyter_start><jupyter_text>Table of ContentsWho am I?Learning OutcomesBy the end of this session, you should be able to:What is a programming paradigm?Python’s flexibility FTWWhat are the different ways to sum a list of numbers in Python?What is Imperative / Procedural Programming?What's The Difference Between Imperative and Procedural Programming?What is Recursion?What is Functional Programming?What is the difference between Imperative / Procedural is vs Functional?Student Activity: Sum-a-list Function Many WaysBenchmarking different paradigmsBenchmarking vs Big OWhat is factorial?What are different programming ways to a calculate factorial?Student Activity: Factorial Function Many WaysStudent Poll: Which method is your favorite? Why?Try not to mix-match stylesSummary of methodsTakeawaysBonus MaterialObject-oriented programming (OOP) can be considered to be paradigmThere are many other computing paradigmsZen of Python Who am I?------My name is <NAME>.I'm a professor of Data Science at [University of San Francisco's Masters in Data Science](https://www.usfca.edu/arts-sciences/graduate-programs/data-science) program.  > It is better to solve one problem five different ways, than to solve five problems one way.   > — George Polya Learning Outcomes----- By the end of this session, you should be able to:- Explain the pros and cons of common programming paradigms.- In each of the paradigms, write code to:    - Sum a list of numbers    - Calculate the factorial of integers What is a programming paradigm?------A style of programming, the way the code is organized. Patterns of thought and action.[Source](https://en.wikipedia.org/wiki/Programming_paradigm) Python’s flexibility FTW-----Flexibility is one of Python's many advantages. Many different programming paradigms can be used within Python. What are the different ways to sum a list of numbers in Python?------- Built-in function- 3rd party functions- Procedural / Imperative    - for    - while- Recursive- Functional What is Imperative / Procedural Programming?-----Imperative programming paradigm that describes computation in terms of statements that change a program state.The focus is on what explicit steps the computer should take. Imperative programming is the workhorse of programming, it gets the job done.Common in: C, C++, Java, COBOL What's The Difference Between Imperative and Procedural Programming?-----For us, there is no meaning difference.If you want to geek out about programming language design, then start [here](https://softwareengineering.stackexchange.com/questions/117092/whats-the-difference-between-imperative-procedural-and-structured-programming). What is Recursion?-----A function that calls itself and each time it moves closer to the solution.A recursive function automatically manages state.Recursive functions can be hard to write (and debug). Some people think they are elegant. What is Functional Programming?-------Everything is function with inputs and outputs.Any forms of state or mutable data are avoided.Functional programming lends itself well to parallel processing because there is no state to consider. Python supports only parts of functional programmingHaskell is a programming language that is a __pure__ functional programming implementation. What is the difference between Imperative / Procedural is vs Functional?-------Imperative / Procedural is lower-level code that lists each explicit step in order. The focus is how it is done.Functional is higher-level code that declares what should be true and delegates the implementation to functions. The focus is what is to be done. Student Activity: Sum-a-list Function Many Ways-----Write a sum a list function the following ways:- Built-in function- 3rd party functions- Procedural / Imperative    - for    - while- Recursive- Functional<jupyter_code>reset -fs
nums = [-1, 1, 42, 1] #=> 43
# Built-in
sum(nums)
# Short for numeric Python
! pip install numpy
# 3rd party
import numpy as np 

np.sum(nums)
# Procedural with for loop
def sum_for(nums):
    total = 0
    for n in nums:
        total += n
    return total

sum_for(nums)
# Procedural with while loop
def sum_while(nums):
    nums_copy = nums[:]
    total = 0
    while len(nums_copy):
        total += nums_copy.pop()
    return total

sum_while(nums)
# Recursive
def sum_recursive(nums):
    if len(nums) == 1:
        return nums[0]
    else:
        return nums[0] + sum_recursive(nums[1:]) 

sum_recursive(nums)
# Functional
from functools import reduce
from operator import add

def sum_functional(nums):
    return reduce(add, nums)

sum_functional(nums)
# They are all semantically correct for integers
nums = [-1, 1, 42, 1]
assert (43 == 
        sum(nums) == 
        np.sum(nums) == 
        sum_for(nums) ==
        sum_while(nums) ==  
        sum_recursive(nums) ==
        sum_functional(nums)
        )<jupyter_output><empty_output><jupyter_text>Benchmarking different paradigms-------How do they stack up for speed?Take a moment to rank them. Write down your predictions.<jupyter_code>from random import random

nums = [random() for _ in range(1_000_000)]
%timeit -n 10 sum(nums)
%timeit -n 10 np.sum(nums)
# When using numpy use numpy types
nums_array = np.array(nums)
%timeit -n 10 np.sum(nums_array)

%timeit -n 10 sum_for(nums)
%timeit -n 10 sum_while(nums)
%timeit -n 10 sum_functional(nums) 
# This will result in an error because of the way Python handles recursion
# %timeit -n 10 sum_recursive(nums[:10_000])<jupyter_output><empty_output><jupyter_text>Benchmarking vs Big O-----Big 0 is short-hand for complexity, typically runtime complexity. Big 0 is about the theoretic performance of algorithms.Often times when writing code you care more about actual performance, thus benchmarking is more useful.Benchmarking will give empirical results based on running code with actual data.Computer Scientist prefer Big O. Software engineers prefer benchmarking. What is factorial?-----Products of every whole number from 1 to n.If n is 1, then 1! is         1 = 1.  If n is 2, then 2! is     2 x 1 = 2.  If n is 3, then 3! is 3 x 2 x 1 = 6.   If n is 4, then 4! is 4 x 3 x 2 x 1 = 24.     If n is 5, then 5! is 5 x 4 x 3 x 2 x 1 = 120. What are different programming ways to a calculate factorial?------ Built-in- Procedural    - for    - while- Recursive- Functional Student Activity: Factorial Function Many Ways------Write a factorial function the following ways:1. Built-in1. 3rd party functions1. Procedural    1. for    1. while1. Recursive1. Functional<jupyter_code>reset -fs
# Built-in
from math import factorial

factorial(5)
# Scipy which is short for scientific Python
! pip install scipy
# 3rd party functions

from scipy.special import factorial as scipy_factorial

scipy_factorial(5)
# Procedural with for loop
def factorial_for(n):
    answer = 1
    for n_iter in range(2, n+1): # Don't need to start at 1 because we initialized it at 1 already 
        answer *= n_iter
    return answer

factorial_for(5)
# Procedural with while loop
def factorial_while(n):
    answer = 1
    while n > 0:
        answer *= n   
        n -= 1                         
    return answer 

factorial_while(5)
# Recursive
def factorial_recurse(n):
    if n == 0:
        return 1
    else:
        return  n * factorial_recurse(n - 1)
    
factorial_recurse(5)
# Functional
from functools import reduce
from operator import mul

def factorial_functional(n):
    return reduce(mul, range(2, n+1))

factorial_functional(5)
# All implementations are all semantically correct
n = 5

assert (120 == 
        scipy_factorial(n) ==
        factorial(n) == 
        factorial_for(n) == 
        factorial_while(n) == 
        factorial_recurse(n) ==
        factorial_functional(n)
        )<jupyter_output><empty_output><jupyter_text>[Source](https://blog.newrelic.com/engineering/python-programming-styles/) Student Poll: Which method is your favorite? Why?    ----- Try not to mix-match styles------You should balance the need to use the best tool / technique for a job with the need to be consistent.It is better to optimize code for readability if it does not dramatically impact performance.So since to the same style within a function, class, or library. You should practice the fundamentals over-and-over then advanced concepts will be easier to learn in the future. Summary of methods------ Built-ins & established 3rd party packages are the best default.- Procedural / Imperative is straightforward to reason about.- Recursive can be elegant but slow.- Functional can be fast but might not be hard to write and understand.Takeaways------ You should be comfortable solving problems in multiple programming paradigms.- Pick the best paradigm for the problem and your team.   Bonus Material------ Object-oriented programming (OOP) can be considered to be paradigm------I think it more of a style - Classes are categories with nouns and verbs that you'll need many specific examples.But you can apply OOP as a paradigm to solve problems.The dangers are:- OOP is often over-applied.- OOP encourages adding extra unneeded features. There are many other computing paradigms------ Declarative - for example, SQL- Data flow - for example, TensorFlow Zen of Python------The title of this tutorial is riff on the _Zen of Python_<jupyter_code>import this<jupyter_output>The Zen of Python, by <NAME>

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
{'fraction_non_alphanumeric': 0.07253784155690977, 'fraction_numerical': 0.012187930017692156, 'mean_word_length': 3.781484962406015, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 22, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '133155', 'license': 'Apache-2.0', 'n_tokens_mistral': 2941, 'n_tokens_neox': 2710, 'n_words': 1313, 'path': 'problem_solving_in_mulitple_paradigms.ipynb', 'repo_name': 'brianspiering/problem_solving_in_multiple_programming_paradigms', 'size': '10186'}