описание проблемы
В промежуточной викторине CS1101S 2015 года задается вопрос, исчерпывают ли следующие функции время и/или ресурсы пространства.
const C = (x => x(x))(x => x(x)); const D = (x => (x(x))(x))(x => x(x)); const E = (x => x(x(x)))(x => x(x)); const F = (x => x(x))(x => x(x(x)));
Задний план
Я понятия не имел, как это работает, пока не прочитал кое-что о *лямбда-исчислении*, поэтому я кратко расскажу, как это работает:
Немного базового лямбда-исчисления
Рассмотрим одно из простейших лямбда-выражений: `λx. х+1`. Это означает, что в функции черного ящика она получает один параметр с именем `x` и выводит результат `x+1`. Знак `λ` вместе с `.` после имени переменной указывает на объявление лямбда-функции.
Итак, как мы передаем параметры лямбда-функции? Мы пишем его (или их) прямо рядом с лямбда-функцией (, что делает важным добавление круглых скобок в начале и конце лямбда-функции, потому что в противном случае вы не сможете определить, является ли это параметром или частью лямбда-функции. функция). Например, `(λx. x+1) 1` выведет `2`, потому что лямбда-функция заменит все `x` после `λx.` и оценит выражение.
Подключиться к Источнику
Теперь вы обнаружите, что `x =› x + 1` является исходным выражением эквивалентности для лямбда-функции `(λx. x+1)`. Кроме того, `(x, y) =› x + y` эквивалентно `(λx.λy. x+y)`; `a =› b =› a + b` эквивалентно `(λa. (λb. a + b))`. Обратите внимание, что `a` в `(λb. a + b)` — это то, что мы называем нерелевантной переменной, потому что она не объявлена в начале этой функции.
Мой опыт
Итак, почему я запутался с проблемой? Я слишком много думал о такой функции! Поскольку это функция черного ящика, она просто выполняет подстановку, то есть заменяет все `x` справа от `=›` значением, полученным из параметра. А параметр всегда находится рядом с функцией. Что еще более важно, компилятор читает слева направо. Когда он заканчивает чтение функции, он немедленно ищет ее параметры. По этой причине в следующей задаче D первая часть (`((x =› x(x))(x =› x(x)))`) никогда не удлиняется. Так как `(x =› x(x))` — это первая функция, которую компилятор читает, то вторая `(x =› x(x))` естественно считается ее параметром и подстановка происходит сразу.
Решение
// — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — const C = (x => x(x))(x => x(x)); //1. (x => x(x))(x => x(x)) //It is exactly the same as the original one //Yes and No // — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — const D = (x => (x(x))(x))(x => x(x)); //1. ((x => x(x))(x => x(x)))(x => x(x)) //2. ((x => x(x))(x => x(x)))(x => x(x)) //Then the function never gets longer //Yes and No // — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — const E = (x => x(x(x)))(x => x(x)); //1. (x => x(x))((x => x(x))(x => x(x))) //2. ((x => x(x))(x => x(x)))((x => x(x))(x => x(x))) //3. ((x => x(x))(x => x(x)))((x => x(x))(x => x(x))) //Then the function never gets longer //Yes and No // — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — const F = (x => x(x))(x => x(x(x))); //1. (x => x(x(x)))(x => x(x(x))) //2. (x => x(x(x)))((x => x(x(x)))(x => x(x(x)))) //Notice the first part of the function remains the same //This ensures that the tail of the function gets to increase //infinitely, unlike D and E where the function //expansion is limited to a fixed-length expansion //of the first part of the function //Yes and Yes // — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Дополнительные примечания
В задачах D и E есть функция, которую я назвал расширением фиксированной длины. В лямбда-выражении это можно записать как `(λx. x x)(λx. x x)`. (Это немного отличается от `(x =› x(x))(x =› x(x))` из-за круглых скобок, но вы также можете записать его как `((x) =› (x)(x)) ((x) =› (x)(x)))`, чтобы имитировать формат лямбда-выражения, верно?) Это бесконечный цикл, который ничего не делает, и он является строительным блоком для Y-комбинатора `λf. ((λx. f(xx))(λx. f(xx)))` и вы можете выполнить простую, но утомительную работу по подстановке, чтобы увидеть, что Y-combinatior приводит к `f(f(f (ф(…))))`. (Вам нужно только оценить часть `(λx. f(x x))(λx. f(x x))`, потому что мы еще не предоставили значение параметру `f`)
Еще кое-что
В предыдущем сообщении блога мы писали:
((function(f){return f(f);})( (function(make_factorial){ return function(n){ return (n===0) ? 1 : n * make_factorial(make_factorial)(n-1); }; })))(5);
После упрощения это выглядит как `(x =› x(x))(f =› (x =› x * f(f)(x-1)))`.
Вспомните Y-combinatior, он должен выглядеть так
— -
Примечание: вместо `x =› f(x(x))` мы пишем `x =› y =› f(x(x))(y))`, потому что мы хотим, чтобы * *используйте** функцию! `x` сам по себе является функцией, мы не пишем так, возвращаемая функция `Y(f)` никогда не ожидает число…
function Y(f){ return (x => y => f(x(x))(y)) (x => y => f(x(x))(y)); }
что сильно отличается от приведенного выше. В чем ключ? Позвольте мне сделать краткую разбивку:
`(x =› y =› f(x(x))(y))(x =› y =› f(x(x))(y))` в основном применяется `(x =› y =› f( x(x))(y))` в себя. Если мы заменим его более коротким именем `p`, тогда `(x =› y =› f(x(x))(y))(x =› y =› f(x(x))(y)) ` эквивалентно `p(p)`. Тогда все это можно рассматривать как результат `(g => g(g))(p)`. Кроме того, `f =› (p)(p)` можно рассматривать как
`(g =› g(g))(f =› p)`.
Примечание: я переместил `f =›` снаружи внутрь, потому что переменная `f` используется только в `p`. Это изменение не влияет на общность нашего решения.
Далее мы смотрим на `(x =› y =› f(x(x))(y))`. Обратите внимание, что «y» — это число, которое мы получаем, поэтому мы определим наш «базовый случай» и «индуктивный шаг» по отношению к «y».
Базовый случай: `f(x(x))(0) = 1`, что означает, что при `y=0` должно возвращаться `1`
Индуктивный шаг: `f(x(x))(y) = y * f(x(x))(y-1)`, что означает, что в противном случае он должен возвращать `y * f(y -1)`
Что такое "ф"? Нам все равно! В любом случае это черный ящик. Мы предполагаем, что у нас уже есть такая функция `f` в качестве нашего параметра (принятие желаемого за действительное). Затем мы можем начать определять, как должно выглядеть `(x =› y =› f(x(x))(y))`:
function x(f){ return y => y === 0 ? 1 : y * f(y-1); }