Перейти к содержимому

Что такое предикат в программировании

  • автор:

Что такое предикат в программировании

Объекты-функции – это объекты, у которых перегружен оператор вызова функций operator(). В библиотеке STL уже определено несколько полезных арифметических и других объектов-функций (описание в файле ):

Рассмотрим пример с отрицанием всех элементов вектора. Можно выполнить этот пример с помощью цикла, а можно сделать намного проще с использованием алгоритма transform и стандартной функции negate.

vectorint> v; vectorint>::iterator it=v.begin(); while(it != v.end()) < *it = -(*it); it++; >// то же самое с использованием объекта-функции negate transform(v.begin(),v.end(), v.begin(), negateint>()); // вывод контейнера на экран copy(v.begin(), v.end(), ostream_iteratorint>(cout, " "));

Алгоритмы (описание в файле < algorithm>) позволяют выполнять некоторые типовые действия надо контейнерами с использованием объектов-функций стандартной библиотеки или своих объектов-функций. Подробно алгоритмы, функции, и другие возможности библиотеки STL приводятся в Приложении 5.

Стандартные алгоритмы можно использовать и для ввода и вывод контейнера на экран. При чтении ввод происходит до ввода первого не числового символа.

Программисты могут определить свои объекты-функции, которые могут быть наследниками от стандартных. В объектах-функциях обязательно должен быть перегружен оператор вызова функции (), в конструкторе могут задаваться необходимые параметры, или он может быть пустым (см.пример 6.5).

Функции могут быть двух типов:

  • Унарная функция – это функция, в которой участвует один операнд (например, x=-y — унарный минуc)
  • Бинарная функция – это функция, в которой участвуют два операнда (например, x=y+z — сложение, умножение, и т.д.)

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

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

Функция binder2nd – преобразует бинарную функцию в унарную, и принимает второй аргумент как параметр бинарной функции (описание в файле )

// умножение каждого элемента на 2 transform (v.begin(), v.end(), v.begin(), bind2nd(multipliesint>(), 2));
Пример 6.5. Использование объектов-функций
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 6.5. Использование объектов-функций // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include using namespace std; ///////////////////////////////////////////////////////////////////////////// // создание объекта-функции для заполнения случайными числами // функция Rand - шаблон, наследник от стандартной функции unary_function // параметры шаблона: PAR - тип данных, void - возвращаемое значение оператора () template class PAR> class Rand : public unary_functionvoid> < // диапазон случайных чисел PAR m_min, m_max; public: // конструктор, в котором задается диапазон случайных чисел Rand(PAR min, PAR max) : m_min(min), m_max(max) < >// перегруженный оператор вызова функции, в котором число value заполняется случайным числом void operator() (PAR& value) < value=(PAR)(rand()*(m_max-m_min))/RAND_MAX+m_min; >>; ///////////////////////////////////////////////////////////////////////////// // тестирование объектов-функций STL - negate, ввод-вывод void main() < vectorint> v; // чтение контейнера из потока ввода (до первого не числового символа) copy(istream_iteratorint>(cin), istream_iteratorint>(), back_inserter(v)); // вывод контейнера на экран copy(v.begin(), v.end(), ostream_iteratorint>(cout, " ")); cout// использование объекта-функции negate transform(v.begin(),v.end(), v.begin(), negateint>()); copy(v.begin(), v.end(), ostream_iteratorint>(cout, " ")); cout// заполнение контейнера случайными числами при помощи объекта-функции Rand for_each(v.begin(), v.end(), Randint>(-10, 10)); copy(v.begin(), v.end(), ostream_iteratorint>(cout, " ")); cout// умножение каждого элемента на 2, // использование стандартной бинарной функции multiplies, // преобразованной в унарную при помощи функции bind2nd transform (v.begin(), v.end(), v.begin(), bind2nd(multipliesint>(), 2)); copy (v.begin(), v.end(),ostream_iteratorint>(cout, " ")); cout /////////////////////////////////////////////////////////////////////////////

6.4.2. Предикаты. Пример 6.6 (использование предикатов)

Предикаты позволяют без изменения шаблона изменять критерии сравнения элементов контейнера и другие подобные действия. У предикатов объект-функция возвращает значение bool.

