0% found this document useful (0 votes)
9 views

PROGRAMMING PARADIGMS

PROGRAMMING PARADIGMS

Uploaded by

Bhuvaneswari
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

PROGRAMMING PARADIGMS

PROGRAMMING PARADIGMS

Uploaded by

Bhuvaneswari
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 66

UNIT 4

Pythonic Programming Paradigm


Functional Programming Paradigm: Concepts; Pure Function and Built-in Higher-
Order Functions; Logic Programming Paradigm: Structures, Logic, and Control;
Parallel Programming Paradigm: Shared and Distributed memory; Multi-Processing
– Ipython; Network Programming Paradigm: Socket; Socket Types; Creation and
Configuration of Sockets in TCP / UDP – Client / Server Model.
Functional Programming
• Functional programming treats computation as the evaluation of
mathematical functions.
• It emphasizes immutability, pure functions, higher-order functions,
and declarative programming.
• Functional programming avoids mutable state and emphasizes data
transformations.
• Languages like Haskell, Lisp, and Scala support functional
programming.
• Functional programming is another declarative programming
paradigm that treats computation as the evaluation of mathematical
functions.
Functional Programming (Cont..)
• The functional paradigm is popular because it offers several
advantages over other programming paradigms.
• Functional code is:
• High level: You’re describing the result you want rather than
explicitly specifying the steps required to get there. Single statements
tend to be concise but pack a lot of punch.
• Transparent: The behavior of a pure function depends only on its
inputs and outputs, without intermediary values. That eliminates the
possibility of side effects, which facilitates debugging.
• Parallelizable: Routines that don’t cause side effects can more
easily run in parallel with one another.
How Python Support Functional Programming?
• To support functional programming, it’s useful if a function in a given
programming language has two abilities:
• To take another function as an argument
• To return another function to its caller
• Everything in a Python program is an object.
• All objects in Python have more or less equal stature, and functions
are no exception.
• In Python, functions are first-class citizens, means functions have the
same characteristics as values like strings and numbers.
• Anything you would expect to be able to do with a string or number
you can do with a function as well.
Concepts of Functional Programming
Pure Functions: These functions have two main properties.
• First, they always produce the same output for the same arguments
irrespective of anything else.
• Secondly, they have no side-effects i.e. they do modify any argument
or global variables or output something.
Recursion: There are no “for” or “while” loop in functional languages.
Iteration in functional languages is implemented through recursion.
Concepts of Functional Programming(Cont..)
Functions are First-Class and can be Higher-Order: First-class functions
are treated as first-class variable.

• The first-class variables can be passed to functions as a parameter,


can be returned from functions or stored in data structures.

Variables are Immutable: In functional programming, it is not possible


to modify a variable after it’s been initialized.

• Possible to create new variables – but can’t modify existing variables.


Pure Functions
Pure functions have two properties.
• First, It always produces the same output for the same arguments. For
example, 3+7 will always be 10 no matter what.
• It does not change or modifies the input variable.
• The second property is also known as immutability.
• The only result of the Pure Function is the value it returns.
• They are deterministic.
• Pure functions also make it easier to write parallel/concurrent
applications.
Pure Functions (Cont..)
• A function is called pure function if it always returns the same result
for same argument values and it has no side effects like modifying an
argument (or global variable) or outputting something.
• The only result of calling a pure function is the return value.
• Examples of pure functions are strlen(), pow(), sqrt() etc.
• Examples of impure functions are printf(), rand(), time(), etc.
• If a function is known as pure to compiler then Loop
optimization and subexpression elimination can be applied to it.
• In GCC, the “pure” attribute is used to mark the function as pure
funciton.
Python program to demonstrate pure functions
# A pure function that does Not
# changes the input list and
# returns the new List
• def pure_func(List):
• New_List = []
• for i in List:
• New_List.append(i**2)
Program (Cont..)
return New_List
# Driver's code
• Original_List = [1, 2, 3, 4]
• Modified_List = pure_func(Original_List)

