Практика 7

Задания в классе

Тема занятия - Формулирование абстракций с помощью процедур высшего порядка.

На предыдущих занятиях мы строил абстракции на процедурах. То есть писали и тестировали некоторые процедуры, которые решали определенную задачу. Далее их могли бы использовать другие программисты в своих целях: определять простое ли число, искать сумму цифр числа. В итоге мы написали даже целую утилиту для шифра Виженера.

Но процедурной абстракции не достаточно для построения серьезных систем. Нужен еще один уровень - уровень абстракции процедур высшего порядка.

Рассмотрим следующие задачи:

  1. Сумма чисел от 1 до N.
  2. Сумма кубов чисел от 1 до N.
  3. Сумма термов следующего вида:

Очевидно, что за этими задачами стоит одна общая абстракция - суммирование последовательностей. В математике мы знакомы с сигма нотацией, реализующей эту абстракцию:

Данная абстракция предполагает, что у нас есть некоторая процедура суммирования результатов некоторой другой процедуры. Для этого нам нужно уметь использовать функции в Python как переменные, и это возможно!

# Абстрагируем функцию возведения в квадрат.

def plus_one(x):
    return x + 1
def power2(function, x):
    return function(x) * function(x)

# Можем возводить в квадрату сумму    
power2(plus_one, 4) == (4+1)*(4+1) == 5*5 == 25
# lambda - это однострочная функция без имени
power2(lambda x: x*2, 4) == (4*2)*(4*2) == 8*8 == 64
# Длину строки!!
power2(lambda x: len(x), 'hello') == len('hello') * len('hello') == 5 * 5 == 25

Приступим к заданиям

Абстрактная сумма последовательности

Контракт функции, который необходимо реализовать:

def summa(f, start, stop, step):
    """
    :param f: функция из 1 аргумента, которую нужно применить над каждым элементом
    :param start: начинаем сумму с числа start
    :param step: ссылка на функцию которая возвращает следующее число
    :param stop: заканчиваем суммирование на числе stop
    """
    return 0

# Пример использования - сумма остатков от деления на три каждого второго числа от 1 до 100
summa(lambda x: x % 3, 1, 100, lambda x: x + 2)

# Сумма квадратов чисел от 1 до 5
summa(lambda x: x**2, 1, 5, lambda x: x + 1) == 1**1 + 2**2 + 3**2 + 4**2

Далее напишите вычисление числа используя формулу из начала практики и абстрактуню сумму.

Абстракция накопления данных

Кроме суммы у нас существует еще и проивзедение элементов последовательностей. Абстракцией над этими частными случаями является общее понятие Накопление.

Тогда получаем следуюущую сигнатуру метода:

def accumulate(combiner, initial_value, f, start, stop, step):
    """
    :param combiner: функция из 2 аргументов, которая указывает как присоединить текущее значение к уже накопленным
    :param initial_value: значение с которого всё начинается. Для суммы это 0, а для произведения - 1.
    :param f: функция из 1 аргумента, которую нужно применить над каждым элементом
    """
    return 0

# сумма квадратов чисел от 0 до 10
accumulate(lambda acc, cur: acc + cur, 0, lambda x: x**2, 1, 10, lambda i: i + 1)

Используя эту абстракцию напишите возведение в квадрат четных чисел от 2 до 10.

Домашнее задание

Не забывайте про тесты.

Фильтрующий аккумулятор

К нашей абстракции накопления можно добавить еще и фильтрацию. Тогда мы будем накапливать только те элементы, которые удовлетворяют некоторому условию. Добавим еще один аругмент predicate - ссылку на функцию, принимающую 1 аргумент и возвращающую True/False

def filtered_accumulate(combiner, initial_value, predicate, f, start, stop, step):
    return 0

filtered_accumulate(lambda acc, cur: acc + cur, 10, lambda x: x % 10 == 6, lambda x: x, 1, 1025, lambda i: i*2) == 10 + 16 + 256

С помощью этой абстракции выразите следующее:

  1. Сумма квадратов простых чисел в диапазоне от a до b.

  2. Произведение суммы цифр чисел последовательности : [1, 4, 9, 16, 25, 36, 49, ...] -> [1, 4, 9, 7, 7, 9, 13, ...] -> 1*4*9*7*7*9*13*....

  3. Сложная дополнительная задача на много баллов. Перепишите предыдущую функцию так, чтобы она использовала предикат, как пост-условие. То есть оно будет проверяться после применения функции f над элементом последовательности. Решите задачу: Создание строки английских букв, кроме гласных букв, из букв алфавита, полученных из суммы цифр чисел последовательности Фибоначчи до 1000(имеется ввиду не 1000 номеров фибоначчи, а N чисел фибоначчи, которые не превышают 1000). Если полученный ASCII код не входит в диапазон английских букв, то верните его в него. Предполагается, что здесь вам нужно будет написать функцию step таким образом, чтобы она возвращала вам числа фибоначчи. filtered-accumulate(combiner, "", predicate, f, 1, 1000, next_fib_number) == "bbcdfdhkrjrhy". То есть есть [1 1 2 3 5 8 13 21] -> [1 1 2 3 5 8 4 3] -> [b b c d f i e d] -> [b b c d f d] -> 'bbcdfd'. Алгоритм должен работать эффективно, то есть числа фибоначчи не должны вычисляться каждйы раз заново.

Hints

Читайте главу 1.3 из учебника “Структура и интерпретация компьютерных программ”.

Читайте конспект лекций про функциональные коллекции, чтобы понять зачем это нужно MIT 6.005 - Map Filter Reduce.