В файле уже определено несколько полезных предикатов:

  • equal_to бинарный предикат равенства
  • not_equal_to бинарный предикат неравенства
  • greater бинарный предикат >
  • less бинарный предикат < (используется по умолчанию)
  • greater_equal бинарный предикат >=
  • less_equal бинарный предикат
  • logical_and бинарный предикат И
  • logical_or бинарный предикат ИЛИ
  • logical_not унарный предикат НЕ

Например, стандартный алгоритм сортировки сортирует по возрастанию, если мы хотим сделать сортировку по убыванию – можно использовать предикат greater:

// сортировка в порядке убывания sort(v.begin(), v.end(), greaterint>());

Можно определять свои предикаты, как наследники от стандартных объектов-функций.

Пример 6.6. Использование предикатов
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 6.6. Использование предикатов // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include using namespace std; ///////////////////////////////////////////////////////////////////////////// // создание объекта-функции для заполнения случайными числами // функция Rand - шаблон, наследник от стандартной функции unary_function // параметры шаблона: PAR - тип данных, void - возвращаемое значение оператора () template class PAR> class Rand : public unary_functionvoid> < // диапазон случайных чисел PAR m_min, m_max; public: // конструктор, в котором задается диапазон случайных чисел Rand(PAR min, PAR max) : m_min(min), m_max(max) < >// перегруженный оператор вызова функции, в котором число value заполняется случайным числом void operator() (PAR& value) < value=(PAR)(rand()*(m_max-m_min))/RAND_MAX+m_min; >>; ///////////////////////////////////////////////////////////////////////////// // создание объекта-функции определения попадания числа в диапазон // функция InRange - шаблон, наследник от стандартной функции unary_function // параметры шаблона: int - тип данных, bool - возвращаемое значение оператора () class InRange : public unary_functionint, bool> < // диапазон чисел int m_left, m_right; public: // конструктор, в котором задается диапазон InRange(int left, int right) : m_left(left), m_right(right) <> // перегруженный оператор вызова функции, в котором определяется // попадание числа value в диапазон от m_left до m_right bool operator() (const int& value) < return (value>m_left && value >; ///////////////////////////////////////////////////////////////////////////// // тестирование предикатов void main() < vectorint> v(10); // заполнение контейнера случайными числами при помощи объекта-функции Rand for_each(v.begin(), v.end(), Randint>(-10, 10)); copy(v.begin(), v.end(), ostream_iteratorint>(cout, " ")); cout// сортировка с использованием предиката greater sort(v.begin(), v.end(), greaterint>()); copy(v.begin(), v.end(), ostream_iteratorint>(cout, " ")); cout// использование InRange для подсчета количества элементов в диапазоне от 0 до 10 cout /////////////////////////////////////////////////////////////////////////////

Предикаты

Некоторые фразы не могут быть ни правдивыми, ни лживыми: «Реши задачу», «Это утверждение ложно», «Почему небо голубое?». А предикат — это такая фраза, которая может оказаться правдой или неправдой: «этот счёт оплачен», «этот пользователь решил задачу».

В Python предикаты нужны для конструкции if. Например, вот так можно проверить, оплачен ли счёт:

if invoice_is_paid: # выдаём доступ к курсу 

Во время исполнения кода правдивые предикаты превращаются в True , а лживые — в False :

print(1 + 1 == 2) # True print(1 + 1 == 3) # False 

Над предикатами можно совершать логические операции: отрицание ( not ), конъюнкцию ( and ) и дизъюнкцию ( or ):

print(not True) # False print(1 + 1 == 2 or 1 + 1 == 3) # True 

Любое выражение в Python можно сделать предикатом, если использовать функцию bool :

print(bool(1)) # True print(bool('Привет')) # True print(bool(-1)) # True print(bool('')) # False print(bool(0)) # False print(bool(None)) # False 

Из примера выше видно, что нулевые значения, вроде » , 0 и None равны False , а любые ненулевые (и даже отрицательные) — True .

Выражения становятся предикатами сами по себе, без вызова bool , если в них есть знаки сравнения ( == , ) или отрицание not :

print(not 'Привет') # False print(0  1) # True 

О том, как используются предикаты на практике, мы рассказываем в статье «Превращение типов в bool».

Альтернативные источники

Если мы не смогли объяснить материал, напишите нам или попробуйте альтернативные источники по теме:

  • Питонтьютор. Условия
  • Официальный туториал

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Что такое предикат в программировании

На этом шаге мы рассмотрим использование унарных предикатов .

