Beginning Python - From Novice to Professional

Beginning Python - From Novice to Professional Beginning Python - From Novice to Professional

16.01.2014 Views

CHAPTER 6 ■ ABSTRACTION 133 search recursively with new limits. You could even make this easier to use by making the limit specifications optional. You simply add the following conditional to the beginning of the function definition: def search(sequence, number, lower=0, upper=None): if upper is None: upper = len(sequence)-1 ... Now, if you don’t supply the limits, they are set to the first and last positions of the sequence. Let’s see if this works: >>> seq = [34, 67, 8, 123, 4, 100, 95] >>> seq.sort() >>> seq [4, 8, 34, 67, 95, 100, 123] >>> search(seq, 34) 2 >>> search(seq, 100) 5 But why go to all this trouble, you ask? For one thing, you could simply use the list method index, and if you wanted to implement this yourself, you could just make a loop starting at the beginning and iterating along until you found the number. Sure. Using index is just fine. But using a simple loop may be a bit inefficient. Remember I said you needed seven questions to find one number (or position) among 100? And the loop obviously needs 100 questions in the worst-case scenario. Big deal, you say. But if the list has 100,000,000,000,000,000,000,000,000,000,000,000 elements, and the same number of questions with a loop (perhaps a somewhat unrealistic size for a Python list), this sort of thing starts to matter. Binary search would then need only 117 questions. Pretty efficient, huh? ■Tip There is a standard library module called bisect, which implements binary search very efficiently. Throwing Functions Around By now, you are probably used to using functions just like other objects (strings, number, sequences, and so on) by assigning them to variables, passing them as parameters, and returning them from other functions. Some programming languages (such as Scheme or LISP) use functions in this way to accomplish almost everything. Even though you usually don’t rely that heavily on functions in Python (you usually make your own kinds of objects—more about that in the next chapter), you can. This section describes a few functions that are useful for this sort of “functional programming.” These functions are map, filter, reduce, and apply.

134 CHAPTER 6 ■ ABSTRACTION LAMBDA EXPRESSIONS In the material that follows, I sometimes use something called lambda expressions. These are small, unnamed functions that can only contain an expression, and that return its value. A lambda expression is written like this: lambda x, y, z: x + y + z The first word, lambda, is a reserved word (keyword). 1 It is followed by the parameters, a colon (:), and finally the body (an expression). Although lambdas can be useful at times, you are usually better off writing a full-fledged function, especially because the function name will then say something about what your function does. map The map function “maps” one sequence into another (of the same length) by applying a function to each of the elements. For example, you may have a list of numbers, and you want to create another list in which all the numbers are doubled: >>> numbers = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] >>> map(lambda n: 2*n, numbers) [144, 202, 216, 216, 222, 88, 64, 238, 222, 228, 216, 200, 66] You don’t have to use lambda expressions—it works just fine with named functions as well: >>> map(chr, numbers) ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'] The built-in function chr takes a number as its only parameter and returns the character corresponding to that number (the so-called ordinal number, which is really its ASCII code). The reverse of chr is ord: >>> map(ord, 'Hello, world!') [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] Because strings are just sequences of characters, you can use map directly. Note that the result is a list, not another string. See the following section for a note about map, filter, and list comprehensions. filter The filter function returns a new list in which the elements that you don’t want have been filtered out. Or, to put it another way, it returns exactly those you do want. You supply filter with a function that returns a Boolean (truth) value for a given sequence element. If the function returns true, the element is part of the returned sequence; if it returns false, the element is not included in the returned sequence. (The original sequence is not modified.) For example, you might want to retain only the even numbers from the list numbers: 1. The name “lambda” comes from the Greek letter λ, which is used in mathematics to indicate an anonymous function.

CHAPTER 6 ■ ABSTRACTION 133<br />

search recursively with new limits. You could even make this easier <strong>to</strong> use by making the limit<br />

specifications optional. You simply add the following conditional <strong>to</strong> the beginning of the<br />

function definition:<br />

def search(sequence, number, lower=0, upper=None):<br />

if upper is None: upper = len(sequence)-1<br />

...<br />

Now, if you don’t supply the limits, they are set <strong>to</strong> the first and last positions of the sequence.<br />

Let’s see if this works:<br />

>>> seq = [34, 67, 8, 123, 4, 100, 95]<br />

>>> seq.sort()<br />

>>> seq<br />

[4, 8, 34, 67, 95, 100, 123]<br />

>>> search(seq, 34)<br />

2<br />

>>> search(seq, 100)<br />

5<br />

But why go <strong>to</strong> all this trouble, you ask? For one thing, you could simply use the list method<br />

index, and if you wanted <strong>to</strong> implement this yourself, you could just make a loop starting at the<br />

beginning and iterating along until you found the number.<br />

Sure. Using index is just fine. But using a simple loop may be a bit inefficient. Remember I<br />

said you needed seven questions <strong>to</strong> find one number (or position) among 100? And the loop<br />

obviously needs 100 questions in the worst-case scenario. Big deal, you say. But if the list has<br />

100,000,000,000,000,000,000,000,000,000,000,000 elements, and the same number of questions<br />

with a loop (perhaps a somewhat unrealistic size for a <strong>Python</strong> list), this sort of thing starts <strong>to</strong><br />

matter. Binary search would then need only 117 questions. Pretty efficient, huh?<br />

■Tip There is a standard library module called bisect, which implements binary search very efficiently.<br />

Throwing Functions Around<br />

By now, you are probably used <strong>to</strong> using functions just like other objects (strings, number,<br />

sequences, and so on) by assigning them <strong>to</strong> variables, passing them as parameters, and<br />

returning them from other functions. Some programming languages (such as Scheme or LISP)<br />

use functions in this way <strong>to</strong> accomplish almost everything. Even though you usually don’t rely<br />

that heavily on functions in <strong>Python</strong> (you usually make your own kinds of objects—more about<br />

that in the next chapter), you can. This section describes a few functions that are useful for this<br />

sort of “functional programming.” These functions are map, filter, reduce, and apply.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!