Показать сообщение отдельно
  #15 (permalink)  
Старый 06.02.2020, 13:43
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Функция betterRecursive, о которой упоминалось выше, написана на JavaScript, где функции могут принимать более одного параметра. Формально комбинатор принимает только один параметр и работает только с функциями, которые принимают только один параметр.

Мы можем перевести нашу функцию betterRecursive в формальный комбинатор, Y-комбинатор. Чтобы было понятней, давайте сначала представим рекурсивную функцию:

const isEven =
  n =>
    (n === 0) || !isEven(n - 1);


Функция, которая принимается функцией betterRecursive

const _isEven =
  (myself, n) =>
    (n === 0) || !myself(n - 1);


Увы, сейчас она принимает два параметра. Можно исправить, применив каррирование...

const __isEven =
  myself =>
    n =>
      (n === 0) || !myself(n - 1);

Вместо того, чтобы принимать два параметра (myself и n), теперь эта функция, которая принимает один параметр myself и возвращает функцию, которая принимает другой параметр n.

Чтобы приспособить функцию betterRecursive к этой форме функции, нужно взять betterRecursive и выполнить аналогичные модификации. Начнем, как указано выше, переименовав его:

const Y =
  fn =>
    (
      maker =>
        (...args) =>
          fn(maker(maker), ...args)
    )(
      maker =>
        (...args) =>
          fn(maker(maker), ...args)
    );

Далее мы видим, что (...args) => fn(maker(maker), ...args) недопустимо, мы не используем деструктуризацию аргументов и применение полученного путём деструкткризации массива параметров к другой функции. Во-первых, мы изменяем ...args на arg, поскольку разрешен только один параметр:

const Y =
  fn =>
    (
      maker =>
        arg => fn(maker(maker), arg)
    )(
      maker =>
        arg => fn(maker(maker), arg)
    );


fn(maker(maker), arg) также не допускается, мы не передаем два параметра любой функции. Вместо этого мы передаем один параметр, получаем функцию обратно и передаем второй параметр этой функции....

const Y =
  fn =>
    (
      maker =>
        arg => fn(maker(maker))(arg)
    )(
      maker =>
        arg => fn(maker(maker))(arg)
    );


Или с более краткими именами, вариант, который упоминался ранее...
const Y =
	f => (
		g =>
			x => f(g(g))(x)
	)(
		g =>
			x => f(g(g))(x)
	);

Давай попробуем:

const Y =
	f => (
		g =>
			x => f(g(g))(x)
	)(
		g =>
			x => f(g(g))(x)
	);
	
alert(Y(
	myself =>
		n =>
			(n === 0) || !myself(n - 1)
)(1962));


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

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

Также вы можете использовать https://www.npmjs.com/package/y-combinator
Ответить с цитированием