Особую разновидность вспомогательных функций, используемых алгоритмами, составляют предикаты. Предикатом называется функция, которая возвращает логическое значение. С помощью предикатов часто определяют критерии сортировки или поиска. В зависимости от способа применения предикаты делятся на унарные и бинарные . Учтите, что не любая унарная или бинарная функция, возвращающая логическую величину, является действительным предикатом. STL требует, чтобы при неизменности входных данных предикат всегда давал постоянный результат. Тем самым из категории предикатов исключаются функции, внутреннее состояние которых изменяется в процессе вызова.

Унарные предикаты

Унарный предикат проверяет некоторое свойство одного аргумента. Типичный пример — функция, используемая в качестве критерия поиска первого простого числа:

//--------------------------------------------------------------------------- #include #include #include #include #include //необходимо для getch() #include //для abs <)#pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) < char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; > // Предикат проверяет, является ли целое число простым bool isPrime (int number) < // Знак числа игнорируется number = abs(number); // 0 и 1 не являются простыми числами if (number == 0 || number == 1) < return false; > // Поиск множителя, на который число делятся без остатка int divisor; for (divisor = number/2; number%divisor != 0; --divisor) < ; >// Если не найдено ни одного множителя, большего 1. // проверяемое число является простым. return divisor == 1; > int main(int argc, char* argv[]) < listcoll; // Вставка элементов со значениями от 24 до 30 for (int i=24; i // Поиск простого числа list::iterator pos; pos = find_if (coll.begin(), coll.end(), // Интервал isPrime); // Предикат if (pos != coll.end()) < // Найдено простое число cout else < // Простые числа не найдены cout cout return 0; > //--------------------------------------------------------------------------- 

Текст этого примера можно взять здесь.

В приведенном примере алгоритм find_if() ищет в заданном интервале первый элемент, для которого передаваемый унарный предикат возвращает true . В качестве предиката передается функция isPrime() , которая проверяет, является ли число простым. В итоге алгоритм возвращает первое простое число в заданном интервале. Если алгоритм не находит ни одного элемента, удовлетворяющего предикату, возвращается конец интервала (второй аргумент). Это условие проверяется после вызова. Коллекция из нашего примера содержит простое число между 24 и 30, поэтому результат выполнения программы выглядит так:

Рис.1. Результат работы приложения

На следующем шаге мы рассмотрим бинарные предикаты .

Жаргон функционального программирования

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

В примерах используется JavaScript ES2015). (Почему JavaScript?)

Работа над материалом продолжается; присылайте свои пулл-реквесты в оригинальный репозиторий на английском языке.

В документе используются термины из спецификации Fantasy Land spec по мере необходимости.

Arity (арность)

Количество аргументов функции. От слов унарный, бинарный, тернарный (unary, binary, ternary) и так далее. Это необычное слово, потому что состоит из двух суффиксов: «-ary» и «-ity.». Сложение, к примеру, принимает два аргумента, поэтому это бинарная функция, или функция, у которой арность равна двум. Иногда используют термин «диадный» (dyadic), если предпочитают греческие корни вместо латинских. Функция, которая принимает произвольное количество аргументов называется, соответственно, вариативной (variadic). Но бинарная функция может принимать два и только два аргумента, без учета каррирования или частичного применения.

const sum = (a, b) => a + b const arity = sum.length console.log(arity) // 2 // The arity of sum is 2

Higher-Order Functions (функции высокого порядка)

Функция, которая принимает функцию в качестве аргумента и/или возвращает функцию.

const filter = (predicate, xs) => < const result = [] for (let idx = 0; idx < xs.length; idx++) < if (predicate(xs[idx])) < result.push(xs[idx]) >> return result >
const is = (type) => (x) => Object(x) instanceof type 
filter(is(Number), [0, '1', 2, null]) // [0, 2]

Partial Application (частичное применение)

Частичное применение функции означает создание новой функции с пред-заполнением некоторых аргументов оригинальной функции.

// Helper to create partially applied functions // Takes a function and some arguments const partial = (f, . args) => // returns a function that takes the rest of the arguments (. moreArgs) => // and calls the original function with all of them f(. args, . moreArgs) // Something to apply const add3 = (a, b, c) => a + b + c // Partially applying `2` and `3` to `add3` gives you a one-argument function const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c fivePlus(4) // 9

Также в JS можно использовать Function.prototype.bind для частичного применения функции:

const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c

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

Currying (каррирование)

Процесс конвертации функции, которая принимает несколько аргументов, в функцию, которая принимает один аргумент за раз.