• print("Original List:", Original_List)


• print("Modified List:", Modified_List)

Output:
Original List: [1, 2, 3, 4]
Modified List: [1, 4, 9, 16]
Recursion
• During functional programming, there is no concept of for loop
or while loop, instead recursion is used.
• Recursion is a process in which a function calls itself directly or
indirectly.
• In the recursive program, the solution to the base case is provided
and the solution to the bigger problem is expressed in terms of
smaller problems.
• A question may arise what is base case? The base case can be
considered as a condition that tells the compiler or interpreter to exit
from the function.
Python program to demonstrate recursion
# Recursive Function to find
# sum of a list
• def Sum(L, i, n, count):
• # Base case
• if n <= i:
• return count
• count += L[i]
# Going into the recursion
• count = Sum(L, i + 1, n, count)
• return count
Program(Cont..)
# Driver's code
• L = [1, 2, 3, 4, 5]
• count = 0
• n = len(L)
• print(Sum(L, 0, n, count))

Output:
15
Built-in Higher-Order Functions - Functions are First-Class and can be
Higher-Order

• First-class objects are handled uniformly throughout.

• They may be stored in data structures, passed as arguments, or used


in control structures.

• A programming language is said to support first-class functions if it


treats functions as first-class objects.
Properties of first class functions
• A function is an instance of the Object type.
• To store the function in a variable.
• To pass the function as a parameter to another function.
• To return the function from a function.
• To store them in data structures such as hash tables, lists,. etc.
Python program to demonstrate higher order functions

