Будучи программистом, вы постоянно изучаете новые термины, техники и лучшие практики. Но что, если я открою целый мир понятий, о существовании которых многие программисты даже не подозревают, — понятий, которые могут полностью изменить ваш подход к написанию и осмыслению кода? Будь вы начинающим программистом или опытным профессионалом, эти 18 малоизвестных терминов помогут вам подняться на новый уровень в освоении JS и Python.

1. Преобразователь (thunk): извлечение пользы из отложенного выполнения

Вы когда-нибудь откладывали выполнение какого-либо действия до последнего момента? Thunk делает именно это — откладывает выполнение функции до тех пор, пока она не станет абсолютно необходимой.

Представьте, что у вас есть задача, которая требует больших затрат на вычисление, но вы не уверены, что результат вам понадобится. Вместо того, чтобы вычислять его заранее, оберните его в thunk.

const thunk = () => 5 + 10;
console.log(thunk()); // Вычисления происходят только при вызове

2. Монада (monad): защитная сетка для кода

Монада — своего рода защитная сетка для функций. Они помогают выстраивать цепочки операций и обрабатывать неожиданное поведение (например, ошибки или нулевые значения), не приводя к сбою всей программы. Если вы работаете с асинхронными данными, то наверняка уже использовали их, даже не подозревая об этом!

new Promise((resolve) => resolve(5))
.then(value => console.log(value)) // Вывод: 5

Чтобы монады не казались чем-то непостижимым, представляйте их как контейнеры, которые безопасно передают данные по конвейеру.

3. Замыкание (closure): код, который помнит

Представьте, что ваше прошлое «я» смогло бы передать вашему будущему «я» воспоминания даже после того, как ваше прошлое «я» осталось далеко позади. Именно это и делает замыкание. Оно позволяет функции «помнить» переменные из контекста, в котором эта функция была создана.

def counter():
    count = 0

    def _():
        nonlocal count
        count += 1
        return count
    return _

increment = counter()
print(increment())
print(increment())
print(increment())
1
2
3

4. Мемоизация (memoization): будьте эффективны

Вам когда-нибудь задавали один и тот же вопрос снова и снова? Раздражает, правда? Ваш компьютер тоже так думает. Мемоизация решает эту проблему, сохраняя результаты, чтобы не повторять дорогостоящие вычисления.

def fib(n, memo={}):
    if n in memo:
        return memo[n]
    if n < 2:
        return n
    memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
    return memo[n]

fib(100)

#354224848179261915075

5. Продолжение (continuation): держите будущее в своих руках

Продолжение — своего рода нажатие «паузы» в коде с сохранением последующих шагов на потом. Это секретный «ингредиент» многих асинхронных систем.

function asyncTask(callback) {
    setTimeout(() => {
        console.log("Task complete");
        callback();
    }, 1000);
}
asyncTask(() => console.log("Next step"));
  • Через 1 секунду будет выполнен код внутри стрелочной функции () => { console.log("Task complete"); callback(); }.

Вы когда-нибудь сталкивались с асинхронным программированием? С коллбэком в конце? Это продолжение как взгляд в будущее, позволяющий определить, что будет дальше!

6. Идемпотентность (idempotence): делай правильно, даже если делаешь это 100 раз

В программировании некоторые действия должны иметь одинаковое поведение независимо от того, сколько раз вы их выполняете. Это и есть идемпотентность — вызываете ли вы API один раз или 100 раз, результат будет один и тот же.

GET /user/123  HTTP/1.1

Сколько бы вы ни вызывали GET /user/123, всегда будете получать в ответ одного и того же пользователя. Мир API основан на этом принципе — именно он обеспечивает предсказуемость и безопасность.

7. Куайн (quine): программа, которая воспроизводит сама себя

Куайн — умная часть кода, которая выводит свой исходный код. Это все равно что посмотреть в зеркало и увидеть… свой генетический код, который смотрит прямо на вас.

s = 's = {!r}; print(s.format(s))'; print(s.format(s))

#output => s = 's = {!r}; print(s.format(s))'; print(s.format(s))

8. Зиппер (zipper): суперэффективная навигация по структурам 

Навигация и модификация структур данных без нарушения порядка может оказаться непростой задачей. Вводим функцию zipper — и она позволяет эффективно перемещаться и изменять структуры (например, деревья), не разрушая оригинал.

class Zipper:
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def move_left(self):
        return Zipper(self.left[:-1], [self.left[-1]] + self.right)

    def move_right(self):
        return Zipper(self.left + [self.right[0]], self.right[1:])

Зипперы — навигационный компас для сложных структур данных.

9. Функтор (functor): возможность отображения

Функтор — то, через что можно отобразить функцию. Это все равно что применять преобразование к каждому элементу в контейнере, не трогая сам контейнер. Это больше, чем просто функциональное программирование — это мощная комбинация.

numbers = [1, 2, 3]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9]

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