При каждом вызове функции она принимает один аргумент и возвращает функцию, которая принимает один аргумент до тех пор, пока все аргументы не будут обработаны.

const sum = (a, b) => a + b const curriedSum = (a) => (b) => a + b curriedSum(40)(2) // 42. const add2 = curriedSum(2) // (b) => 2 + b add2(10) // 12 

Auto Currying (автоматическое каррирование)

Трансформация функции, которая принимает несколько аргументов, в новую функцию. Если в новую функцию передать меньшее чем предусмотрено количество аргументов, то она вернет функцию, которая принимает оставшиеся аргументы. Когда функция получает правильное количество аргументов, то она исполняется.

В Underscore, lodash и ramda есть функция curry .

const add = (x, y) => x + y const curriedAdd = _.curry(add) curriedAdd(1, 2) // 3 curriedAdd(1) // (y) => 1 + y curriedAdd(1)(2) // 3

Дополнительные материалы

  • Favoring Curry
  • Hey Underscore, You’re Doing It Wrong!

Function Composition (композиция функций)

Соединение двух функций для формирования новой функции, в которой вывод первой функции является вводом второй.

const compose = (f, g) => (a) => f(g(a)) // Definition const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage floorAndToString(121.212121) // '121'

Purity (чистота)

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

const greet = (name) => 'Hi, ' + name greet('Brianne') // 'Hi, Brianne' 
 let greeting const greet = () => < greeting = 'Hi, ' + window.name >greet() // "Hi, Brianne" 

Side effects (побочные эффекты)

У функции есть побочные эффекты если кроме возврата значения она взаимодействует (читает или пишет) с внешним изменяемым состоянием.

const differentEveryTime = new Date()
console.log('IO is a side effect!')

Idempotent (идемпотентность)

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

f(f(x)) ≍ f(x)
Math.abs(Math.abs(10))
sort(sort(sort([2, 1])))

Point-Free Style (бесточечная нотация)

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

// Given const map = (fn) => (list) => list.map(fn) const add = (a) => (b) => a + b // Then // Not points-free - `numbers` is an explicit argument const incrementAll = (numbers) => map(add(1))(numbers) // Points-free - The list is an implicit argument const incrementAll2 = map(add(1))

Функция incrementAll определяет и использует параметр numbers , так что она не использует бесточечную нотацию. incrementAll2 просто комбинирует функции и значения, не упоминая аргументов. Она использует бесточечную нотацию.

Определения с бесточечной нотацией выглядят как обычные присваивания без function или => .

Predicate (предикат)

Предикат — это функция, которая возвращает true или false в зависимости от переданного значения. Распространенный случай использования предиката — функция обратного вызова (callback) для фильтра массива.

const predicate = (a) => a > 2 ;[1, 2, 3, 4].filter(predicate) // [3, 4]

Categories (категории)

Объекты с функциями, которые подчиняются определенным правилам. Например, моноиды.

Value (значение)

Все, что может быть присвоено переменной.

5 Object.freeze() // The `freeze` function enforces immutability. ;(a) => a ;[1] undefined

Constant (константа)

Переменная, которую нельзя переназначить после определения.

const five = 5 const john =

Константы обладают референциальной прозрачностью или прозрачностью ссылок (referential transparency). То есть, их можно заменить значениями, которые они представляют, и это не повлияет на результат.

С константами из предыдущего листинга следующее выражение выше всегда будет возвращать true .

john.age + five === ().age + (5)

Functor (функтор)

Объект, который реализует функцию map , которая при проходе по всем значениям в объекте создает новый объект, и подчиняется двум правилам:

// сохраняет нейтральный элемент (identity) object.map(x => x) === object
// поддерживает композицию object.map(x => f(g(x))) === object.map(g).map(f)

( f , g — произвольные функции)

В JavaScript есть функтор Array , потому что он подчиняется эти правилам:

[1, 2, 3].map(x => x) // = [1, 2, 3]
const f = x => x + 1 const g = x => x * 2 ;[1, 2, 3].map(x => f(g(x))) // = [3, 5, 7] ;[1, 2, 3].map(g).map(f) // = [3, 5, 7]

Pointed Functor (указывающий функтор)

Объект с функцией of с любым значением. В ES2015 есть Array.of , что делает массивы указывающим функтором.

Array.of(1) // [1]

Lift

Lifting — это когда значение помещается в объект вроде функтора. Если «поднять» (lift) функцию в аппликативный функтор, то можно заставить ее работать со значениями, которые также присутствуют в функторе.