• def shout(text):
• return text.upper()
• def whisper(text):
• return text.lower()
• def greet(func):
• # storing the function in a variable
• greeting = func("Hi, I am created by a function passed as an
argument.")
• print(greeting)
Program (Cont..)
• greet(shout)
• greet(whisper)

Output:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, I am created by a function passed as an argument.
Built-in Higher-order functions
• To make the processing of iterable objects like lists and iterator much easier,
Python has implemented some commonly used Higher-Order Functions.
• These functions return an iterator that is space-efficient. Some of the built-
in higher-order functions are:
Map(): map() function returns a list of the results after applying the given
function to each item of a given iterable (list, tuple etc.)
• Syntax: map(fun, iter)
Parameters:
• fun: It is a function to which map passes each element of given iterable.
• iter: It is a iterable which is to be mapped.
• Return Type: Returns an iterator of map class.
Python program to demonstrate working of map.

# Return double of n
• def addition(n):
• return n + n
# We double all numbers using map()
• numbers = (1, 2, 3, 4)
• results = map(addition, numbers)
# Does not Print the value
• print(results)
Program (Cont..)
# For Printing value
• for result in results:
• print(result, end = " ")

Output:
<map object at 0x7fae3004b630>
2468
Built-in Higher-order functions - Filter
• filter(): The filter() method filters the given sequence with the help of
a function that tests each element in the sequence to be true or not.
Syntax: filter(function, sequence)
Parameters:
• function: a function that tests if each element of a sequence true or
not.
• sequence: sequence which needs to be filtered, it can be sets, lists,
tuples, or containers of any iterators.
• Return Type: returns an iterator that is already filtered.
Python program to demonstrate working of the filter.
# function that filters vowels
• def fun(variable):
• letters = ['a', 'e', 'i', 'o', 'u']

• if (variable in letters):
• return True
• else:
• return False
# sequence
• sequence = ['g', 'e', 'e', 'j', 'k', 's', 'p', 'r']
Program (Cont..)
# using filter function
• filtered = filter(fun, sequence)
• print('The filtered letters are:')
• for s in filtered:
• print(s)

Output:
The filtered letters are:
e
e
Immutability
• Immutability is a functional programming paradigm can be used for
debugging as it will throw an error where the variable is being
changed not where the value is changed.
• Python too supports some immutable data types like string, tuple,
numeric, etc.

# Python program to demonstrate


# immutable data types
# String data types
• immutable = "GeeksforGeeks"
Program (Cont..)
# changing the values will
# raise an error
• immutable[1] = 'K'

Output:
Traceback (most recent call last):
File "/home/ee8bf8d8f560b97c7ec0ef080a077879.py", line 10, in
immutable[1] = 'K'
TypeError: 'str' object does not support item assignment
Anonymous Function With lambda
• Functional programming is all about calling functions and passing
them around, so it naturally involves defining a lot of functions.
• It can be defined, using the def keyword.
• It’s convenient to be able to define an anonymous function on the fly,
without having to give it a name.
• In Python, it is achied with a lambda expression.
Lambda Function Example
• lambda <parameter_list>: <expression>

• >>> lambda s: s[::-1]


• 2<function <lambda> at 0x7fef8b452e18>
• 3
• 4>>> callable(lambda s: s[::-1])
• 5True
Logic Programming paradigm
• Logic Programming is the combination of two words, logic and
programming.
• Logic Programming is a programming paradigm in which the problems
are expressed as facts and rules by program statements but within a
system of formal logic.
• Just like other programming paradigms like object oriented,
functional, declarative, and procedural, etc., it is also a particular way
to approach programming.
Logic Programming paradigm (Cont..)
• Logic programming is a programming paradigm that sees computation
as automatic reasoning over a database of knowledge made of facts
and rules.
• It is a way of programming and is based on formal logic.
• A program in such a language is a set of sentences, in logical form,
one that expresses facts and rules about a problem domain.
• Among others, Datalog is one such major logic programming language
family.
Solving Problems using Logic Programming
• Logic Programming uses facts and rules for solving the problem.
• A goal needs to be specified for every program in logic programming.
• To understand how a problem can be solved in logic programming,
First to know about the building blocks − Facts and Rules.
Facts
• Actually, every logic program needs facts to work with so that it can
achieve the given goal.
• Facts basically are true statements about the program and data. For
example, Delhi is the capital of India.
Logic Programming (Cont..)
Rules
• Actually, rules are the constraints which allow us to make conclusions
about the problem domain.
• Rules basically written as logical clauses to express various facts.
• For example, if we are building any game then all the rules must be
defined.
• Rules are very important to solve any problem in Logic Programming.
Rules are basically logical conclusion which can express the facts.
Syntax for Rule
• Following is the syntax of rule −
A∶− B1,B2,...,Bn.
• Here, A is the head and B1, B2, ... Bn is the body.
For example − ancestor(X,Y) :- father(X,Y).
• ancestor(X,Z) :- father(X,Y), ancestor(Y,Z).
• This can be read as, for every X and Y, if X is the father of Y and Y is an
ancestor of Z, X is the ancestor of Z.
• For every X and Y, X is the ancestor of Z, if X is the father of Y and Y is
an ancestor of Z.
Packages for Logic Programming
Kanren
• It provides us a way to simplify the way we made code for business logic. It
lets us express the logic in terms of rules and facts.
The following command will help you install kanren −
• pip install kanren
SymPy
• SymPy is a Python library for symbolic mathematics.
• It aims to become a full-featured computer algebra system (CAS) while
keeping the code as simple as possible in order to be comprehensible and
easily extensible.
The following command will help you install SymPy.
• pip install sympy
Logic Programming - Checking for Prime Numbers
• from kanren import isvar, run, membero
• from kanren.core import success, fail, goaleval, condeseq, eq, var
• from sympy.ntheory.generate import prime, isprime
• import itertools as it
• def prime_check(x):
• if isvar(x):
• return condeseq([(eq,x,p)] for p in map(prime, it.count(1)))
• else:
• return success if isprime(x) else fail
Checking for Prime Numbers( Cont..)
• x = var()
• print((set(run(0,x,(membero,x,
(12,14,15,19,20,21,22,23,29,30,41,44,52,62,65,85)),
• (prime_check,x)))))
• print((run(10,x,prime_check(x))))

The output of the above code will be as follows −


{19, 23, 29, 41}
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
Parallel Programming Paradigm
• Parallel computing is a sort of computation in which various tasks or
processes are run at the same time.
• High Performance Fortran is based on shared-memory interactions
and data-parallel problem decomposition, and Go provides
mechanism for shared-memory and message-passing interaction.
• Parallel programming, in simple terms, is the process of decomposing
a problem into smaller tasks that can be executed at the same time
using multiple compute resources
Distributed shared memory
• The distributed shared memory (DSM) implements the shared
memory model in distributed systems but it doesn't have physical
shared memory.
• All the nodes share the virtual address space provided by the shared
memory model.
• A SharedMemory has a fixed size and stores byte data.
• Python types can be converted to arrays of bytes and stored in a
SharedMemory and read as arrays of bytes and converted back into
Python types.
• In Python, the multiprocessing module allows us to create and
manage multiple processes to achieve parallel execution.
Inter-process communication (IPC)
Shared Memory: Shared memory allows processes to access the same
region of memory, enabling them to share data without the need for
serialization and deserialization.
• Python’s multiprocessing module provides a useful class called Value
for sharing a single value, and Array for sharing sequences like lists
and arrays.
Message Passing: Message passing involves passing data between
processes using message queues.
• Python’s multiprocessing module provides a class called Queue that
allows processes to enqueue and dequeue messages.
Python code to implement Shared Memory
• from multiprocessing import Process, Value, Array
• def modify_shared_data(shared_list, shared_dict, shared_value):
# Access and modify the shared list
• shared_list[0] = 42
# Access and modify the shared dictionary
• shared_dict['key'] = 'value'
# Access and modify the shared value
• shared_value.value = 100
code (cont..)
• if __name__ == '__main__':
# Create shared list and dictionary
• shared_list = Array('i', [1, 2, 3])
• shared_dict = Array('i', {'key': 'initial_value'})
# Create shared value
• shared_value = Value('i', 0)
# Create a process and pass the shared data
• process = Process(target=modify_shared_data, args=(shared_list, shared_dict, shared_value))
# Start the process
• process.start()
# Wait for the process to finish
• process.join()
# Print the modified shared data
• print(shared_list[:]) # Output: [42, 2, 3]
• print(shared_dict[:]) # Output: {'key': 'value'}
• print(shared_value.value) # Output: 100
Python code to implement Message Passing
• from multiprocessing import Process, Queue
• def modify_data(queue):
# Receive the data from the queue
• data = queue.get()
# Modify the data
• data[0] = 42
• data['key'] = 'value'
# Put the modified data back into the queue
• queue.put(data)
code (cont..)
• if __name__ == '__main__':
# Create a queue
• queue = Queue()
# Create the data to be shared
• data = [1, 2, 3]
# Put the data into the queue
• queue.put(data)
# Create a process and pass the queue
• process = Process(target=modify_data, args=(queue,))
# Start the process
• process.start()
# Wait for the process to finish
• process.join()
# Get the modified data from the queue
• modified_data = queue.get()
# Print the modified data
• print(modified_data) # Output: [42, 2, 3, 'key': 'value']
MULTI PROCESSING

Multiprocessing refers to the ability of a system to support more


than one processor at the same time. Applications in a
multiprocessing system are broken to smaller routines that run
independently. The operating system allocates these threads to the
processors improving performance of the system.
Consider a computer system with a single processor. If it is assigned
several processes at the same time, it will have to interrupt each
task and switch briefly to another, to keep all of the processes
going.
This situation is just like a chef working in a kitchen alone. He has to
do several tasks like baking, stirring, kneading dough, etc.
• So the gist is that: The more tasks you must do at once, the
more difficult it gets to keep track of them all, and keeping
the timing right becomes more of a challenge.

A multiprocessing system can have:


• multiprocessor, i.e. a computer with more than one central
processor.
• multi-core processor, i.e. a single computing component with
two or more independent actual processing units (called
“cores”).
• Here, the CPU can easily executes several tasks at once, with
each task using its own processor.
• It is just like the chef in last situation being assisted by his
assistants. Now, they can divide the tasks among themselves
and chef doesn’t need to switch between his tasks.
CODE –Example 1
# importing the multiprocessing module
import multiprocessing

def print_cube(num):
print("Cube: {}".format(num * num * num))

def print_square(num):
print("Square: {}".format(num * num))

if __name__ == "__main__":
# creating processes
p1 = multiprocessing.Process(target=print_square, args=(10, ))
p2 = multiprocessing.Process(target=print_cube, args=(10, ))
# starting process 1
p1.start()
# starting process 2
p2.start()

# wait until process 1 is finished


p1.join()
# wait until process 2 is finished
p2.join()
# both processes finished
print("Done!")

O/P
Square: 100 Cube: 1000 Done!
•To import the multiprocessing module, we do:
import multiprocessing
•To create a process, we create an object of Process class. It takes following
arguments:
•target: the function to be executed by process
•args: the arguments to be passed to the target function

In above example, we created 2 processes with different target functions:


p1 = multiprocessing.Process(target=print_square, args=(10, ))
p2 = multiprocessing.Process(target=print_cube, args=(10, ))

•To start a process, we use start method of Process class.


p1.start() p2.start()
Once the processes start, the current program also keeps on executing.
•In order to stop execution of current program until a process is complete,
we use join method.p1.join() p2.join()
Example 2
import multiprocessing
import os

def worker1():
# printing process id
print("ID of process running worker1: {}".format(os.getpid()))

def worker2():
# printing process id
print("ID of process running worker2: {}".format(os.getpid()))

if __name__ == "__main__":
# printing main program process id
print("ID of main process: {}".format(os.getpid()))

# creating processes
p1 = multiprocessing.Process(target=worker1)
p2 = multiprocessing.Process(target=worker2)
# starting processes
p1.start()
p2.start()

# process IDs
print("ID of process p1: {}".format(p1.pid))
print("ID of process p2: {}".format(p2.pid))

# wait until processes are finished


p1.join()
p2.join()

# both processes finished


print("Both processes finished execution!")

# check if processes are alive


print("Process p1 is alive: {}".format(p1.is_alive()))
print("Process p2 is alive: {}".format(p2.is_alive()))
O/P

ID of main process: 28628


ID of process running worker1: 29305
ID of process running worker2: 29306
ID of process p1: 29305
ID of process p2: 29306
Both processes finished execution!
Process p1 is alive: False
Process p2 is alive: False
Network Programming Paradigm
The Network paradigm involves thinking of computing in terms of a client, who is essentially in need of some type of information,
and a server, who has lots of information and is just waiting to hand it out. Typically, a client will connect to a server and query for
certain information. The server will go off and find the information and then return it to the client.

In the context of the Internet, clients are typically run on desktop or laptop computers attached to the Internet looking for
information, whereas servers are typically run on larger computers with certain types of information available for the clients to
retrieve. The Web itself is made up of a bunch of computers that act as Web servers; they have vast amounts of HTML pages and
related data available for people to retrieve and browse. Web clients are used by those of us who connect to the Web servers and
browse through the Web pages.

Network programming uses a particular type of network communication known as sockets. A socket is a software abstraction for an
input or output medium of communication.
What is Socket?
• A socket is a software abstraction for an input or output medium of communication.
• Sockets allow communication between processes that lie on the same machine, or on different machines working in diverse
environment and even across different continents.
• A socket is the most vital and fundamental entity. Sockets are the end-point of a two-way communication link.
• An endpoint is a combination of IP address and the port number.
For Client-Server communication,
▪ Sockets are to be configured at the two ends to initiate a connection,
▪ Listen for incoming messages
▪ Send the responses at both ends
▪ Establishing a bidirectional communication.
Socket Types
Datagram Socket
• A datagram is an independent, self-contained piece of information sent over a network whose arrival, arrival time, and content
are not guaranteed. A datagram socket uses User Datagram Protocol (UDP) to facilitate the sending of datagrams (self-contained
pieces of information) in an unreliable manner. Unreliable means that information sent via datagrams isn't guaranteed to make it
to its destination.

Stream Socket:
• A stream socket, or connected socket, is a socket through which data can be transmitted continuously. A stream socket is more
akin to a live network, in which the communication link is continuously active. A stream socket is a "connected" socket through
which data is transferred continuously.
Socket in Python

sock_obj = socket.socket( socket_family, socket_type, protocol=0)

socket_family: - Defines family of protocols used as transport mechanism.


Either AF_UNIX, or
AF_INET (IP version 4 or IPv4).
socket_type: Defines the types of communication between the two end-points.
SOCK_STREAM (for connection-oriented protocols, e.g., TCP), or
SOCK_DGRAM (for connectionless protocols e.g. UDP).
protocol: We typically leave this field or set this field to zero.
Example:
#Socket client example in python
import socket
#create an AF_INET, STREAM socket (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'
Socket Creation
import socket
import sys
try:
#create an AF_INET, STREAM socket (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
sys.exit();

print 'Socket Created'


Client/server symmetry in Sockets applications
Socket in Python
To create a socket, we must use socket.socket() function available in the Python socket module, which has the general syntax as
follows:
S = socket.socket(socket_family, socket_type, protocol=0)
socket_family: This is either AF_UNIX or AF_INET. We are only going to talk about INET sockets in this tutorial, as they account for
at least 99% of the sockets in use.
socket_type: This is either SOCK_STREAM or SOCK_DGRAM.
Protocol: This is usually left out, defaulting to 0.

Client Socket Methods


Following are some client socket methods:
connect( ) : To connect to a remote socket at an address. An address format(host, port) pair is used for AF_INET address family.
Socket in Python
Server Socket Methods
bind( ): This method binds the socket to an address. The format of address depends on socket family mentioned above(AF_INET).

listen(backlog) : This method listens for the connection made to the socket. The backlog is the maximum number of queued
connections that must be listened before rejecting the connection.

accept( ) : This method is used to accept a connection. The socket must be bound to an address and listening for connections. The
return value is a pair(conn, address) where conn is a new socket object which can be used to send and receive data on that
connection, and address is the address bound to the socket on the other end of the connection.
General Socket in Python
sock_object.recv():
Use this method to receive messages at endpoints when the value of the protocol parameter is TCP.
sock_object.send():
Apply this method to send messages from endpoints in case the protocol is TCP.
sock_object.recvfrom():
Call this method to receive messages at endpoints if the protocol used is UDP.
sock_object.sendto():
Invoke this method to send messages from endpoints if the protocol parameter is UDP.
sock_object.gethostname():
This method returns hostname.
sock_object.close():
This method is used to close the socket. The remote endpoint will not receive data from this side.
Simple TCP Server
Simple TCP Client
Simple UDP Server
Simple UDP Client
import socket

HOST = "127.0.0.1" # This is the standard loopback interface address (localhost)

PORT = 65432 # Defines the port to listen on

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

s.bind((HOST, PORT))

s.listen()

conn, addr = s.accept()

with conn:

print(f"Connected by {addr}")

while True:

data = conn.recv(1024)

if not data:

break
conn.sendall(data)
import socket

HOST = "127.0.0.1"

PORT = 65432

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

s.connect((HOST, PORT))

s.sendall(b"This is example data.")

data = s.recv(1024)

print(f"Received {data!r}")
An IP address is the address of a computer on a network.
A port is a number between 1 and 65535.
A socket is one half a channel of communication between two computers over a network on a particular port. (the other half is the corresponding socket on the other
computer)

A process can open a socket to a particular IP address on a specific port.


A process can also open a socket to receive connections from anyone on a specific port.

You might also like