What does `map(lambda s: s != None, some_list)` mean?

Question:

I am trying to understand the Deep Q-learning algorithm for standard Cart-pole example using this tutorial, and in def optimize_model() method, I don’t understand whether the lambda expression returns a boolean or an index:

non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch.next_state)), device=device, dtype=torch.bool) 

where batch.next_state is just a list, and s is defined only in this line.

Judging from the documentation and this example, lambda s: s is not None produces a boolean. However, when I simply type in python:

>>> lambda s: s is None

I get

<function <lambda> at 0x100997010>

If I indeed get a boolean from aforementioned lambda expression, how does the map() method handels it as a first argument?

Thanks for any help in advance.

EDIT: Thank you very much for all your comments and thorough answers! I need a little time to chew it all, but I disagree with [duplicate] mark, as I haven’t seen anywhere how boolean function have been applied to a list with map(). It is still not that clear to me.

Answers:

It’s not only about a lambda expression, it’s also about the map function.
Lambda is just a function and can return any type actually.

is_x_none_lambda = lambda x: x is None # Funtion is declared AND assigned

def is_x_none_function(x)->bool:
    return x is None

some_other_name = is_x_none_function

# In REPL if you don't invoke them:
is_x_none_function # <function is_x_none_function at 0x7f81b467ab60>
is_x_none_lambda # <function <lambda> at 0x7f81b467aac0>

a = None
b = "String, not a None..."

# The return type of both of them is hovever "bool" indeed
# Again in REPL if you invoke any of the above:
is_x_none_function(a) # True
is_x_none_lambda(a) # True

is_x_none_function(b) # False
is_x_none_lambda(b) # False

# map
test_data = [None, 5, "test str"]
# All the calls below do the same
tuple(map(is_x_none_function, test_data))
tuple(map(some_other_name, test_data)) 
tuple(map(lambda x: x is None, test_data))
tuple(map(is_x_none_lambda, test_data)) 
# Take function which I provide (which can be a function assigned to a variable OR a lambda) and "lazily" invoke it on each element of the "test_data"
# The "tuple" is needed as well, cause without it, you'd get <map object at 0x7f81b4643190>

Side notes:

  • instead of tuple it could be e.g. list to convert from map to a data structure
  • map built in docs: https://docs.python.org/3/library/functions.html#map
  • this is completely subjective, but stuff from functional programming like map, reduce and so on can most often be replaced with comprehension of some type, which is imho way more readable
    e.g.
# Instead of
tuple(map(x_is_none_lambda, test_data))
# one can do:
tuple([x_is_none_lambda(item) for item in test_data])
# and if operating on lists instead of:
list(map(x_is_none_lambda, test_data))
# this
[x_is_none_lambda(item) for item in test_data]
Answered By: Gameplay

map is a type, but to a first approximation you could assume it is a function defined as

def map(f, xs):
    for x in xs:
        yield f(x)

To a second approximation, it can take multiple iterable arguments. In this form, it "zips" iterables together using its function argument, rather than just yielding tuples.

def map(f, *args):
    for t in zip(*args):
        yield f(*t)

In your example, the function is being supplied as a lambda expression, rather than a function defined using a def statement.

non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch.next_state)), device=device, dtype=torch.bool) 

is equivalent to

def not_none(s):
    return s is not None

non_final_mask = torch.tensor(tuple(map(not_none, batch.next_state)), device=device, dtype=torch.bool) 

In either case, map yields a series of Boolean values created by applying a function to each element yielded by the iterable batch.next_state; those Booleans are used to create a tuple that’s passed to torch.tensor as its first argument.


Note that map was a function in Python 2, and it returned a list.

$ python2
[some macOS blather deleted]
Python 2.7.16 (default, Mar 25 2021, 03:11:28)
[GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.29.20) (-macos10.15-objc- on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> map(lambda s: s is not None, [1, None, 'c', None])
[True, False, True, False]

In Python 3, it was made a type so that it could return a lazy iterable instead. (The function is only applied as you iterate over the instance of map, not immediately upon calling map.) Had no map function already existed, it’s not likely Python 3 would have added it (or at least, it would only be available via the itertools module, rather than as a built-in type). map can virtually always be replaced with a generator expression. For example,

non_final_mask = torch.tensor(
   tuple(s is not None for s in batch.next_state),
   device=device,
   dtype=torch.bool) 
Answered By: chepner
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.