В некоторых реализациях есть функция lift или liftA2 , которые используются для упрощения запуска функций на функторах.

const liftA2 = (f) => (a, b) => a.map(f).ap(b) const mult = a => b => a * b const liftedMult = liftA2(mult) // this function now works on functors like array liftedMult([1, 2], [3]) // [3, 6] liftA2((a, b) => a + b)([1, 2], [3, 4]) // [4, 5, 5, 6]

Подъем функции с одним аргументом и её применение выполняет то же самое, что и map .

const increment = (x) => x + 1 lift(increment)([2]) // [3] ;[2].map(increment) // [3]

Referential Transparency (прозрачность ссылок)

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

Например, есть функция greet :

const greet = () => 'Hello World!'

Любой вызов greet() можно заменить на Hello World! , так что эта функция является прозрачной (referentially transparent).

Lambda (лямбда)

Анонимная функция, которую можно использовать как значение.

;(function (a) < return a + 1 >) ;(a) => a + 1

Лямбды часто передают в качестве аргументов в функции высокого порядка.

[1, 2].map((a) => a + 1) // [2, 3]

Лямбду можно присвоить переменной.

const add1 = (a) => a + 1

Lambda Calculus (лямбда-исчисление)

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

Lazy evaluation (ленивые вычисления)

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

const rand = function*() < while (1 < 2) < yield Math.random() >>
const randIter = rand() randIter.next() // Каждый вызов дает случайное значение, выражение исполняется при необходимости.

Monoid (моноид)

Объект с функцией, которая «комбинирует» объект с другим объектом того же типа. Простой пример моноида это сложение чисел:

1 + 1 // 2

В этом случае число — это объект, а + это функция.

Должен существовать нейтральный элемент (identity), так, чтобы комбинирование значения с ним не изменяло значение. В случае сложения таким элементом является 0 .

1 + 0 // 1

Также необходимо, чтобы группировка операций не влияла на результат (ассоциативность):

1 + (2 + 3) === (1 + 2) + 3 // true

Конкатенация массивов — это тоже моноид:

;[1, 2].concat([3, 4]) // [1, 2, 3, 4]

Нейтральный элемент — это пустой массив []

;[1, 2].concat([]) // [1, 2]

Если существуют функции нейтрального элемента и композиции, то функции в целом формируют моноид:

const identity = (a) => a const compose = (f, g) => (x) => f(g(x)) 

foo — это любая функция с одним аргументом.

compose(foo, identity) ≍ compose(identity, foo) ≍ foo

Monad (монада)

Монада — это объект с функциями of и chain . chain похож на map , но он производит разложение вложенных объектов в результате.

// Implementation Array.prototype.chain = function (f) < return this.reduce((acc, it) =>acc.concat(f(it)), []) > // Usage ;Array.of('cat,dog', 'fish,bird').chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird'] // Contrast to map ;Array.of('cat,dog', 'fish,bird').map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']]

of также известен как return в других функциональных языках.
chain также известен как flatmap и bind в других языках.

Comonad (комонада)

Объект с функциями extract и extend .

const CoIdentity = (v) => (< val: v, extract () < return this.val >, extend (f) < return CoIdentity(f(this)) >>)

Extract берет значение из функтора.

CoIdentity(1).extract() // 1

Extend выполняет функцию на комонаде. Функция должна вернуть тот же тип, что комонада.

CoIdentity(1).extend((co) => co.extract() + 1) // CoIdentity(2)

Applicative Functor (аппликативный функтор)

Объект с функцией ap . ap применяет функцию в объекте к значению в другом объекте того же типа.

// Implementation Array.prototype.ap = function (xs) < return this.reduce((acc, f) =>acc.concat(xs.map(f)), []) > // Example usage ;[(a) => a + 1].ap([1]) // [2]

Это полезно, когда есть два объекта, и нужно применить бинарную операцию на их содержимом.

// Arrays that you want to combine const arg1 = [1, 3] const arg2 = [4, 5] // combining function - must be curried for this to work const add = (x) => (y) => x + y const partiallyAppliedAdds = [add].ap(arg1) // [(y) => 1 + y, (y) => 3 + y]

В итоге получим массив функций, которые можно вызвать с ap чтобы получить результат:

partiallyAppliedAdds.ap(arg2) // [5, 6, 7, 8]

Morphism (морфизм)

