Anonüümne funktsioon (lambda)

Tavalise funktsiooni deklareerimine algab funktsiooni nime määramisest.

def name_of_the_func(params):
    # Some code here
    return [expression]

Sama funktsioon võib olla kirja pandud ilma nimeta, kasutades nn anonüümset funktsiooni, mille võtmesõnaks on lambda. Anonüümne funktsioon on funktsioon, millel on alati mingi tagastatav väärtus ja puudub nimi.

lambda params: [expression]

Eeldame, et meil on vaja funktsiooni, mis arvutab ette antud arvu x jaoks antud valemi arvulise väärtuse: x ** 2 + 15 * sqrt(x).

from math import *

def solve_some_math(x):
    return x ** 2 + 15 * sqrt(x)

print(solve_some_math(9))  # --> 126.0

# Anonymous function equivalent to the function above would be
func = lambda x: x ** 2 + 15 * sqrt(x)

print(func(9))  # --> 126.0

Või näiteks tahame koostada funktsiooni, mis võtab ette mingi sõna ja tagastab sama sõna, kuid esimene täht on asendatud viimase tähega, ja viimane esimesega. Sõna peab seetõttu koosnema vähemalt kahest tähest.

Meie lambda näeks sel juhul välja selline:

lambda word: [ perform charswap ] if [ condition ] else [ return something else ]

# If given word length is greater than one --> if len(word) > 1
# Perform char swap --> word[len(word) - 1] (last char) + word[1:len(word) - 1] (word body) + word[0] (first char)
# Else return initial word

func = lambda word: word[len(word) - 1] + word[1:len(word) - 1] + word[0] if len(word) > 1 else word

print(func("word"))  # --> dorw
print(func("Lambda"))  # --> aambdL
print(func(""))  # --> ""
print(func("a"))  # --> a

# The trivial representation of this algorithm would be
def char_swap(word):

    if len(word) > 1:

        return word[len(word) - 1] + word[1:len(word) - 1] + word[0]

    return word

Aga anonüümse funktsiooni kasutamine sellistes näidetes väga mõttekas ei ole, kuna antud juhtudel ei ole vahet, kas kasutada tavalist või lambda funktsiooni (mõlemad võtavad sama palju ruumi ja mõlemad on sama lihtsad).

Ikkagi mõnedel juhtudel lambda funktsioonide kasutamine teeb koodi kirjutamist lihtsamaks ja kiiremaks. Tavaliselt seda kasutatakse koos sisseehitatud meetoditega map(), filter() ja reduce(). Neid nimetatakse funktsionaalse programmeerimise elementideks.

filter()

Meetod filter() on kõige lihtsam. Antud meetod võtab ette kaks argumenti - filtreerimise kriteerium, mis on funktsioon, ja andmekogum, kust me võtame väärtused filtreerimiseks. Meetod loob uut järjendit elementidest, millele kehtib määratud kriteerium. Ilma lambda-ta näeb see välja nii

# Data to filter
input_list = [10, 27, 56, 90]

# Defining filtering criteria
def filter_criteria(number):

    # We only need numbers that are divisible by 2
    return number % 2 == 0

# Filtering out the values we need, outcome is <filter> object
filtered = filter(filter_criteria, input_list)

# Converting the <filter> object to list and printing its contents
print(list(filtered))  # --> [10, 56, 90]

Olgu meil antud järjend suvaliste numbritega. Meie tahame sellest järjendist saada ainult need numbrid, mis jaguvad 3-ga või 5-ga. Triviaalseks lahenduseks on for-each tsükkel:

def get_specific_nums(nums):

    # Declaring an empty list
    specific_nums = []

    # For each number in given list
    for number in nums:

        # Check if number is divisible by 3 OR by 5
        if number % 3 == 0 or number % 5 == 0:

            # Add to a new list
            specific_nums.append(number)

    return specific_nums

print(get_specific_nums([1, 3, 14, 27, 15, 100, 151, 9, 2]))  # --> [3, 27, 15, 100, 9]

Tundub päris mahukas. Ja nüüd teeme sama asja kasutades lambda ja filter().

# Initial list
nums = [1, 3, 14, 27, 15, 100, 151, 9, 2]

# Filtering out the values we need, outcome is <filter> object
filtered = filter(lambda x: x % 3 == 0 or x % 5 == 0, nums)

# Converting the <filter> object to list and printing its contents
print(list(filtered))  # --> [3, 27, 15, 100, 9]

Ehk seitsmest koodireast saime kolm, mis on juba tunduvalt lühem tulemus. Tegelikult need kolm rida saab veel korra kokku panna, ja saaksime vaid ühe rea koodi:

print(list(filter(lambda x: x % 3 == 0 or x % 5 == 0, nums)))  # --> [3, 27, 15, 100, 9]

Juba palju parem, eks? Lühike ja ilus lahendus meie probleemile. Veel üks näide:

words = ["lambdas", "are", "cool", "but", "such", "code", "may be", "difficult", "to read"]

# Filtering words by length
filtered = filter(lambda word: 6 > len(word) > 3, words)

print(list(filtered))  # --> ['cool', 'such', 'code']

# Or

print(list(filter(lambda word: 6 > len(word) > 3, words)))  # --> ['cool', 'such', 'code']

map()

Meetod map() teostab mingit kasutaja poolt määratud tehet igale elemendile algsest andmekogumist. Näiteks on meil järjend numbritest ja me tahame saada sellest uut järjendit, kus iga uus number võrdub algse järjendi numbri ruuduga.

# Data to change
input_list = [1, 2, 3, 4]

# Defining changing method
def change_method(number):

    # Return the same number squared
    return number ** 2

# Changing the values in input_list, outcome is <map> object
mapped = map(change_method, input_list)

# Converting the <map> object to list and printing its contents
print(list(mapped))  # --> [1, 4, 9, 16]

Ja kasutades lambda-t:

# Data to change
input_list = ["Clean", "code", "is", "good", "code"]

# Changing the values in input_list, outcome is <map> object
# Replacing word "is" to "="
mapped = map(lambda word: "=" if word == "is" else word, input_list)

# Converting the <map> object to list and joining contents to string
print(" ".join(list(mapped)))  # --> Clean code = good code

# Or ... (isn't quite clean code though)
print(" ".join(list(map(lambda word: "=" if word == "is" else word, input_list))))  # --> Clean code = good code

# Trivial solution would be
def change_element(input_list):
    result = []
    for word in input_list:
        if word == "is":
            result.append("=")
        else:
            result.append(word)
    return " ".join(result)

print(change_element(["Clean", "code", "is", "good", "code"]))

# Or using the built-in replace() method
" ".join(input_list).replace("is", "=")