Download presentation
Presentation is loading. Please wait.
1
COSC 1306 COMPUTER SCIENCE AND PROGRAMMING
Jehan-François Pâris Fall 2016 1 1
2
THE ONLINE BOOK CHAPTER X LISTS
3
Introduction Lists are ordered sequences of elements
Separated by commas Enclosed in square brackets ("[…]") Examples [ 1, 2, 3,4 ,5] ['Alice', 'Bob', 'Carol', Dean'] ['Alice', 'freshman', [100, 90, 70]]
4
Usages Group together related objects Lists of people, courses, exams
Vectors and matrices of algebra and physics Store records Contact list entry ['Bill', ' ', xyx.com'] Anything else
5
Lists and strings Lists Ordered sequences Similar primitives
Elements can be any Python objects Including lists Mutable Strings Ordered sequences Similar primitives Can only contain characters Non mutable
6
Operations Finding the length of a list Access the elements of a list
Deciding on list membership Concatenating lists Defining slices Modify individual elements Lists are mutable
7
Accessing Elements (I)
>>> names = ['Ann', 'Bob', 'Carol', 'end'] >>> names ['Ann', 'Bob', 'Carol', 'end'] >>> print(names) >>> names[0] 'Ann' We can access individual elements
8
Accessing Elements (II)
>>> names = ['Ann', 'Bob', 'Carol', 'end'] >>> names[1] 'Bob' >>> names[-1] 'end' List indexing uses the same conventions as string indexing
9
Finding the length of a list
names = ['Ann', 'Bob', 'Carol', 'end'] >>> len(names) 4 >>>alist = ['Ann', 'Bob', 'Carol', [1,2,3]] >>> len(alist) 4 New list has still four elements, the last one is list [1, 2, 3]
10
List membership names = ['Ann', 'Bob', 'Carol', 'end']
>>> 'Ann' in names True >>> 'Lucy' in names False >>> 'Alice' not in names True Same operators as for strings
11
a designates the whole list
An example a = [10, 20, 30, 40, 50] 10 20 50 30 40 a designates the whole list a[0] a[1] a[2] a[3] a[4]
12
List concatenation (I)
>>> names = ['Alice'] + ['Bob'] >>> names ['Alice', 'Bob'] >>> names =['Carol'] >>> names = names + 'Dean' … TypeError: can only concatenate list (not "str") to list
13
List concatenation (II)
>>> mylist = ['Ann'] >>> mylist*3 ['Ann', 'Ann', 'Ann'] >>> newlist = [0]*8 >>> newlist [0, 0, 0, 0, 0, 0, 0, 0] >>> [[0]*2]*3 [[0, 0], [0, 0], [0, 0]]
14
List slices >>> names = ['Ann', 'Bob', 'Carol', 'end']
Two observations A list slice is always a list names[0:1] starts with names[0] but stops before names[1]
15
More list slices >>> names[0:2] ['Ann', 'Bob']
Includes names[0] and names[1] >>> names[0:] ['Ann', 'Bob', 'Carol', 'end'] The whole list >>> names[1:] ['Bob', 'Carol', 'end']
16
TRAP WARNING More list slices >>> names[-1:] ['end']
A list slice is a list >>> names[-1] 'end' Not the same thing! TRAP WARNING
17
Let us check names = ['Ann', 'Bob', 'Carol', 'end']
>>> names[-1] 'done' >>> names[-1:] ['done'] >>> names[-1] == names[-1:] False What's true for strings is not always true for lists
18
Mutable and immutable quantities
Python lists are mutable They can be modified in place names = ['Ann', 'Bob', 'Carol', 'end'] >>> names[-1] = 'Astrid' >>> names ['Ann', 'Bob', 'Carol', 'Astrid'] Can cause surprises!
19
Mutable and immutable quantities
By default, Python quantities are immutable Each time you modify them, you create a new value Expensive solution Works as we expect it
20
How Python implements variables
A Python variable contains the address of its current value a = 5 a 5
21
How Python implements variables
A Python variable contains the address of its current value a = 5 b = a a 5 b
22
How Python implements variables
A Python variable contains the address of its current value a = 5 b = a a = a +1 a b 6 5
23
How Python implements variables
This work as we expected We saved the old value of a into be before modifying it People write a = # initial value b = a # save initial value a = a +1 # increment
24
A big surprise The old value of a was not saved!
>>> a = ['Ann', 'Bob', 'Carol'] >>> b = a >>> b ['Ann', 'Bob', 'Carol'] >>> a[0] = 'Lucy' >>> a ['Lucy', 'Bob', 'Carol'] >>> b ['Lucy', 'Bob', 'Carol'] The old value of a was not saved!
25
What happened (I) a b ['Ann', 'Bob', 'Carol']
>>> a = ['Ann', 'Bob', 'Carol'] >>> b = a >>> b ['Ann', 'Bob', 'Carol'] a ['Ann', 'Bob', 'Carol'] b
26
What happened (II) a b ['Lucy', 'Bob', 'Carol']
>>> a[0] = 'Lucy' >>> a ['Lucy', 'Bob', 'Carol'] >>> b ['Lucy', 'Bob', 'Carol'] a b ['Lucy', 'Bob', 'Carol']
27
Why this mess? Making lists immutable would have made Python much slower Lists can be very large Conflict between ease of use and efficiency This time efficiency won! Because efficiency penalty would have been very big
28
How to copy a list Copy a slice containing the whole list
>>> a = ['Ann', 'Bob', 'Carol'] >>> b = a[:] >>> b ['Ann', 'Bob', 'Carol'] >>> a[0] = 'Lucy' >>> a ['Lucy', 'Bob', 'Carol']
29
Deletions Can delete individual elements or entire slices by specifying their location names = ['Ann', 'Bob', 'Carol', 'David'] >>> del names[2] >>> names['Ann', 'Bob', 'David'] >>> del names[0:2] >>>names ['David']
30
Object and references (I)
>>> a = ['Ann'] >>> b = ['Ann'] >>> a == b True >>> a is b False a and b point to different objects
31
No sharing a b ['Ann'] ['Ann']
32
Object and references (II)
>>> a = ['Ann'] >>> b = a >>> a == b True >>> a is b True a and b now point to the same object
33
Same object is shared a b ['Ann']
34
Aliasing and cloning (I)
When we do >>> a = ['Ann'] >>> b = a we do not make a copy of a, we just assign an additional variable name to the object pointed by a b is just an alias for a
35
Aliasing and cloning (II)
To make a true copy of a, we must do >>> a = ['Ann'] >>> b = a[:] we make a true copy of a b is a clone of a
36
A weird behavior >>> pets = ['cats', 'dogs']
>>> oddlist =[pets]*2 >>> oddlist [['cats', 'dogs'], ['cats', 'dogs']] >>> pets[0] = 'snake' >>> pets ['snake', 'dogs'] >>> oddlist [['snake', 'dogs'], ['snake', 'dogs']]
37
What did happen? (I) pets 'cats' 'dogs'
38
What did happen? (II) Nothing is copied! oddlist pets 'cats' 'dogs'
39
What did happen? (III) oddlist pets 'snakes' 'dogs'
40
My take Example of an odd behavior Must be aware of it
Might encounter it In a complex not too well written program In an exam question (Not in COSC 1306) Should stay away for it If possible
41
Editing list >>> mylist = [11, 12, 13, 'done']
>>> mylist[-1] = 'finished' >>> mylist [11, 12, 13, 'finished'] >>> mylist[0:4] = ['XI', 'XII', 'XIII'] >>> mylist ['XI', 'XII', 'XIII', 'finished'] We can change the values of the existing list entries but cannot add or delete any of them
42
List methods Four groups of methods
Adding an item to a list and telling where to put it Extracting an item from a list Modifying the order of a list Locating and removing individual items
43
Adding an element to a list
Two methods append() insert() Both methods return no value
44
Append(I) >>> mylist = [11, 12, 13, 'finished']
>>> mylist.append('Not yet!') >>> mylist [11, 12, 13, 'finished', 'Not yet!']
45
Appending means adding at the end.
Append (II) >>> listoflists = [[14.5,'1306'], [17.5,'6360']] >>> listoflists.append([16.5, 'TAs']) >>> listoflists [[14.5,'1306'],[17.5,'6360'],[16.5, 'TAs']] Appending means adding at the end.
46
Insert (I) >>> mylist = [11, 12, 13,'finished']
>>> mylist.insert(0, 10) #BEFORE mylist[0] >>> mylist [10, 11, 12, 13, 'finished'] >>> mylist.insert(4, 14) #BEFORE mylist[4] [10, 11, 12, 13, 14,'finished']
47
Insert(II) mylist.insert(index, item)
index specifies entry before which the new item should be inserted mylist.insert(0, item) inserts the new item before the first list entry mylist.insert(1, item) inserts the new item before the second element and after the first one
48
a designates the whole list
Example (I) a = [10, 20, 30, 40, 50] 10 20 50 30 40 a designates the whole list a[4] a[1] a[2] a[0] a[3]
49
Example (II) a Where to insert 25 and keep the list sorted? a[4] a[1]
10 20 50 30 40 a a[4] a[1] a[2] a[0] a[3]
50
Example (III) a Where to insert 25 and keep the list sorted? a[4] a[1]
10 20 50 30 40 a a[4] a[1] a[2] a[0] a[3] 25
51
Example (IV) We do a.insert(2, 25) after a[1] and before a[2]
52
Let us check >>> a = [10, 20, 30, 40, 50]
>>> a.insert(2,25) >>> a [10, 20, 25, 30, 40, 50]
53
Example (V) Where to insert 55 and keep the list sorted? a a[4] a[1]
10 20 50 30 40 a a[4] a[1] a[2] a[0] a[3]
54
Example (VI) Where to insert 55 and keep the list sorted? a a[4] a[1]
10 20 50 30 40 a a[4] a[1] a[2] a[0] a[3] 55
55
Example (VII) We must insert After a[4] Before no other element
We act as if a[5] existed a.insert(5, 55) It works! Same as a.append(55)
56
Let us check >>> a = [10, 20, 30, 40, 50]
>>> a.insert(5, 55) >>> a [10, 20, 30, 40, 50, 55]
57
Let us make it a bit easier
len(list) returns the length of a list Same as number of its elements Equal to index of last element plus one We could have written a.insert(len(a), 55) Same as a.append(55)
58
Extracting items One by one thislist.pop(i)
removes thislist[i] from thislist returns the removed entry thislist.pop( ) removes the last entry from thislist
59
Examples (I) >>> mylist = [11, 22, 33, 44, 55, 66]
>>> mylist.pop(0) 11 >>> mylist [22, 33, 44, 55, 66]
60
Examples (II) >>> mylist.pop() 66 >>> mylist
[22, 33, 44, 55] >>> mylist.pop(2) 44 [22, 33, 55]
61
Examples (III) >>> waitlist = ['Ann', 'Cristi', 'Dean']
>>> waitlist.pop(0) 'Ann' >>> waitlist ['Cristi', 'Dean'] >>> waitlist.append('David') ['Cristi', 'Dean', 'David']
62
Reordering a list Two methods mylist.sort() sorts the list
returns no value mylist.reverse() reverse the order of the list entries
63
Sorting lists >>> mylist = [11, 12, 13, 14, 'done']
>>> mylist.sort() Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> mylist.sort() TypeError: unorderable types: str() < int() Cannot compare apples and oranges
64
Sorting lists of strings
>>> names = ['Alice', 'Carol', 'Bob'] >>> names.sort() >>> print(names) ['Alice', 'Bob', 'Carol'] names.sort() is a method applied to the list names In-place sort
65
Sorting lists of numbers
>>> newlist = [0, -1, +1, -2, +2] >>> newlist.sort() >>> print(newlist) [-2, -1, 0, 1, 2] In-place sort
66
Sorting lists with sublists
>>> schedule = [[14.5, '1306'], [17.5, '6360'], [16.5, 'TA']] >>> schedule.sort() >>> schedule [[14.5, '1306'], [16.5, 'TA'], [17.5, '6360']] >>>
67
Reversing the order of a list
>>> names = ['Alice', 'Bob', 'Carol'] >>> names.reverse() >>> names ['Carol', 'Bob', 'Alice'] namelist.reverse() is a method applied to the list namelist
68
Sorting into a new list >>> newlist = [0, -1, +1, -2, +2]
>>> sortedlist= sorted(newlist) >>> print(newlist) [0, -1, 1, -2, 2] >>> print(sortedlist) [-2, -1, 0, 1, 2] sorted(…) is a conventional Python function that returns a new list
69
Searching and retrieving
mylist.index(item) Returns the position of first occurrence of item in the list mylist.count(item) Returns the number of occurrences of item in the list mylist.remove(item) Removes the first occurrence of item from the list
70
Examples >>> names ['Ann', 'Carol', 'Bob', 'Alice', 'Ann']
>>> names.index('Carol') 1 >>> names.count('Ann') 2 >>> names.remove('Ann') ['Carol', 'Bob', 'Alice', 'Ann']
71
Sum: a very useful function
>>> list = [10, 20 ,20] >>> sum(list) 50 >>> list = ['Alice', ' and ', 'Bob'] >>> sum(list) Does not work!
72
Easy averages >>> prices = [1.899, 1.959, 2.029, 2.079]
>>> sum(prices) >>> len(prices) 4 >>> sum(prices)/len(prices) # GETS AVERAGE
73
Appending vs. Concatenating
We append a new entry to a list >>> names = ['Ann', 'Bob', 'Alice'] >>> names.append('Carol') >>> names ['Ann', 'Bob', 'Alice', 'Carol'] We have updated the list
74
Appending vs. Concatenating
We concatenate two lists >>> names = ['Ann', 'Bob', 'Alice'] >>> names + ['Carol'] ['Ann', 'Bob', 'Alice', 'Carol'] >>> names ['Ann', 'Bob', 'Alice'] # UNCHANGED >>> names = names +['Carol'] ['Ann', 'Bob', 'Alice', 'Carol'] # OK
75
Lists and for loops By item for item in alist :
Processes successively each item in the list Most natural
76
Lists and for loops By position for index in range(len(alist)) :
Goes through the indices of all the list entries Gives us access to their positions
77
Examples >>> names ['Ann', 'Bob', 'Alice', 'Carol']
>>> for item in names : print(item) Ann Bob Alice Carol
78
Usage >>> scores = [50, 80, 0, 90]
>>> def countzeroes(alist) : count = 0 for item in alist : if item == 0 : count += 1 return count >>> countzeroes(scores) 1
79
Examples >>> names ['Ann', 'Bob', 'Alice', 'Carol'] >>> for i in range(len(names)): print(names[i]) Ann Bob Alice Carol
80
Another countzeroes() function
>>> scores = [50, 80, 0, 90] >>> def countzeroes(alist) : count = 0 for index in range(len(scores)) : if scores[index] == 0 : count += 1 return count >>> countzeroes(scores) 1
81
Using lists as arguments
Can modify a list from inside a function def capitalizeList(l) : for i in range(len(l)) : l[i] = l[i].capitalize() names = ['Ann', 'bob', 'lIZ'] capitalizeList(names) print(names) ['Ann', Bob', 'Liz']
82
Notes Only works for lists that were passed to the function as an argument Must traverse the list by position for entry in l: … does not work Function returns no value Operates through a side effect
83
Functions returning a list
def capitalizeList(l) : newlist = [] for item in l : newlist.append(item.capitalize()) return newlist names= ['ann', 'Bob', 'lIZ'] print(capitalizeList(names)) ['Ann', Bob', 'Liz']
84
Pure functions Return a value Have no side effects
Like mathematical functions Implement a black box Black box Inputs Output
85
Main advantage of pure functions
Easier to understand Preferred whenever possible
86
Another function returning a list
def minSec(miltime) : """ Converts hhmm into hors and minutes""" hours = miltime//100 minutes = miltime % 100 return([hours, minutes]) [hh, ss] = minSec(1538) print(hh) print(ss)
87
One more def primes_upto(n) :
""" Return a list of prime numbers < n.""" result = [] # START WITH EMPTY LIST for i in (2, n) : # SKIP ONE if is_prime(i) : result.append(i) # ADD TO LIST return result
88
Guess the output myname = "Edgar Allan Poe" namelist = myname.split() init = "" for aname in namelist: init = init + aname[0] print(init)
89
The answer myname = "Edgar Allan Poe" namelist = myname.split() init = "" for aname in namelist: init = init + aname[0] print(init) "EAP"
90
Initializing lists Use list comprehensions
>>> [ 0 for n in range(0, 9)] [0, 0, 0, 0, 0, 0, 0, 0, 0] >>> [n for n in range (1,11)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> [2*n+1 for n in range(0, 6)] [1, 3, 5, 7, 9, 11]
91
Warning! The for n clause is essential [0 in range(0, 10)] [True]
Because 0 is in range(0, 10)
92
More comprehensions >>> [ c for c in 'Cougars'] [ 'C', 'o', 'u', 'g', 'a', 'r', 's'] >>> names = ['Ann', 'bob', 'liz'] >>> new = [n.capitalize() for n in names] >>> new ['Alice', 'Bob', 'Liz'] >>> names ['Alice', 'bob', 'liz']
93
More comprehensions >>> first5 = [n for n in range(1,6)] >>> first5 [1, 2, 3, 4, 5] >>> first5squares = [n*n for n in first5] [1, 4, 9, 16, 25]
94
An equivalence [n*n for n in range(1, 6)] [1, 4, 9, 16, 25] is same as a = [ ] for n in range(1,6) : a.append(n*n)
95
Filtered comprehensions
Filtered through a condition def primes_upto(n) : """ Return a list of prime numbers < n.""" result = [x for x in range(2,n) if isprime(x)] return result
96
Filtered comprehensions
>>> a = [11, 22, 33, 44, 55] >>> b = [ n for n in a if n%2 == 0] >>> b [22, 44] >>> c = [n//11 for n in a if n > 20] >>> c [2, 3, 4, 5] A very powerful tool !
97
More filtered comprehensions
>>> s = [['Ann', 'CS'],['Bob', 'CE'],['Liz', 'CS']] >>> [x[0] for x in s if x[1] =='CS'] ['Ann', 'Liz'] >>> sum([1 for _ in s if _[1] =='CS']) 2
98
Computingπ I have sometimes asked students to compute π by throwing simulated random darts at a target Imagine a target consisting of a circle inscribed in a square
99
Computing π If r denotes the radius of the circle
The area of the circle is πr2 The area of the square is 4 r2 The circle occupies π/4 of the area of the circle If we throw n random darts at the target An average of nπ/4 will and inside the circle
100
My solution import random def computePi(niterations) : incount = 0 for i in range(niterations) : x = random.uniform(-1, 1) y = random.uniform(-1, 1) if x*x + y*y <= 1 : incount += 1 return 4*incount/niterations
101
Making things more complicated
>>> from random import uniform >>> >>> def gilligan(k): ... return 4 * sum([1 for c in [uniform(0,1)**2+uniform(0,1)**2 for _ in range(k)] if c < 1]) / float(k) ... >>> gilligan(100000) I did not write this code float(..) required by Python
102
Understanding the function (I)
My friend used two comprehensions [uniform(0,1)**2+uniform(0,1)**2 for _ in range(k)] creates a list containing k instances of uniform(0,1)**2+uniform(0,1)**2 the underscore ('_') denotes the last evaluated expression
103
Understanding the function (II)
[1 for c in […] if c < 1] creates a list containing a 1 for each value in c such that c < 1 each entry of the new list represents one dart that felt into the circle
104
Understanding the function (III)
The last step is summing the contents of the outer list sum([1 for c in […] if c < 1]) returns total number of darts each entry of the new list represents one dart that felt into the circle
105
Programming styles My friend's programming style favors conciseness over clarity I prefer clarity Code should be so dumb that anyone could understand it without any effort Our definitions of clarity shift over time What appears obscure to us today will appear trivial once we are familiar with the language
106
Nested lists or lists of lists
>>> nested = [['Ann', 90], ['Bob', 84]] >>> innerlist = nested[1] >>> innerlist ['Bob', 84] >>> innerlist[0] 'Bob' >>> nested[1][0]
107
Strings and lists (I) >>> s = 'No pain, no gain'
>>> lst1 = s.split() >>> lst1 ['No', 'pain,', 'no', 'gain'] >>> lst2 = s.split(',') >>> lst3 = s.split('ai') >>> lst3 ['No p', 'n no g', 'n']
108
Strings and lists (II) list function applies to strings
>>> list('Hello') ['H', 'e', 'l', 'l', 'o']
109
TUPLES LESS IMPORTANT
110
Tuples Same as lists but Immutable Enclosed in parentheses
A tuple with a single element must have a comma inside the parentheses: a = ('Liz',)
111
Examples >>> mytuple = ('Ann', 'Bob', 33)
The comma is required!
112
Why? No confusion possible between ['Ann'] and 'Ann'
('Ann') is a perfectly acceptable expression ('Ann') without the comma is the string 'Ann' ('Ann', ) with the comma is a tuple containing the string 'Ann' Sole dirty trick played on us by tuples!
113
Tuples are immutable >>> mytuple = ('Ann', 'Bob', 'Liz')
>>> saved = mytuple >>> mytuple += ('Ed',) >>> mytuple ('Ann', 'Bob', 'Liz','Ed') >>> saved ('Ann', 'Bob', 'Liz')
114
Things that do not work mytuple += 'Phil' Traceback (most recent call last):Z … TypeError: can only concatenate tuple (not "string") to tuple Can understand that!
115
sorted( ) returns a list!
Sorting tuples >>> atuple = ('Liz', 'Bob', 'Ann') >>> atuple.sort() Traceback (most recent call last): … AttributeError: 'tuple' object has no attribute 'sort' >>> atuple = sorted(atuple) >>> atuple ['Ann', 'Bob', 'Liz'] Tuples are immutable! sorted( ) returns a list!
116
Most other things work! >>> atuple = ('Ann', 'Bob', 'Liz')
>>> len(atuple) 3 >>> 44 in atuple False >>> [ i for [i for i in atuple] ['Ann', 'Bob', 'Liz']
117
The reverse does not work
>>> alist = ['Ann', 'Bob', 'Liz'] >>> (i for i in alist) <generator object <genexpr> at 0x02855DA0> Does not work!
118
Converting sequences into tuples
>>> alist = ['Ann', 'Bob', 'Liz'] >>> atuple = tuple(alist) >>> atuple ('Ann', 'Bob', 'Liz') >>> newtuple = tuple('Hello!') >>> newtuple ('H', 'e', 'l', 'l', 'o', '!')
119
The following slides will never be on any COSC 1306 quiz
Just for science fans The following slides will never be on any COSC 1306 quiz
120
Lists in geometry (I) yA xA Consider a point A in the Cartesian plane
It has two coordinates xA and yA yA A xA
121
Lists in geometry (II) We will represent each point in the Cartesian plane by a list a with two elements such that a[0] represents xA a[1] represents yA Can now speak of the origin as point [0, 0]
122
Distance between two points
The distance between two points A and B is the square root of (Ax – Bx)2 + (Ay – By)2 Pythagora's theorema
123
Why? xA yA A B yB xB
124
Function computing this distance
>>> def distance( a, b) : """ Cartesian distance between a and b ""“ import math return math.sqrt((a[0] - b[0])** (a[1] - b[1])**2)
125
Function computing this distance
>>> origin = [0, 0] >>> a = [1, 1] >>> distance(origin, a) >>>
126
Lists in physics (I) Speed, accelerations can be expressed by vectors
In Cartesian plane force F has two components Fx and Fy We will represent each force F by a list f with two elements such that f[0] represents Fx f[1] represents Fy
127
Lists in geometry (II) Fy F θ Fx
128
Absolute value of a force
>>> def vecmodule(f) """ Module of a two-dimensional vector ""“ import math return math.sqrt(f[0]**2 + f[1]**2) >>> vecmodule([0,2]) 2.0 >>> vecmodule([3,4]) 5.0
129
Adding two vectors (I) [0, 2] + [0, 3] [0, 2, 0, 3] Not what we want
+ operator concatenates lists
130
Adding two vectors (I) y Gy F F+G Fy G Gx Fx x
131
Adding two vectors (III)
>>> def vecadd(f, g) : """ adds two 2-D vectors ""“ return [f[0] + g[0], f[1] + g[1]] >>> vecadd([0, 2], [0, 3]) [0, 5] A function can return a list
132
Multiplying two vectors
Which product? Dot product returns a scalar Cross product returns a vector Applies to three-dimensional spaces
133
Dot product def dotproduct (f, g): “”” dot product of two two-dimensional vectors ""“ return f[0]*g[0] + f[1]*g[1] >>> dotproduct([1,0], [2, 2]) 2
134
List of lists (I) >>> a = [[1, 2], [3, 1]]
135
List of lists (II) Can be used to represent matrices ( ) a00 a01 a11
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.