Logical and Function Constructions K. 1 1 Department of Mathematics 2018
Boolean Values Many languages (including) Python use Boolean values: True/False. The invocation of Boole s name always carries an identification of True with 1, False with 0. This is binary computers are good at that. Thus, computers can carry out actions based on simple decisions. In most computer languages, 0 represents False ; any other number is actually interpreted as True not just 1.
Logical Operators x=2 print(x==2) results in True. Remember that = denotes assignment: evaluate the expression on the right and put that value in the variable named on the left. The symbol == is a binary test: it asks the for the truth value of the statement, the thing on the left is the same as the thing on the right. dog = cat print(dog== cat ) produces True.
Logical Operators The test for equality is just one of many possibilities. Symbol Test == equal to!= not equal to < less than > greater than <= less than or equal to >= greater than or equal to There are also the usual conjunctions, and and or. Use lots of parentheses when you use and or or.
Bitwise Logicals and and or test whether numbers are zero or nonzero. There are other operators that look familiar that actually do Boolean logic on binary representations of integers. Symbol Test & bitwise and bitwise or ˆ bitwise xor 6 & 7 0110 0111 6 8 & 7 1000 0111 0 6 7 0110 0111 7 8 7 1000 0111 15
Beware! Be careful! 2 and 1 produces 1, but 2 & 1 produces 0. Likewise, 8 & 7 gives 0, but 8 and 7 gives... 7. Also, and and or can compare floats, but the bitwise operators only work for integers. Thus, a&b produces a non-zero if a and b have even a single common bit set to 1, but is zero if the bits of a and b are set in different positions. By contrast, a and b is True only if both a and b are non-zero. Usually we want to use and.
Arrays We can make arrays of logicals. We can then use these as numbers. x = arange(-1.,1.1,.1) print(x>0) produces [False False... False True... True] Now we could convert those logicals to floats: print(1.0*(x>0)) gives [ 0. 0.... 0. 1.... 1.] Note that we multiply by a floating point 1 to do the conversion because the float() function only works on scalars.
Tricks Clearly we can do some tricky things with these logical arrays. For example, we might want to evaluate the function f given by { x x > 0 f (x) = 0 otherwise on a uniformly spaced mesh on [-1,1]. h =.1 x = arange(-1.,1.+h,h) fx = sqrt(x*(x>0)) does that. Look ma, no for loops!
If Statements We are used to if... then constructions based on Boolean evaluations. In Python, the syntax goes like this. if scalar logical: statements more statements elif scalar logical: more statements else: default statements The leading white spaces are important lines must be aligned. The elif and else clauses are both optional.
If Example if x>=0: y = sqrt(x) else: y = 0. This could also be written as y = 0. if x>=0: y = sqrt(x)
If Example A generalized process for evaluating square roots. if iscomplex(x): sq does= not sqrt(x) check type just number elif real(x)<0: sq = sqrt(complex(x,0)) else: gets real part can do sq = sqrt(x) gives NaN if x < 0 print(sq) complex(2+2j,-1j) produces 1.41421356237 if x=2; produces 1.41421356237 if x=2.0; produces 1.41421356237j if x=-2.; produces 1.41421356237j if x=-2.+0j.
No Arrays! Note that if statements only work for scalar logicals. if
Lambda functions We might want to define a Python construction that evaluates the piecewise function f on the earlier page. We can do that many ways, but the easiest is to use a lambda definition. f = lambda x: sqrt(x*(x>0)) print(f(x)) gives [-0.... -0. 0.31622777... 1.] lambda makes an abstract function definition, which we assign to a name. The x in the definition is just a placeholder the fact that we assigned it earlier does not interfere. Note the -0. values. Recall the sign bit on the mantissa of floating point numbers: e.g. -0.5*(-0.5>0) = -0.
Lambda functions The word lambda is evidently a reference to the lambda calculus. Indeed, a lambda definition uses exactly one argument. If we wanted to pursue this, we could do some currying : g = lambda x: lambda y: x+y*y print( g(2)(3) ) yields 11. There is probably little point in currying lambda functions we can make lambda functions with multiple arguments, and Python has a powerful ability to create functions in a more familiar way.
Functions Here is a different definition for the piecewise square root function we made: def f(x): y = sqrt(x*(x>0.)) return y The leading white spaces are important lines must be aligned. Spaces (ASCII 32) and tabs (ASCII 9) each count as one white space. When the leading white spaces stop, the function is over no braces. Any line aligned with def is outside the function.
Scope def name(input_variables): MMvariable assignments MMreturn output_variables argument list output list The only variables from outside that the function can use are those in the argument list. The only variables from the function that the outside world has access to are those on return lines. What happens in the function stays in the function. I didn t tell you about globals. Not gonna.
Multiple Inputs We can make a function of two variables with no currying. def g(x,y): return x+y*y In this case we can call the function more naturally: g(2,3) yields 11.
Another Example def pnorm(x,p): x = abs(x)**p return sum(x)**(1./p) x = 2.0*ones(10) print(pnorm(x,1.5)) yields 9.28317766723. We can have as many input arguments as we like. They can be arrays. If any are arrays, the function gets copies not the addresses of the original arrays.
General Sqrt Here is a general square root function based on earlier work. def gen_sqrt(x): if iscomplex(x): sq = sqrt(x) elif real(x)<0: sq = sqrt(complex(x,0)) else: sq = sqrt(x) return sq print(sq) It returns a floating point number if x is a non-negative floating point, complex otherwise. In C, this would be overloaded.
Default Values There is no function overloading in Python. Sorry. Instead, we can specify default values for arguments and test their type and nature. def pnorm(x,p=2.0): x = abs(x)**p return sum(x)**(1./p) x = 2.0*ones(10) print(pnorm(x)) gives 6.32455532034.
Multiple Outputs Sometimes we need to send several things back to the calling program. We can return only one object at a time... but that object can be a list. def inner_outer(x,y): inner_prod = inner(x,y) outer_prod = outer(x,y) return [inner_prod,outer_prod] Then inner_outer(x,x) returns [16.0, array([[ 4., 4., 4., 4.], 16.0, [ 4., 4., 4., 4.], 16.0, [ 4., 4., 4., 4.], 16.0, [ 4., 4., 4., 4.]])] when x = 2.0*ones(4).
Passing Functions We can pass function names to other functions as arguments. Recall that the composite trapezoidal rule can be written for h = x i x i 1, i = 1,..., n, as b a f (x) dx h ( n 1 f (x i ) + i=1 ) f (a) + f (b). 2 def trapezoid(f,a,b,n): h = float(b-a)/float(n) x = arange(a+h,b,h) return h*(sum(f(x))+0.5*(f(a)+f(b))) Then trapezoid(sin,0,pi,1000) gives 1.99999835507.
More Function Passing trapezoid(lambda x: 0.3333335. x*x,0,1,1000) gives But... trapezoid(sin(x),0,1,1000) gives an error. trapezoid(x*x,0,1,1000) gives an error. The point is that sin and lambda x:x*x are functions; sin(x) and x*x are just expressions that require a variable called x to have a value assigned already.