0Pricing
Coding Interview Prep · レッスン

関数、クロージャ、ラムダ

再利用可能なヘルパー関数を定義し、デフォルト引数を使い、面接問題のソートや関数型パターンにラムダ式を適用します。

「関数、クロージャ、ラムダ」はCoddyKit上の無料Coding Interview Prepレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCoding Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Coding Interview Prepコースには全4レッスンが含まれています。

Pythonで関数を定義する

Pythonでは関数は第一級オブジェクトです。関数を渡したり、返したり、保存したりできます。名前の付け方がよいヘルパー関数は、面接用のコードを読みやすくします。まずエッジケースに対処してください。

def is_palindrome(s: str) -> bool:
    '''Return True if s reads the same forwards and backwards.'''
    s = s.lower()
    return s == s[::-1]

print(is_palindrome('Racecar'))  # True
print(is_palindrome('hello'))    # False

# Functions as values
checks = [is_palindrome]
print(checks[0]('level'))  # True

デフォルト引数とキーワード引数

デフォルト値を使うと、呼び出し側は引数を省略できます。ただし、リストのようなミュータブルなデフォルト値は絶対に使わないでください。すべての呼び出し元で共有されてしまいます。Noneをデフォルトにして、関数内でリストを作成するのが解決策です。コードで確認します。

# WRONG: shared mutable default
def bad_append(val, lst=[]):
    lst.append(val)
    return lst

print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2]  surprise!

# CORRECT: use None sentinel
def good_append(val, lst=None):
    if lst is None:
        lst = []
    lst.append(val)
    return lst

print(good_append(1))  # [1]
print(good_append(2))  # [2]

*argsと**kwargs

*argsは追加の位置引数をタプルにまとめ、**kwargsは追加のキーワード引数をdictにまとめます。関数を呼び出すときに*を使うと、シーケンスをアンパックすることもできます。

def total(*args):
    return sum(args)

print(total(1, 2, 3))     # 6
print(total(1, 2, 3, 4))  # 10

# Unpack a list as positional args
point = [3, 7]
print(max(*point))   # 7

# **kwargs
def greet(**kwargs):
    name = kwargs.get('name', 'World')
    return f'Hello, {name}!'

print(greet(name='Alice'))  # Hello, Alice!

ラムダ式

lambdaは、lambda params: expressionのように書く小さな1行関数です。sortedやmaxで簡単なkeyとして使うのに最適です。それ以上複雑な処理には、名前付きのdefのほうが読みやすくなります。

# Lambda as sort key
pairs = [(1, 3), (2, 1), (3, 2)]
sorted_by_second = sorted(pairs, key=lambda p: p[1])
print(sorted_by_second)  # [(2,1),(3,2),(1,3)]

# Lambda with map
double = list(map(lambda n: n * 2, [1, 2, 3]))
print(double)  # [2, 4, 6]

# Named function is clearer for complex logic
def sort_key(p):
    return (p[1], -p[0])  # secondary sort
print(sorted(pairs, key=sort_key))

ネストされた関数とクロージャ

クロージャとは、それを囲む関数の変数を記憶する関数です。別の関数の内部でヘルパーを定義すると、外側の変数を使えるため、DFSに便利です。

def make_counter(start=0):
    count = [start]  # list to allow mutation
    def increment():
        count[0] += 1
        return count[0]
    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2

# Alternative: nonlocal keyword
def make_counter2():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

再帰的なDFSでのクロージャ

よく使われるパターンです。メイン関数の内部でdfsヘルパーを定義し、外側の変数に結果を集めさせます。内部関数は、そのスコープを閉じ込めたクロージャです。

def max_depth(root):
    '''Closure pattern for tree DFS.'''
    max_d = [0]  # mutable container for closure

    def dfs(node, depth):
        if node is None:
            return
        max_d[0] = max(max_d[0], depth)
        dfs(node.left, depth + 1)
        dfs(node.right, depth + 1)

    dfs(root, 1)
    return max_d[0]