Endomorphism (эндоморфизм)

Функция, у которой ввод и вывод — одного типа.

// uppercase :: String -> String const uppercase = (str) => str.toUpperCase() // decrement :: Number -> Number const decrement = (x) => x - 1

Isomorphism (изоморфизм)

Пара структурных трансформаций между двумя типами объектов без потери данных.

Например, двумерные координаты можно хранить в массиве [2,3] или объекте .

// Providing functions to convert in both directions makes them isomorphic. const pairToCoords = (pair) => () const coordsToPair = (coords) => [coords.x, coords.y] coordsToPair(pairToCoords([1, 2])) // [1, 2] pairToCoords(coordsToPair()) //

Setoid

Объект, у которого есть функция equals , которую можно использовать для сравнения объектов одного типа.

Сделать массив сетоидом:

Array.prototype.equals = (arr) => < const len = this.length if (len !== arr.length) < return false >for (let i = 0; i < len; i++) < if (this[i] !== arr[i]) < return false >> return true > ;[1, 2].equals([1, 2]) // true ;[1, 2].equals([0]) // false

Semigroup (полугруппа)

Объект с функцией concat , которая комбинирует его с другим объектом того же типа.

;[1].concat([2]) // [1, 2]

Foldable

Объект, в котором есть функция reduce , которая трансформирует объект в другой тип.

const sum = (list) => list.reduce((acc, val) => acc + val, 0) sum([1, 2, 3]) // 6

Type Signatures (сигнатуры типа)

Часто функции в JavaScript содержат комментарии с указанием типов их аргументов и возвращаемых значений. В сообществе существуют разные подходы, но они все схожи:

// functionName :: firstArgType -> secondArgType -> returnType // add :: Number -> Number -> Number const add = (x) => (y) => x + y // increment :: Number -> Number const increment = (x) => x + 1

Если функция принимает другую функцию в качестве аргумента, то ее помещают в скобки.

// call :: (a -> b) -> a -> b const call = (f) => (x) => f(x) 

Символы a , b , c , d показывают, что аргументы могут быть любого типа. Следующая версия функции map принимает:

  1. функцию, которая трансформирует значение типа a в другой тип b
  2. массив значений типа a ,

и возвращает массив значений типа b .

// map :: (a -> b) -> [a] -> [b] const map = (f) => (list) => list.map(f) 

Дополнительные материалы

  • Ramda’s type signatures
  • Mostly Adequate Guide
  • What is Hindley-Milner? на Stack Overflow

Union type (тип-объединение)

Комбинация двух типов в один, новый тип.

В JavaScript нет статических типов, но давайте представим, что мы изобрели тип NumOrString , который является сложением String и Number .

Операция + в JavaScript работает со строками и числами, так что можно использовать наш новый тип для описания его ввода и вывода:

// add :: (NumOrString, NumOrString) -> NumOrString const add = (a, b) => a + b add(1, 2) // Возвращает число 3 add('Foo', 2) // Возвращает строку "Foo2" add('Foo', 'Bar') // Возвращает строку "FooBar"

Тип-объединение также известно как алгебраический тип, размеченное объединение и тип-сумма.

Существует пара библиотек в JavaScript для определения и использования таких типов.

Product type (тип-произведение)

Тип-произведение комбинирует типы таким способом, который вам скорее всего знаком:

// point :: (Number, Number) -> const point = (x, y) => ()

Его называют произведением, потому что возможное значение структуры данных это произведение (product) разных значений.

Option (опцион)

Тип-объединение с двумя случаями: Some и None . Полезно для композиции функций, которые могут не возвращать значения.

// Naive definition const Some = (v) => (< val: v, map (f) < return Some(f(this.val)) >, chain (f) < return f(this.val) >>) const None = () => (< map (f) < return this >, chain (f) < return this >>) // maybeProp :: (String, ) -> Option a const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key]) 

Используйте chain для построения последовательности функций, которые возвращают Option .

 // getItem :: Cart -> Option CartItem const getItem = (cart) => maybeProp('item', cart) // getPrice :: Item -> Option Number const getPrice = (item) => maybeProp('price', item) // getNestedPrice :: cart -> Option a const getNestedPrice = (cart) => getItem(obj).chain(getPrice) getNestedPrice(<>) // None() getNestedPrice(>) // None() getNestedPrice(>) // Some(9.99)

Option также известен как Maybe . Some иногда называют Just . None иногда называют Nothing .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *