Reputation: 9499
So Ive been giving the following code in a kind of sort of python class. Its really a discrete math class but he uses python to demonstrate everything. This code is supposed to demonstate a multiplexer and building a xor gate with it.
def mux41(i0,i1,i2,i3):
return lambda s1,s0:{(0,0):i0,(0,1):i1,(1,0):i2,(1,1):i3}[(s1,s0)]
def xor2(a,b):
return mux41(0,1,1,0)(a,b)
In the xor2
function I dont understand the syntax behind return mux41(0,1,1,0)(a,b)
the 1's and 0's are the input to the mux function, but what is the (a,b) doing?
Upvotes: 5
Views: 576
Reputation: 76715
That is fairly advanced code to throw at Python beginners, so don't feel bad it wasn't obvious to you. I also think it is rather trickier than it needs to be.
def mux41(i0,i1,i2,i3):
return lambda s1,s0:{(0,0):i0,(0,1):i1,(1,0):i2,(1,1):i3}[(s1,s0)]
This defines a function object that returns a value based on two inputs. The two inputs are s1
and s0
. The function object builds a dictionary that is pre-populated with the four values passed int to mux41()
, and it uses s0
and s1
to select one of those four values.
Dictionaries use keys to look up values. In this case, the keys are Python tuples: (0, 0)
, (0, 1)
, (1, 0)
, and (1,1)
. The expression (s1,s0)
is building a tuple from the arguments s0
and s1
. This tuple is used as the key to lookup a value from the dictionary.
def xor2(a,b):
return mux41(0,1,1,0)(a,b)
So, mux41()
returns a function object that does the stuff I just discussed. xor2()
calls mux41()
and gets a function object; then it immediately calls that returned function object, passing in a
and b
as arguments. Finally it returns the answer.
The function object created by mux41()
is not saved anywhere. So, every single time you call xor2()
, you are creating a function object, which is then garbage collected. When the function object runs, it builds a dictionary object, and this too is garbage collected after each single use. This is possibly the most complicated XOR function I have ever seen.
Here is a rewrite that might make this a bit clearer. Instead of using lambda
to create an un-named function object, I'll just use def
to create a named function.
def mux41(i0,i1,i2,i3):
def mux_fn(s1, s0):
d = {
(0,0):i0,
(0,1):i1,
(1,0):i2,
(1,1):i3
}
tup = (s1, s0)
return d[tup]
return mux_fn
def xor2(a,b):
mux_fn = mux41(0,1,1,0)
return mux_fn(a,b)
EDIT: Here is what I would have written if I wanted to make a table-lookup XOR in Python.
_d_xor2 = {
(0,0) : 0,
(0,1) : 1,
(1,0) : 1,
(1,1) : 0
}
def xor2(a,b):
tup = (a, b)
return _d_xor2[tup]
We build the lookup dictionary once, then use it directly from xor2()
. It's not really necessary to make an explicit temp variable in xor2()
but it might be a bit clearer. You could just do this:
def xor2(a,b):
return _d_xor2[(a, b)]
Which do you prefer?
And of course, since Python has an XOR operator built-in, you could write it like this:
def xor2(a,b):
return a ^ b
If I were writing this for real I would probably add error handling and/or make it operate on bool
values.
def xor2(a,b):
return bool(a) ^ bool(b)
EDIT: One more thing just occurred to me. In Python, the rule is "the comma makes the tuple". The parentheses around a tuple are sometimes optional. I just checked, and it works just fine to leave off the parentheses in a dictionary lookup. So you can do this:
def xor2(a,b):
return _d_xor2[a, b]
And it works fine. This is perhaps a bit too tricky? If I saw this in someone else's code, it would surprise me.
Upvotes: 5
Reputation: 32300
The (a, b)
is actually the input to the lambda
function that you return in the mux41
function.
Your mux41
function returns a lambda
function which looks like it returns a value in a dictionary based on the input to the mux41
function. You need the second input to say which value you want to return.
It is directly equivalent to:
def xor2(a,b):
f = mux41(0,1,1,0)
return f(a,b)
Upvotes: 12