10. TCO: бесконечная рекурсия без переполнения стека

TCO (tail call optimization — оптимизация хвостового/завершающего вызова) позволяет писать рекурсивные функции, не беспокоясь о нехватке памяти. Если функция вызывает саму себя в качестве последнего действия, TCO может повторно использовать стековый кадр текущей функции.

def factorial(n, acc=1):
    if n == 0:
        return acc
    return factorial(n - 1, acc * n)

Примечание: в примере выше показана только структура TCO, но помните, что Python не поддерживает TCO нативно.

11. Каррирование (currying): один аргумент за раз

Каррирование разбивает функцию, принимающую несколько аргументов, на ряд функций, каждая из которых имеет один аргумент. Это похоже на пошаговую реализацию рецепта.

def add(x):
    def inner(y):
        return x + y
    return inner

add_five = add(5)
print(add_five(3))  # 8

Каррирование — ключ к гибкости, если понадобится частично применить аргументы. Каррирование поддержит вас в этом.

Можете также использовать partial для каррирования функций.

from functools import partial

def adder(a, b):
    return a + b

two_adder = partial(adder, 2)
two_adder(10)

#12

12. Spectre и Meltdown: скрытые угрозы для CPU

Вы наверняка слышали о Spectre и Meltdown — печально известных уязвимостях, которые потрясли мир в 2018 году. Они воспользовались технологией оптимизации производительности современных процессоров для похищения конфиденциальных данных.

if (untrusted_input < array_size) {
    temp = array[untrusted_input * 512];
}

Эксплойты высокой производительности вернулись, чтобы преследовать нас. Спекулятивное исполнение может ускорить работу, но иногда дает больше утечек, чем нам хотелось бы!

13. Гомоморфизм (homomorphism): сохранение структуры

Гомоморфизм — модное слово для обозначения функции, которая сохраняет структуру двух алгебраических систем. Это очень важно как в математике, так и в программировании при преобразовании данных (гомоморфизм — одна из важнейших концепций функционального программирования).

numbers = [1, 2, 3, 4]
def square(x):
return x * x
squared_numbers = list(map(square, numbers))
print(squared_numbers)

# Вывод: [1, 4, 9, 16]

14. Ленивая оценка (lazy evaluation): только тогда, когда нужно

Ленивая оценка откладывает оценку выражения до тех пор, пока не понадобится значение. Это полезно для повышения производительности в программах с большими наборами данных или вычислениями.

def lazy_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

for num in lazy_range(5):
    print(num)

Генератор в данном случае вычисляет значения только по мере необходимости, экономя память и время обработки.

В чем разница между преобразованием и генераторами?

  • Ленивая оценка — стратегия откладывания оценки выражения до тех пор, пока его значение не понадобится. Она помогает оптимизировать производительность, избегая ненужных вычислений, особенно при работе с бесконечными структурами данных или при выполнении дорогостоящих операций.
  • Преобразование (thunk) — особая техника, используемая для реализации ленивой оценки. По сути, это функция без аргументов, которая заключает в себе вычисления или выражения. Вычисления откладываются до явного вызова thunk.

15. Каноникализация (canonicalization): нормализация данных

Каноникализация — процесс преобразования данных в стандартную или нормализованную форму. Он часто используется в базах данных и URL для обеспечения согласованности.

http://example.com
https://example.com
http://www.example.com

Каноникализация гарантирует, что все они будут указывать на одну предпочтительную версию.

16. Побочный эффект (side effect): больше, чем вы ожидали

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

def add_to_list(value, lst=[]):
lst.append(value)
return lst

print(add_to_list(1)) # [1]
print(add_to_list(2)) # [1, 2] (неожиданно!)

В этом примере функция модифицирует аргумент по умолчанию, вызывая непредвиденные побочные эффекты.

17. Поднятие (hoisting): перемещение объявлений вверх

В JavaScript на этапе компиляции поднятие перемещает объявление переменных и функций в верхнюю часть содержащей их области видимости.

console.log(x);  // undefined
var x = 5;

Несмотря на то, что x объявляется после console.log, JavaScript поднимает объявление вверх, делая x доступным еще до его определения.

18. Моноид (monoid): последовательное объединение данных

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

result = ''.join(['a', 'b', 'c'])
print(result)  # 'abc'

Здесь конкатенация строк образует моноид с '' в качестве нейтрального элемента и + в качестве бинарной операции.

Заключение

Эти продвинутые термины программирования поначалу могут показаться слишком сложными, но их понимание значительно улучшит ваш подход к написанию кода. Будь то оптимизация производительности, безопасности или читабельности, эти понятия дадут вам мощные инструменты для решения сложных проблем изящными способами.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Rahul Beniwal: 18 Programming Concepts You’ve Never Heard of (But Should!)

Предыдущая статьяМикрофронтенды: 9 шаблонов для каждого разработчика
Следующая статьяПишем балансировщик нагрузки на Golang