# This pattern avoids a class or global state.

メモ化のためのfunctools.lru_cache

lru_cache(3.9以降では@cacheも使用可能)は、引数ごとに関数の結果を記憶し、素朴なFibonacciをO(2^n)からO(n)に変えます。引数はハッシュ可能でなければならないため、tupleを使ってください。

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(40))   # 102334155  fast!
print(fib.cache_info())  # hits, misses, size

高階関数

高階関数とは、関数を受け取るか、関数を返す関数です。sorted、map、filterはいずれも高階関数です。自分で高階関数を書くと、繰り返し現れるパターンを抽象化できます。コードで合成を示します。

def apply_twice(f, x):
    return f(f(x))

print(apply_twice(lambda n: n * 2, 3))  # 12  (3*2*2)

# Composing transformations
def compose(f, g):
    return lambda x: f(g(x))

double = lambda n: n * 2
add_one = lambda n: n + 1
double_then_add = compose(add_one, double)
print(double_then_add(5))  # 11  ((5*2)+1)

ヘルパー関数を使った再帰

多くの解法では、準備とエッジケースを処理する外側の関数と、再帰を実行する内部のヘルパーを組み合わせます。公開APIをすっきり保ち、ロジックを分離できます。コードで確認します。

def flatten(nested):
    '''Flatten an arbitrarily nested list.'''
    result = []

    def _flatten(lst):
        for item in lst:
            if isinstance(item, list):
                _flatten(item)
            else:
                result.append(item)

    _flatten(nested)
    return result

print(flatten([1, [2, [3, 4], 5], 6]))
# [1, 2, 3, 4, 5, 6]

functools.partialによる部分関数

functools.partialは一部の引数をあらかじめ設定し、新しい呼び出し可能オブジェクトを返します。引数なしの関数を要求するAPIに対して、特定のパラメーターを固定した関数を渡したい場合に便利です。

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube   = partial(power, exp=3)

print(square(5))   # 25
print(cube(3))     # 27
print(list(map(square, [1, 2, 3, 4])))  # [1, 4, 9, 16]

独自の比較関数のためのlambda

Python 3では2引数の比較関数が廃止されましたが、cmp_to_keyを使えば、難しい順序付けで再び利用できます。たとえば、連結した値が最大になるように数値を並べる場合です。コードで確認します。

from functools import cmp_to_key

def largest_number(nums):
    def compare(a, b):
        if a + b > b + a: return -1
        if a + b < b + a: return 1
        return 0
    strs = list(map(str, nums))
    strs.sort(key=cmp_to_key(compare))
    result = ''.join(strs)
    return '0' if result[0] == '0' else result

print(largest_number([3, 30, 34, 5, 9]))  # '9534330'

理解度チェック

理解度を簡単に確認しましょう。関数、クロージャ、ラムダについて学んだことを示す時間です。深呼吸して、始めてください。🚀

レッスンのまとめ

まとめ:ミュータブルなデフォルト値は絶対に使わず(Noneを使います)、クロージャを使うと内部のヘルパーから外側の変数にアクセスできるためDFSに役立ち、lru_cacheなどのfunctoolsのツールが高速な解法を支えます。

よくある質問

「関数、クロージャ、ラムダ」レッスンは無料ですか?

はい。「関数、クロージャ、ラムダ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Coding Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Coding Interview Prepコースには全4レッスンが含まれています。

「関数、クロージャ、ラムダ」で何を学びますか?

再利用可能なヘルパー関数を定義し、デフォルト引数を使い、面接問題のソートや関数型パターンにラムダ式を適用します。 ブラウザで直接実行するハンズオンコードでCoding Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Coding Interview Prepを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのCoding Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「関数、クロージャ、ラムダ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このCoding Interview Prepレッスンでコードを書いて実行できますか?

はい。すべてのCoding Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. リスト、タプル、スライス
  2. Pythonの辞書と集合
  3. 内包表記と組み込み関数
  4. 関数、クロージャ、ラムダ
← Coding Interview Prepに戻る